Compare commits
No commits in common. "main" and "registro-horas" have entirely different histories.
main
...
registro-h
Binary file not shown.
60
package.json
60
package.json
@ -4,7 +4,7 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@f0rce/ace-widget": "1.0.2",
|
||||
"@polymer/polymer": "3.5.1",
|
||||
"@polymer/polymer": "3.5.2",
|
||||
"@vaadin-component-factory/vcf-pdf-viewer": "2.0.1",
|
||||
"@vaadin/bundles": "24.5.1",
|
||||
"@vaadin/common-frontend": "0.0.19",
|
||||
@ -19,29 +19,30 @@
|
||||
"@vaadin/vaadin-usage-statistics": "2.1.3",
|
||||
"construct-style-sheets-polyfill": "3.1.0",
|
||||
"date-fns": "2.29.3",
|
||||
"lit": "3.1.4",
|
||||
"lit": "3.2.1",
|
||||
"print-js": "1.6.0",
|
||||
"proj4": "2.12.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-router-dom": "6.23.1"
|
||||
"react-router-dom": "6.26.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-react": "7.24.7",
|
||||
"@rollup/plugin-replace": "5.0.7",
|
||||
"@rollup/pluginutils": "5.1.0",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@vitejs/plugin-react": "4.3.1",
|
||||
"async": "3.2.5",
|
||||
"glob": "10.4.1",
|
||||
"@babel/preset-react": "7.25.7",
|
||||
"@preact/signals-react-transform": "0.4.0",
|
||||
"@rollup/plugin-replace": "6.0.1",
|
||||
"@rollup/pluginutils": "5.1.2",
|
||||
"@types/react": "18.3.11",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"@vitejs/plugin-react": "4.3.3",
|
||||
"async": "3.2.6",
|
||||
"glob": "10.4.5",
|
||||
"rollup-plugin-brotli": "3.1.0",
|
||||
"rollup-plugin-visualizer": "5.12.0",
|
||||
"strip-css-comments": "5.0.0",
|
||||
"transform-ast": "2.4.4",
|
||||
"typescript": "5.4.5",
|
||||
"vite": "5.3.3",
|
||||
"vite-plugin-checker": "0.6.4",
|
||||
"typescript": "5.6.3",
|
||||
"vite": "5.4.9",
|
||||
"vite-plugin-checker": "0.8.0",
|
||||
"workbox-build": "7.1.1",
|
||||
"workbox-core": "7.1.0",
|
||||
"workbox-precaching": "7.1.0"
|
||||
@ -49,7 +50,7 @@
|
||||
"vaadin": {
|
||||
"dependencies": {
|
||||
"@f0rce/ace-widget": "1.0.2",
|
||||
"@polymer/polymer": "3.5.1",
|
||||
"@polymer/polymer": "3.5.2",
|
||||
"@vaadin-component-factory/vcf-pdf-viewer": "2.0.1",
|
||||
"@vaadin/bundles": "24.5.1",
|
||||
"@vaadin/common-frontend": "0.0.19",
|
||||
@ -64,34 +65,35 @@
|
||||
"@vaadin/vaadin-usage-statistics": "2.1.3",
|
||||
"construct-style-sheets-polyfill": "3.1.0",
|
||||
"date-fns": "2.29.3",
|
||||
"lit": "3.1.4",
|
||||
"lit": "3.2.1",
|
||||
"print-js": "1.6.0",
|
||||
"proj4": "2.12.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-router-dom": "6.23.1"
|
||||
"react-router-dom": "6.26.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-react": "7.24.7",
|
||||
"@rollup/plugin-replace": "5.0.7",
|
||||
"@rollup/pluginutils": "5.1.0",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@vitejs/plugin-react": "4.3.1",
|
||||
"async": "3.2.5",
|
||||
"glob": "10.4.1",
|
||||
"@babel/preset-react": "7.25.7",
|
||||
"@preact/signals-react-transform": "0.4.0",
|
||||
"@rollup/plugin-replace": "6.0.1",
|
||||
"@rollup/pluginutils": "5.1.2",
|
||||
"@types/react": "18.3.11",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"@vitejs/plugin-react": "4.3.3",
|
||||
"async": "3.2.6",
|
||||
"glob": "10.4.5",
|
||||
"rollup-plugin-brotli": "3.1.0",
|
||||
"rollup-plugin-visualizer": "5.12.0",
|
||||
"strip-css-comments": "5.0.0",
|
||||
"transform-ast": "2.4.4",
|
||||
"typescript": "5.4.5",
|
||||
"vite": "5.3.3",
|
||||
"vite-plugin-checker": "0.6.4",
|
||||
"typescript": "5.6.3",
|
||||
"vite": "5.4.9",
|
||||
"vite-plugin-checker": "0.8.0",
|
||||
"workbox-build": "7.1.1",
|
||||
"workbox-core": "7.1.0",
|
||||
"workbox-precaching": "7.1.0"
|
||||
},
|
||||
"hash": "1a0f17d48b329307b5862bc57499307d1b89f7d89260121c2b7189f76957c436"
|
||||
"hash": "2dc40a4f634ae025081ca2239cba00b14a35fe94ab78ac0a4dd3023d882081d5"
|
||||
},
|
||||
"overrides": {
|
||||
"@vaadin/bundles": "$@vaadin/bundles",
|
||||
|
10
pom.xml
10
pom.xml
@ -120,11 +120,6 @@
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>1.9.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.17.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
@ -191,11 +186,6 @@
|
||||
<artifactId>viritin</artifactId>
|
||||
<version>2.8.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.overcoded</groupId>
|
||||
<artifactId>panel-for-vaadin</artifactId>
|
||||
<version>24.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
|
Binary file not shown.
@ -15,33 +15,30 @@ import java.util.Optional;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Exam extends BaseEntity {
|
||||
public class Assessment extends BaseEntity {
|
||||
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
|
||||
@JoinTable(name = "EXAM_QUESTIONS", joinColumns = @JoinColumn(name = "exam_id"),
|
||||
@JoinTable(name = "ASSESSMENT_QUESTIONS", joinColumns = @JoinColumn(name = "assessment_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "question_id"))
|
||||
private List<Question> questions = new ArrayList<>();
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "exam", cascade = {CascadeType.ALL})
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "assessment", cascade = {CascadeType.ALL})
|
||||
private List<Submission> submissions = new ArrayList<>();
|
||||
|
||||
@ManyToOne
|
||||
private Candidate candidate;
|
||||
|
||||
@ManyToOne
|
||||
private Evaluation evaluation;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
|
||||
@JoinColumn(name = "EXAM_ID")
|
||||
private List<ExamEvent> examEvents = new ArrayList<>();
|
||||
@JoinColumn(name = "ASSESSMENT_ID")
|
||||
private List<AssessmentEvent> assessmentEvents = new ArrayList<>();
|
||||
|
||||
public Submission getCurrentSubmission() {
|
||||
return submissions.getLast();
|
||||
}
|
||||
|
||||
public Long getRemainingTimeSeconds() {
|
||||
final Optional<Instant> started = examEvents.stream()
|
||||
.filter(e -> e.getStatus() == ExamStatus.STARTED)
|
||||
.map(ExamEvent::getTimestamp)
|
||||
final Optional<Instant> started = assessmentEvents.stream()
|
||||
.filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst();
|
||||
|
||||
final Integer totalTimeMinutes = questions.stream()
|
||||
@ -55,24 +52,24 @@ public class Exam extends BaseEntity {
|
||||
}
|
||||
|
||||
public Instant getStartingTime() {
|
||||
final Optional<Instant> started = examEvents.stream()
|
||||
.filter(e -> e.getStatus() == ExamStatus.STARTED)
|
||||
.map(ExamEvent::getTimestamp)
|
||||
final Optional<Instant> started = assessmentEvents.stream()
|
||||
.filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst();
|
||||
|
||||
return started.orElse(null);
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return examEvents.stream().filter(e -> e.getStatus() == ExamStatus.COMPLETED)
|
||||
.map(ExamEvent::getTimestamp)
|
||||
return assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.COMPLETED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
public boolean isStarted() {
|
||||
return examEvents.stream().filter(e -> e.getStatus() == ExamStatus.STARTED)
|
||||
.map(ExamEvent::getTimestamp)
|
||||
return assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
@ -13,7 +13,7 @@ import java.time.Instant;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ExamEvent extends BaseEntity {
|
||||
public class AssessmentEvent extends BaseEntity {
|
||||
private Instant timestamp;
|
||||
private ExamStatus status;
|
||||
private AssessmentStatus status;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum ExamStatus {
|
||||
public enum AssessmentStatus {
|
||||
CREATED,
|
||||
STARTED,
|
||||
COMPLETED
|
@ -1,11 +1,7 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
@MappedSuperclass
|
||||
@ -13,18 +9,22 @@ public abstract class BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Getter
|
||||
@Setter
|
||||
private UUID id;
|
||||
|
||||
@Version
|
||||
@Getter
|
||||
private int version;
|
||||
@Getter
|
||||
@ColumnDefault("NOW()")
|
||||
private Instant created;
|
||||
@ColumnDefault("NOW()")
|
||||
@Getter
|
||||
private Instant updated;
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
@ -44,14 +44,4 @@ public abstract class BaseEntity {
|
||||
}
|
||||
return super.equals(that);
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
public void updateCreated() {
|
||||
this.created = Instant.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void updateUpdated() {
|
||||
this.updated = Instant.now();
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,17 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Candidate extends BaseEntity implements HasLabel {
|
||||
public class Candidate extends BaseEntity {
|
||||
@Column(unique = true)
|
||||
private String email;
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return email;
|
||||
}
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "candidate")
|
||||
private List<Assessment> assessments;
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public record Certification(String title, Integer year) {
|
||||
}
|
@ -1,34 +1,36 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum DocumentType {
|
||||
CARNET_DE_IDENTIDAD,
|
||||
RECIBOS_DE_PAGO,
|
||||
CONTRATO_DE_TRABAJO,
|
||||
CERTIFICADO_DE_TRABAJO,
|
||||
All,
|
||||
ID_CARD,
|
||||
PAY_STUB,
|
||||
PAY_SLIPS,
|
||||
EMPLOYMENT_CONTRACT,
|
||||
WORK_CERTIFICATES,
|
||||
NDA,
|
||||
MEMORÁNDUMS,
|
||||
APROBACIÓN_DE_CONTRATO_MTEPS,
|
||||
CERTIFICADO_DE_ANTECEDENTES,
|
||||
EVALUACIÓN_PRE_EMPLEO,
|
||||
FORMULARIO_DE_INSCRIPCIÓN_AL_SEGURO,
|
||||
FORMULARIO_DE_CANCELACIÓN_DE_SEGURO,
|
||||
TÍTULO_PROFESIONAL_1,
|
||||
CERTIFICACIÓN_PROFESIONAL_1,
|
||||
TÍTULO_PROFESIONAL_2,
|
||||
CERTIFICACIÓN_PROFECIONAL_2,
|
||||
TÍTULO_PROFESIONAL_3,
|
||||
CERTIFICACIÓN_PROFECIONAL_3,
|
||||
NORMATIVA_LABORAL_GENERAL,
|
||||
NORMAS_DE_TRABAJO_REMOTO,
|
||||
NORMAS_DE_SEGURIDAD,
|
||||
INSTRUCTIVOS_DE_RECURSOS_HUMANOS,
|
||||
MANUAL_DE_FUNCIONES_DE_ADMINISTRACIÓN,
|
||||
MANUAL_DE_FUNCIONES_DE_INGENIERÍA,
|
||||
LEY_GENERAL_DEL_TRABAJO,
|
||||
DECRETOS_SUPREMOS,
|
||||
RESOLUCIONES_O_DISPOSICIONES_REGLAMENTARIAS,
|
||||
NORMATIVA_COMPLEMENTARIA,
|
||||
LEY_GRAL_DE_HIGIENE_SALUD_SEGURIDAD_OCUPACIONAL_Y_BIENESTAR,
|
||||
NORMATIVA_REGLAMENTARIA_PARA_DESARROLLO_DE_PASANTÍAS,
|
||||
OTROS
|
||||
MEMORANDUMS,
|
||||
CONTRACT_APPROVAL_MTEPS,
|
||||
BACKGROUND_CHECK_CERTIFICATE,
|
||||
PRE_EMPLOYMENT_EVALUATION,
|
||||
INSURANCE_REGISTRATION_FORM,
|
||||
INSURANCE_CANCELLATION_FORM,
|
||||
PROFESSIONAL_DEGREE_1,
|
||||
PROFESSIONAL_CERTIFICATE_1,
|
||||
PROFESSIONAL_DEGREE_2,
|
||||
PROFESSIONAL_CERTIFICATE_2,
|
||||
PROFESSIONAL_DEGREE_3,
|
||||
PROFESSIONAL_CERTIFICATE_3,
|
||||
GENERAL_LABOR_REGULATIONS,
|
||||
REMOTE_WORK_GUIDELINES,
|
||||
SAFETY_REGULATIONS,
|
||||
HUMAN_RESOURCES_GUIDELINES,
|
||||
ADMINISTRATION_FUNCTIONS_MANUAL,
|
||||
ENGINEERING_FUNCTIONS_MANUAL,
|
||||
GENERAL_LABOR_LAW,
|
||||
SUPREME_DECREE,
|
||||
REGULATORY_RESOLUTION,
|
||||
COMPLEMENTARY_REGULATION,
|
||||
HEALTH_SAFETY_LAW,
|
||||
INTERNSHIP_RULES,
|
||||
OTHER
|
||||
}
|
||||
|
@ -1,27 +1,24 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.Type;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Employee extends BaseEntity implements UserDetails, HasLabel {
|
||||
public class Employee extends BaseEntity implements UserDetails {
|
||||
|
||||
private String username;
|
||||
@NotNull(message = "El nombre no puede estar vacío")
|
||||
@ -30,7 +27,6 @@ public class Employee extends BaseEntity implements UserDetails, HasLabel {
|
||||
@NotNull(message = "El apellido no puede estar vacío")
|
||||
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El apellido solo debe contener letras")
|
||||
private String lastName;
|
||||
@MinAge(18)
|
||||
private LocalDate birthday;
|
||||
@Pattern(regexp = "^[a-zA-Z ,]+$", message = "La ciudad de nacimiento solo debe contener letras, espacios o comas")
|
||||
private String birthCity;
|
||||
@ -44,10 +40,6 @@ public class Employee extends BaseEntity implements UserDetails, HasLabel {
|
||||
private String phoneNumber;
|
||||
@Email(message = "El correo personal no tiene un formato válido")
|
||||
private String personalEmail;
|
||||
@Pattern(regexp = "^[0-9]+$", message = "El número de teléfono debe contener solo números")
|
||||
private String phoneNumberProfessional;
|
||||
@Email(message = "El correo profesional no tiene un formato válido")
|
||||
private String professionalEmail;
|
||||
|
||||
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El cargo solo debe contener letras")
|
||||
private String position;
|
||||
@ -64,50 +56,56 @@ public class Employee extends BaseEntity implements UserDetails, HasLabel {
|
||||
private String emergencyCPhone;
|
||||
@Email(message = "El correo de contacto de emergencia no tiene un formato válido")
|
||||
private String emergencyCEmail;
|
||||
|
||||
@Max(value = 10, message = "El número de hijos no puede exceder a 10")
|
||||
@Pattern(regexp = "^[0-9]+$", message = "La cantidad de hijos debe contener solo números")
|
||||
private String numberOfChildren;
|
||||
@Pattern(regexp = "^[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 issuedIn;
|
||||
private String pTitle1;
|
||||
private String pTitle2;
|
||||
private String pTitle3;
|
||||
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
@ColumnDefault("JSON_ARRAY()")
|
||||
private List<Certification> educationTitles = Lists.newArrayList();
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
@ColumnDefault("JSON_ARRAY()")
|
||||
private List<Certification> certifications = Lists.newArrayList();
|
||||
private String pStudy1;
|
||||
private String pStudy2;
|
||||
private String pStudy3;
|
||||
|
||||
private String certification1;
|
||||
private String certification2;
|
||||
private String certification3;
|
||||
private String certification4;
|
||||
private String recognition;
|
||||
private String achievements;
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
@ColumnDefault("JSON_ARRAY()")
|
||||
private List<Language> languages = Lists.newArrayList();
|
||||
|
||||
private String language;
|
||||
private String languageLevel;
|
||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "El código debe contener solo letras y números")
|
||||
private String cod;
|
||||
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El lead manager solo debe contener letras")
|
||||
private String leadManager;
|
||||
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El proyecto solo debe contener letras")
|
||||
private String project;
|
||||
|
||||
private LocalDate dateOfEntry;
|
||||
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 BigDecimal salaryTotal = BigDecimal.ZERO;
|
||||
private BigDecimal salaryBasic = BigDecimal.ZERO;
|
||||
private BigDecimal professionalBonus = BigDecimal.ZERO;
|
||||
private BigDecimal tenureBonus = BigDecimal.ZERO;
|
||||
@Pattern(regexp = "^[0-9]+(\\.[0-9]{1,2})?$", message = "El salario debe ser un número con hasta dos decimales")
|
||||
private String salary;
|
||||
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre del banco solo debe contener letras")
|
||||
private String bankName;
|
||||
@Pattern(regexp = "^[0-9]+$", message = "El número de cuenta debe contener solo números")
|
||||
private String accountNumber;
|
||||
@Setter
|
||||
private String customContractType;
|
||||
|
||||
private String gpss;
|
||||
private String sss;
|
||||
@Pattern(regexp = "^[a-zA-Z ]+$", message = "Los derechohabientes solo deben contener letras")
|
||||
private String beneficiarie1;
|
||||
@Pattern(regexp = "^[a-zA-Z ]+$", message = "Los derechohabientes solo deben contener letras")
|
||||
private String beneficiarie2;
|
||||
private String beneficiaries;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String profileImage;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ -115,7 +113,7 @@ public class Employee extends BaseEntity implements UserDetails, HasLabel {
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return Lists.newArrayList(new SimpleGrantedAuthority("ROLE_" + this.role.name()));
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -148,31 +146,13 @@ public class Employee extends BaseEntity implements UserDetails, HasLabel {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private MaritalStatus maritalStatus;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Gender gender;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ContractType contractType;
|
||||
@Size(max = 255, message = "El detalle del contrato no debe exceder 255 caracteres")
|
||||
private String otherContractDetail;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Role role = Role.USER;
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return String.format("%s %s", firstName, lastName);
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
ACTIVE,
|
||||
INACTIVE
|
||||
}
|
||||
|
||||
public enum Gender {
|
||||
MALE,
|
||||
FEMALE
|
||||
}
|
||||
@Enumerated(EnumType.STRING)
|
||||
private MaritalStatus maritalStatus;
|
||||
|
||||
public enum MaritalStatus {
|
||||
SINGLE,
|
||||
@ -181,13 +161,11 @@ public class Employee extends BaseEntity implements UserDetails, HasLabel {
|
||||
DIVORCED
|
||||
}
|
||||
|
||||
public enum ContractType {
|
||||
CONTRATO_LABORAL,
|
||||
CONTRATO_CIVIL_O_SERVICIOS,
|
||||
CONTRATO_PLAZO_FIJO,
|
||||
CONSULTORIA_INTERNA,
|
||||
CONSULTORIA_EXTERNA,
|
||||
CONTRATO_MIXTO,
|
||||
OTROS
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Gender gender;
|
||||
|
||||
public enum Gender {
|
||||
MALE,
|
||||
FEMALE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum EmployeePosition {
|
||||
JUNIOR_DEV,
|
||||
JUNIOR_QA,
|
||||
SENIOR_DEV,
|
||||
SENIOR_QA
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Evaluation extends BaseEntity {
|
||||
@ManyToOne
|
||||
private Candidate candidate;
|
||||
private Integer points;
|
||||
private EmployeePosition candidatePosition;
|
||||
private Employee interviewer;
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
@ColumnDefault("JSON_ARRAY()")
|
||||
private List<SkillEvaluation> skillEvaluations = Lists.newArrayList();
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "evaluation")
|
||||
private List<Exam> exams;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public interface HasLabel {
|
||||
String getLabel();
|
||||
}
|
149
src/main/java/com/primefactorsolutions/model/HoursWorked.java
Normal file
149
src/main/java/com/primefactorsolutions/model/HoursWorked.java
Normal file
@ -0,0 +1,149 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class HoursWorked extends BaseEntity {
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "employee_id", nullable = true)
|
||||
private Employee employee;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "team_id", nullable = true)
|
||||
private Team team;
|
||||
|
||||
private int weekNumber;
|
||||
private LocalDate date;
|
||||
private String actividad;
|
||||
private String tareasEspecificas;
|
||||
private double hours;
|
||||
private double horasTareasEspecificas;
|
||||
private double horaspendientes;
|
||||
private double totalHours;
|
||||
|
||||
public static double calculateTotalHours(final List<HoursWorked> activities) {
|
||||
return activities.stream()
|
||||
.mapToDouble(activity -> activity.hours + activity.horasTareasEspecificas)
|
||||
.sum();
|
||||
}
|
||||
|
||||
public static double calculatePendingHours(final List<HoursWorked> activities) {
|
||||
double totalHoursWorked = calculateTotalHours(activities);
|
||||
return Math.max(0, 40 - totalHoursWorked);
|
||||
}
|
||||
|
||||
public Employee getEmployee() {
|
||||
return employee;
|
||||
}
|
||||
|
||||
public void setEmployee(final Employee employee) {
|
||||
this.employee = employee;
|
||||
}
|
||||
|
||||
public int getWeekNumber() {
|
||||
return weekNumber;
|
||||
}
|
||||
|
||||
public void setWeekNumber(final int weekNumber) {
|
||||
this.weekNumber = weekNumber;
|
||||
}
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(final LocalDate date) {
|
||||
this.date = date;
|
||||
if (date != null) {
|
||||
WeekFields weekFields = WeekFields.of(Locale.getDefault());
|
||||
this.weekNumber = date.get(weekFields.weekOfWeekBasedYear());
|
||||
}
|
||||
}
|
||||
|
||||
public String getActividad() {
|
||||
return actividad;
|
||||
}
|
||||
|
||||
public void setActividad(final String actividad) {
|
||||
this.actividad = actividad;
|
||||
}
|
||||
|
||||
public double getHours() {
|
||||
return hours;
|
||||
}
|
||||
|
||||
public void setHours(final double hours) {
|
||||
this.hours = hours;
|
||||
}
|
||||
|
||||
public double getTotalHours() {
|
||||
double total = this.getHours();
|
||||
return totalHours + total;
|
||||
}
|
||||
|
||||
public void setTotalHours(final double totalHours) {
|
||||
this.totalHours = totalHours;
|
||||
}
|
||||
|
||||
public Team getTeam() {
|
||||
return team;
|
||||
}
|
||||
|
||||
public void setTeam(final Team team) {
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
public String getTareasEspecificas() {
|
||||
return tareasEspecificas;
|
||||
}
|
||||
|
||||
public void setTareasEspecificas(final String tareasEspecificas) {
|
||||
this.tareasEspecificas = tareasEspecificas;
|
||||
}
|
||||
|
||||
public double getHorasTareasEspecificas() {
|
||||
return horasTareasEspecificas;
|
||||
}
|
||||
|
||||
public void setHorasTareasEspecificas(final double horasTareasEspecificas) {
|
||||
this.tareasEspecificas = tareasEspecificas;
|
||||
}
|
||||
public double getHoraspendientes() {
|
||||
//double horasTrabajadas = this.getTotalHours() + this.getHorasTareasEspecificas();
|
||||
return 40;
|
||||
}
|
||||
|
||||
public void setHoraspendientes(final double horaspendientes) {
|
||||
this.horaspendientes = horaspendientes;
|
||||
}
|
||||
|
||||
public double getHoursWorked() {
|
||||
return hours;
|
||||
}
|
||||
|
||||
public void setHoursWorked(final double hoursWorked) {
|
||||
this.hours = hoursWorked;
|
||||
}
|
||||
|
||||
public double getTotalHoursWorked() {
|
||||
return totalHours;
|
||||
}
|
||||
|
||||
public void setTotalHoursWorked(final double totalHoursWorked) {
|
||||
this.totalHours = totalHoursWorked;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public record Language(String name, Proficiency proficiency) {
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = MinAgeValidator.class)
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MinAge {
|
||||
String message() default "El empleado debe ser mayor de {value} años";
|
||||
Class<?>[] groups() default {};
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
int value();
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
public class MinAgeValidator implements ConstraintValidator<MinAge, LocalDate> {
|
||||
private int minAge;
|
||||
|
||||
@Override
|
||||
public void initialize(final MinAge constraintAnnotation) {
|
||||
this.minAge = constraintAnnotation.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(final LocalDate birthday, final ConstraintValidatorContext context) {
|
||||
if (birthday == null) {
|
||||
return true;
|
||||
}
|
||||
return ChronoUnit.YEARS.between(birthday, LocalDate.now()) >= minAge;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum Proficiency {
|
||||
BASIC,
|
||||
ADVANCED,
|
||||
FLUENT
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public record SkillEvaluation(SkillType skillType, SkillLevel level, String comments) {
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum SkillLevel {
|
||||
NEEDS_IMPROVEMENT,
|
||||
FAIR,
|
||||
VERY_GOOD,
|
||||
EXCELLENT
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum SkillType {
|
||||
TECHNICAL_KNOWLEDGE,
|
||||
ASSERTIVENESS,
|
||||
COMPLETENESS_AND_CONCISENESS,
|
||||
COMMUNICATION
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
@ -7,6 +9,9 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@ -18,12 +23,14 @@ public class Submission extends BaseEntity {
|
||||
private Question question;
|
||||
|
||||
@Lob
|
||||
private String text;
|
||||
private String response;
|
||||
|
||||
private String output;
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
private Map<String, Object> results;
|
||||
|
||||
private SubmissionStatus submissionStatus;
|
||||
|
||||
@ManyToOne
|
||||
private Exam exam;
|
||||
private Assessment assessment;
|
||||
}
|
||||
|
@ -10,4 +10,7 @@ public enum TimeOffRequestStatus {
|
||||
VENCIDO,
|
||||
|
||||
SOLICITADO,
|
||||
EN_REVISION,
|
||||
COMPLETADO,
|
||||
CANCELADO,
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum TimeOffRequestType {
|
||||
TODOS,
|
||||
AÑO_NUEVO,
|
||||
LUNES_CARNAVAL,
|
||||
MARTES_CARNAVAL,
|
||||
@ -13,6 +14,7 @@ public enum TimeOffRequestType {
|
||||
AÑO_NUEVO_ANDINO,
|
||||
ANIVERSARIO_DEPARTAMENTAL,
|
||||
DIA_DE_TODOS_LOS_DIFUNTOS,
|
||||
|
||||
CUMPLEAÑOS,
|
||||
MATERNIDAD,
|
||||
PATERNIDAD,
|
||||
|
21
src/main/java/com/primefactorsolutions/model/Timesheet.java
Normal file
21
src/main/java/com/primefactorsolutions/model/Timesheet.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Timesheet extends BaseEntity {
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
private List<TimesheetEntry> entries;
|
||||
}
|
@ -1,30 +1,13 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.IsoFields;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TimesheetEntry extends BaseEntity {
|
||||
|
||||
private String task;
|
||||
private String details;
|
||||
private LocalDate date;
|
||||
private double hours;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "employee_id")
|
||||
private Employee employee;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "team_id")
|
||||
private Team team;
|
||||
|
||||
public int getWeekNumber() {
|
||||
return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
}
|
||||
public class TimesheetEntry {
|
||||
private TaskType taskType;
|
||||
private int hours;
|
||||
}
|
||||
|
@ -8,22 +8,20 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TimeOff extends BaseEntity {
|
||||
public class Vacation extends BaseEntity {
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TimeOffRequestType category;
|
||||
private LocalDate date;
|
||||
private Integer monthOfYear;
|
||||
private Integer dayOfMonth;
|
||||
private Double duration;
|
||||
private Double expiration;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Type type;
|
||||
|
||||
public enum Type {
|
||||
FIXED,
|
||||
MOVABLE,
|
@ -1,46 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.IsoFields;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public record Week(LocalDate from, LocalDate to) {
|
||||
@Override
|
||||
public String toString() {
|
||||
final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
return String.format("from %s to %s", formatter.format(from), formatter.format(to));
|
||||
}
|
||||
|
||||
public static Week getCurrent() {
|
||||
final int weekNumber = LocalDate.now().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
final LocalDate from = getFirstDayOfWeek(weekNumber);
|
||||
|
||||
return new Week(from, from.plusDays(6));
|
||||
}
|
||||
|
||||
public static List<Week> getLastWeeks(final int numberOfWeeks) {
|
||||
final int weekNumber = LocalDate.now().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
LocalDate from = getFirstDayOfWeek(weekNumber);
|
||||
final ArrayList<Week> result = Lists.newArrayList();
|
||||
|
||||
for (int i = 0; i < numberOfWeeks; i++) {
|
||||
result.add(new Week(from, from.plusDays(6)));
|
||||
from = from.minusDays(7);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static LocalDate getFirstDayOfWeek(final int weekNumber) {
|
||||
return LocalDate
|
||||
.now()
|
||||
.with(WeekFields.of(Locale.US).getFirstDayOfWeek())
|
||||
.with(WeekFields.of(Locale.US).weekOfWeekBasedYear(), weekNumber);
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.Evaluation;
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EvaluationRepository extends JpaRepository<Evaluation, UUID> {
|
||||
public interface AssessmentRepository extends JpaRepository<Assessment, UUID> {
|
||||
}
|
@ -9,8 +9,9 @@ import java.util.UUID;
|
||||
|
||||
public interface EmployeeRepository extends JpaRepository<Employee, UUID> {
|
||||
Optional<Employee> findByUsername(String username);
|
||||
|
||||
Optional<Employee> findByPersonalEmail(String personalEmail);
|
||||
List<Employee> findAllByTeamId(UUID teamId);
|
||||
//Optional<Employee> findByTeamIdAndLeadManagerTrue(UUID teamId);
|
||||
Optional<Employee> findByTeamId(UUID teamId);
|
||||
|
||||
List<Employee> findByTeamName(String teamName);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.Exam;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ExamRepository extends JpaRepository<Exam, UUID> {
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.HoursWorked;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
public interface HoursWorkedRepository extends JpaRepository<HoursWorked, UUID> {
|
||||
List<HoursWorked> findByWeekNumber(int weekNumber);
|
||||
List<HoursWorked> findByDate(LocalDate date);
|
||||
List<HoursWorked> findByEmployeeIdAndWeekNumber(UUID employeeId, int weekNumber);
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.TimeOff;
|
||||
import com.primefactorsolutions.model.TimeOffRequestType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface TimeOffRepository extends JpaRepository<TimeOff, UUID> {
|
||||
TimeOff findByCategory(TimeOffRequestType category);
|
||||
List<TimeOff> findByDateBetween(LocalDate from, LocalDate to);
|
||||
}
|
@ -10,7 +10,6 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface TimeOffRequestRepository extends JpaRepository<TimeOffRequest, UUID> {
|
||||
List<TimeOffRequest> findByOrderByUpdatedDesc();
|
||||
List<TimeOffRequest> findByEmployeeId(UUID idEmployee);
|
||||
Optional<TimeOffRequest> findByEmployeeIdAndState(UUID employeeId, TimeOffRequestStatus state);
|
||||
List<TimeOffRequest> findByEmployeeIdAndCategory(UUID employeeId, TimeOffRequestType category);
|
||||
|
@ -1,12 +0,0 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface TimesheetEntryRepository extends JpaRepository<TimesheetEntry, UUID> {
|
||||
List<TimesheetEntry> findByDateBetween(LocalDate from, LocalDate to);
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.TimeOffRequestType;
|
||||
import com.primefactorsolutions.model.Vacation;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface VacationRepository extends JpaRepository<Vacation, UUID> {
|
||||
Vacation findByCategory(TimeOffRequestType category);
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.repositories.AssessmentRepository;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class AssessmentService {
|
||||
|
||||
private final AssessmentRepository assessmentRepository;
|
||||
private final EntityManager entityManager;
|
||||
private final JavaMailSender emailSender;
|
||||
|
||||
public Assessment createOrUpdate(final Assessment assessment) {
|
||||
final Assessment saved = assessmentRepository.save(assessment);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
public void sendEmail(final Assessment assessment) {
|
||||
try {
|
||||
final String evaluationLink = String.format("https://careers.primefactorsolutions.com/evaluation/%s",
|
||||
assessment.getId());
|
||||
final SimpleMailMessage message = new SimpleMailMessage();
|
||||
message.setFrom("no-reply@primefactorsolutions.com");
|
||||
message.setBcc("no-reply@primefactorsolutions.com");
|
||||
message.setTo(assessment.getCandidate().getEmail());
|
||||
message.setSubject("PFS - Evaluacion Tecnica");
|
||||
message.setText(String.format("Estimado candidato,\n\nGracias por su candidatura. En esta etapa del "
|
||||
+ "proceso de seleccion, usted debe completar "
|
||||
+ "una evaluacion tecnica de programacion en JAVA. La prueba tiene una duracion de 30 minutos y "
|
||||
+ "puede completarla cuando tenga una buena "
|
||||
+ "conexion de internet.\n\n"
|
||||
+ "Haga click aca: " + evaluationLink + "\n\n"
|
||||
+ "Exito!"));
|
||||
|
||||
emailSender.send(message);
|
||||
log.info("Sent email to {}", assessment.getCandidate().getEmail());
|
||||
} catch (Exception e) {
|
||||
log.error("Error sending email to {}", assessment.getCandidate().getEmail(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Assessment> getAssessments() {
|
||||
return assessmentRepository.findAll();
|
||||
}
|
||||
|
||||
public Assessment getAssessment(final UUID id) {
|
||||
return assessmentRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public Assessment startAssessment(final UUID id) {
|
||||
final Assessment assessment = assessmentRepository.findById(id).get();
|
||||
if (assessment.isStarted()) {
|
||||
return assessment;
|
||||
}
|
||||
|
||||
assessment.getAssessmentEvents().add(new AssessmentEvent(Instant.now(), AssessmentStatus.STARTED));
|
||||
|
||||
return assessmentRepository.save(assessment);
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final UUID assessmentId, final UUID currSubmissionId) {
|
||||
return getNextSubmission(assessmentId, currSubmissionId, true);
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final UUID assessmentId, final UUID currSubmissionId,
|
||||
final boolean checkCompleted) {
|
||||
final Assessment assessment = assessmentRepository.findById(assessmentId).get();
|
||||
|
||||
Assessment saved;
|
||||
if (currSubmissionId == null) {
|
||||
if (checkCompleted && assessment.isCompleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question firstQuestion = assessment.getQuestions().stream().findFirst().get();
|
||||
final Optional<Submission> submissionToReturn = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(firstQuestion))
|
||||
.findFirst();
|
||||
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
final Submission result = new Submission(firstQuestion, firstQuestion.getContent(), Map.of(),
|
||||
SubmissionStatus.FAIL, assessment);
|
||||
assessment.getSubmissions().add(result);
|
||||
}
|
||||
|
||||
saved = assessmentRepository.save(assessment);
|
||||
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(firstQuestion))
|
||||
.findFirst();
|
||||
|
||||
return submissionToReturn2.get();
|
||||
}
|
||||
|
||||
final Submission currSubmission = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getId().equals(currSubmissionId))
|
||||
.findFirst().get();
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
int idx = assessment.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == assessment.getQuestions().size() - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question nextQuestion = assessment.getQuestions().get(idx + 1);
|
||||
|
||||
final Optional<Submission> submissionToReturn = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
final Submission result = new Submission(nextQuestion, nextQuestion.getContent(), Map.of(),
|
||||
SubmissionStatus.FAIL, assessment);
|
||||
assessment.getSubmissions().add(result);
|
||||
}
|
||||
|
||||
saved = assessmentRepository.save(assessment);
|
||||
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
return submissionToReturn2.get();
|
||||
}
|
||||
|
||||
public Submission getPrevSubmission(final UUID assessmentId, final Submission currSubmission) {
|
||||
if (currSubmission == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
Assessment assessment = assessmentRepository.findById(assessmentId).get();
|
||||
int idx = assessment.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question prevQuestion = assessment.getQuestions().get(idx - 1);
|
||||
|
||||
return assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(prevQuestion))
|
||||
.findFirst().orElseThrow(() -> new IllegalStateException("submission invalid"));
|
||||
}
|
||||
|
||||
public Assessment completeAssessment(final UUID id) {
|
||||
Assessment assessment = assessmentRepository.findById(id).get();
|
||||
Optional<AssessmentEvent> completed = assessment.getAssessmentEvents().stream()
|
||||
.filter(e -> e.getStatus() == AssessmentStatus.COMPLETED)
|
||||
.findFirst();
|
||||
|
||||
if (completed.isPresent()) {
|
||||
return assessment;
|
||||
}
|
||||
|
||||
assessment.getAssessmentEvents().add(new AssessmentEvent(Instant.now(), AssessmentStatus.COMPLETED));
|
||||
Assessment saved = assessmentRepository.save(assessment);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
public void saveSubmission(final UUID id, final Submission currSubmission) {
|
||||
Assessment assessment = assessmentRepository.findById(id).get();
|
||||
final Submission submission = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getId().equals(currSubmission.getId()))
|
||||
.findFirst().get();
|
||||
|
||||
submission.setResponse(currSubmission.getResponse());
|
||||
Assessment saved = assessmentRepository.save(assessment);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Assessment saveAssessment(final Assessment assessment) {
|
||||
Candidate merged = entityManager.merge(assessment.getCandidate());
|
||||
List<Question> mergedQuestions = assessment.getQuestions().stream().map(entityManager::merge)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assessment.setCandidate(merged);
|
||||
assessment.setQuestions(mergedQuestions);
|
||||
|
||||
return assessmentRepository.save(assessment);
|
||||
}
|
||||
}
|
@ -13,8 +13,8 @@ import java.util.UUID;
|
||||
public class CandidateService {
|
||||
private final CandidateRepository candidateRepository;
|
||||
|
||||
public Candidate createOrUpdate(final Candidate candidate) {
|
||||
final Candidate saved = candidateRepository.save(candidate);
|
||||
public Candidate createOrUpdate(final Candidate assessment) {
|
||||
final Candidate saved = candidateRepository.save(assessment);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
package com.primefactorsolutions.service;
|
||||
import com.google.common.base.Strings;
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import lombok.AllArgsConstructor;
|
||||
@ -38,10 +37,6 @@ public class EmployeeService {
|
||||
.build();
|
||||
}
|
||||
|
||||
public List<Employee> getEmployees() {
|
||||
return employeeRepository.findAll();
|
||||
}
|
||||
|
||||
public Employee getDetachedEmployeeByUsername(final String username) {
|
||||
final Employee employee = employeeRepository.findByUsername(username).orElse(null);
|
||||
|
||||
@ -55,18 +50,14 @@ public class EmployeeService {
|
||||
|
||||
public String getTeamLeadName(final UUID teamId) {
|
||||
// Encuentra al empleado con el rol de lead_manager en el equipo especificado
|
||||
List<Employee> teamMembers = employeeRepository.findAllByTeamId(teamId);
|
||||
Optional<Employee> leadManager = teamMembers.stream()
|
||||
.filter(e -> Strings.isNullOrEmpty(e.getLeadManager()))
|
||||
.findFirst();
|
||||
Optional<Employee> leadManager = employeeRepository.findByTeamId(teamId);
|
||||
|
||||
return leadManager.map(employee -> employee.getFirstName() + " " + employee.getLastName())
|
||||
.orElse("No asignado");
|
||||
}
|
||||
|
||||
public List<Employee> findEmployees(
|
||||
final int start, final int pageSize, final String sortProperty, final boolean asc) {
|
||||
List<Employee> employees = employeeRepository.findAll();
|
||||
Collections.reverse(employees);
|
||||
|
||||
int end = Math.min(start + pageSize, employees.size());
|
||||
employees.sort(new BeanComparator<>(sortProperty));
|
||||
@ -80,7 +71,6 @@ public class EmployeeService {
|
||||
|
||||
public List<Employee> findEmployees(final int start, final int pageSize) {
|
||||
List<Employee> employees = employeeRepository.findAll();
|
||||
Collections.reverse(employees);
|
||||
|
||||
int end = Math.min(start + pageSize, employees.size());
|
||||
return employees.subList(start, end);
|
||||
@ -130,9 +120,7 @@ public class EmployeeService {
|
||||
}
|
||||
|
||||
public List<Employee> findAllEmployees() {
|
||||
List<Employee> employees = employeeRepository.findAll();
|
||||
Collections.reverse(employees);
|
||||
return employees;
|
||||
return employeeRepository.findAll();
|
||||
}
|
||||
|
||||
public List<Employee> findEmployeesByTeam(final String teamName) {
|
||||
|
@ -1,27 +0,0 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.Evaluation;
|
||||
import com.primefactorsolutions.repositories.EvaluationRepository;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class EvaluationService {
|
||||
private final EvaluationRepository evaluationRepository;
|
||||
|
||||
public Evaluation createOrUpdate(final Evaluation candidate) {
|
||||
return evaluationRepository.save(candidate);
|
||||
}
|
||||
|
||||
public List<Evaluation> getEvaluations() {
|
||||
return evaluationRepository.findAll();
|
||||
}
|
||||
|
||||
public Evaluation getEvaluation(final UUID id) {
|
||||
return evaluationRepository.findById(id).orElse(null);
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.repositories.ExamRepository;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class ExamService {
|
||||
|
||||
private final ExamRepository examRepository;
|
||||
private final EntityManager entityManager;
|
||||
private final JavaMailSender emailSender;
|
||||
|
||||
public Exam createOrUpdate(final Exam exam) {
|
||||
return examRepository.save(exam);
|
||||
}
|
||||
|
||||
public void sendEmail(final Exam exam) {
|
||||
try {
|
||||
final String evaluationLink = String.format("https://careers.primefactorsolutions.com/candidate-exam/%s",
|
||||
exam.getId());
|
||||
final SimpleMailMessage message = new SimpleMailMessage();
|
||||
message.setFrom("no-reply@primefactorsolutions.com");
|
||||
message.setBcc("no-reply@primefactorsolutions.com");
|
||||
message.setTo(exam.getCandidate().getEmail());
|
||||
message.setSubject("PFS - Evaluacion Tecnica");
|
||||
message.setText(String.format("Estimado candidato,\n\nGracias por su candidatura. En esta etapa del "
|
||||
+ "proceso de seleccion, usted debe completar "
|
||||
+ "una evaluacion tecnica de programacion en JAVA. La prueba tiene una duracion de 30 minutos y "
|
||||
+ "puede completarla cuando tenga una buena "
|
||||
+ "conexion de internet.\n\n"
|
||||
+ "Haga click aca: " + evaluationLink + "\n\n"
|
||||
+ "Exito!"));
|
||||
|
||||
emailSender.send(message);
|
||||
log.info("Sent email to {}", exam.getCandidate().getEmail());
|
||||
} catch (Exception e) {
|
||||
log.error("Error sending email to {}", exam.getCandidate().getEmail(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Exam> getExams() {
|
||||
return examRepository.findAll();
|
||||
}
|
||||
|
||||
public Exam getExam(final UUID id) {
|
||||
return examRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public Exam startExam(final UUID id) {
|
||||
final Exam exam = examRepository.findById(id).get();
|
||||
|
||||
if (exam.isStarted()) {
|
||||
return exam;
|
||||
}
|
||||
|
||||
exam.getExamEvents().add(new ExamEvent(Instant.now(), ExamStatus.STARTED));
|
||||
|
||||
return examRepository.save(exam);
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final UUID examId, final UUID currSubmissionId) {
|
||||
return getNextSubmission(examId, currSubmissionId, true);
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final UUID examId, final UUID currSubmissionId,
|
||||
final boolean checkCompleted) {
|
||||
final Exam exam = examRepository.findById(examId).get();
|
||||
|
||||
Exam saved;
|
||||
if (currSubmissionId == null) {
|
||||
if (checkCompleted && exam.isCompleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question firstQuestion = exam.getQuestions().stream().findFirst().get();
|
||||
final Optional<Submission> submissionToReturn = exam.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(firstQuestion))
|
||||
.findFirst();
|
||||
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
final Submission result = new Submission(firstQuestion, firstQuestion.getContent(), null,
|
||||
SubmissionStatus.FAIL, exam);
|
||||
exam.getSubmissions().add(result);
|
||||
}
|
||||
|
||||
saved = examRepository.save(exam);
|
||||
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(firstQuestion))
|
||||
.findFirst();
|
||||
|
||||
return submissionToReturn2.get();
|
||||
}
|
||||
|
||||
final Submission currSubmission = exam.getSubmissions().stream()
|
||||
.filter(s -> s.getId().equals(currSubmissionId))
|
||||
.findFirst().get();
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
int idx = exam.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == exam.getQuestions().size() - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question nextQuestion = exam.getQuestions().get(idx + 1);
|
||||
|
||||
final Optional<Submission> submissionToReturn = exam.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
final Submission result = new Submission(nextQuestion, nextQuestion.getContent(), null,
|
||||
SubmissionStatus.FAIL, exam);
|
||||
exam.getSubmissions().add(result);
|
||||
}
|
||||
|
||||
saved = examRepository.save(exam);
|
||||
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
return submissionToReturn2.get();
|
||||
}
|
||||
|
||||
public Submission getPrevSubmission(final UUID examId, final Submission currSubmission) {
|
||||
if (currSubmission == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
Exam exam = examRepository.findById(examId).get();
|
||||
int idx = exam.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question prevQuestion = exam.getQuestions().get(idx - 1);
|
||||
|
||||
return exam.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(prevQuestion))
|
||||
.findFirst().orElseThrow(() -> new IllegalStateException("submission invalid"));
|
||||
}
|
||||
|
||||
public Exam completeExam(final UUID id) {
|
||||
Exam exam = examRepository.findById(id).get();
|
||||
Optional<ExamEvent> completed = exam.getExamEvents().stream()
|
||||
.filter(e -> e.getStatus() == ExamStatus.COMPLETED)
|
||||
.findFirst();
|
||||
|
||||
if (completed.isPresent()) {
|
||||
return exam;
|
||||
}
|
||||
|
||||
exam.getExamEvents().add(new ExamEvent(Instant.now(), ExamStatus.COMPLETED));
|
||||
Exam saved = examRepository.save(exam);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
public void saveSubmission(final UUID id, final Submission currSubmission) {
|
||||
Exam exam = examRepository.findById(id).get();
|
||||
final Submission submission = exam.getSubmissions().stream()
|
||||
.filter(s -> s.getId().equals(currSubmission.getId()))
|
||||
.findFirst().get();
|
||||
|
||||
submission.setText(currSubmission.getText());
|
||||
Exam saved = examRepository.save(exam);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Exam saveExam(final Exam exam) {
|
||||
Candidate merged = entityManager.merge(exam.getCandidate());
|
||||
List<Question> mergedQuestions = exam.getQuestions().stream().map(entityManager::merge)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
exam.setCandidate(merged);
|
||||
exam.setQuestions(mergedQuestions);
|
||||
|
||||
return examRepository.save(exam);
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.HoursWorked;
|
||||
import com.primefactorsolutions.repositories.HoursWorkedRepository;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class HoursWorkedService {
|
||||
private final HoursWorkedRepository hoursWorkedRepository;
|
||||
|
||||
@Autowired
|
||||
public HoursWorkedService(final HoursWorkedRepository hoursWorkedRepository) {
|
||||
this.hoursWorkedRepository = hoursWorkedRepository;
|
||||
}
|
||||
|
||||
public List<HoursWorked> findAll() {
|
||||
return hoursWorkedRepository.findAll();
|
||||
}
|
||||
|
||||
public double getTotalHoursWorkedByEmployeeForWeek(final UUID employeeId, final int weekNumber) {
|
||||
List<HoursWorked> hoursWorkedList = hoursWorkedRepository.findByWeekNumber(weekNumber);
|
||||
return hoursWorkedList.stream()
|
||||
.filter(hw -> hw.getEmployee().getId().equals(employeeId))
|
||||
.mapToDouble(HoursWorked::getTotalHours)
|
||||
.sum();
|
||||
}
|
||||
|
||||
public HoursWorked findHoursWorked(final UUID id) {
|
||||
Optional<HoursWorked> hoursWorked = hoursWorkedRepository.findById(id);
|
||||
HoursWorked hw = hoursWorked.get();
|
||||
return hw;
|
||||
}
|
||||
|
||||
public HoursWorked saveHoursWorked(final HoursWorked hoursWorked) {
|
||||
return hoursWorkedRepository.save(hoursWorked);
|
||||
}
|
||||
|
||||
public HoursWorked save(final HoursWorked hoursWorked) {
|
||||
return hoursWorkedRepository.save(hoursWorked);
|
||||
}
|
||||
|
||||
public double getTotalHoursForEmployee(final UUID employeeId, final int weekNumber) {
|
||||
List<HoursWorked> activities = hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber);
|
||||
return HoursWorked.calculateTotalHours(activities);
|
||||
}
|
||||
|
||||
public double getPendingHoursForEmployee(final UUID employeeId, final int weekNumber) {
|
||||
List<HoursWorked> activities = hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber);
|
||||
return HoursWorked.calculatePendingHours(activities);
|
||||
}
|
||||
|
||||
public List<HoursWorked> findByWeekNumber(final int weekNumber) {
|
||||
return hoursWorkedRepository.findByWeekNumber(weekNumber);
|
||||
}
|
||||
|
||||
public List<HoursWorked> findByDate(final LocalDate date) {
|
||||
return hoursWorkedRepository.findByDate(date);
|
||||
}
|
||||
|
||||
public List<HoursWorked> findByDateAndWeekNumber(final LocalDate date, final int weekNumber) {
|
||||
return hoursWorkedRepository.findByDate(date);
|
||||
}
|
||||
|
||||
public List<HoursWorked> findHoursWorkeds(
|
||||
final int start, final int pageSize, final String sortProperty, final boolean asc) {
|
||||
List<HoursWorked> hoursWorkeds = hoursWorkedRepository.findAll();
|
||||
|
||||
int end = Math.min(start + pageSize, hoursWorkeds.size());
|
||||
hoursWorkeds.sort(new BeanComparator<>(sortProperty));
|
||||
|
||||
if (!asc) {
|
||||
Collections.reverse(hoursWorkeds);
|
||||
}
|
||||
|
||||
return hoursWorkeds.subList(start, end);
|
||||
}
|
||||
|
||||
public List<HoursWorked> findHoursWorkeds(final int start, final int pageSize) {
|
||||
List<HoursWorked> hoursWorkeds = hoursWorkedRepository.findAll();
|
||||
|
||||
int end = Math.min(start + pageSize, hoursWorkeds.size());
|
||||
return hoursWorkeds.subList(start, end);
|
||||
}
|
||||
|
||||
public HoursWorked getHoursWorked(final UUID id) {
|
||||
final Optional<HoursWorked> hoursWorked = hoursWorkedRepository.findById(id);
|
||||
return hoursWorked.get();
|
||||
}
|
||||
|
||||
public List<HoursWorked> findListHoursWorkedEmployee(final UUID employeeId, final int weekNumber) {
|
||||
return hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber);
|
||||
}
|
||||
|
||||
}
|
@ -2,7 +2,7 @@ package com.primefactorsolutions.service;
|
||||
|
||||
import com.openhtmltopdf.pdfboxout.PdfBoxRenderer;
|
||||
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.repositories.HoursWorkedRepository;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.DefaultObjectWrapper;
|
||||
import freemarker.template.Template;
|
||||
@ -24,9 +24,13 @@ import java.util.TimeZone;
|
||||
|
||||
@Service
|
||||
public class ReportService {
|
||||
public ReportService() {
|
||||
private final HoursWorkedRepository hoursWorkedRepository;
|
||||
|
||||
public ReportService(final HoursWorkedRepository hoursWorkedRepository) {
|
||||
this.hoursWorkedRepository = hoursWorkedRepository;
|
||||
}
|
||||
|
||||
// Este método ahora solo crea el archivo Excel a partir de los datos que recibe.
|
||||
public byte[] writeAsExcel(final String reportName, final List<String> headers,
|
||||
final List<Map<String, Object>> data, final String selectedTeam,
|
||||
final int weekNumber, final int currentYear)
|
||||
@ -170,81 +174,4 @@ public class ReportService {
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
public byte[] generateExcelReport(final Employee employee) {
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
Sheet sheet = workbook.createSheet("Empleado");
|
||||
Row titleRow = sheet.createRow(0);
|
||||
Cell titleCell = titleRow.createCell(0);
|
||||
titleCell.setCellValue("Información General del Empleado");
|
||||
CellStyle titleStyle = workbook.createCellStyle();
|
||||
Font titleFont = workbook.createFont();
|
||||
titleFont.setBold(true);
|
||||
titleFont.setFontHeightInPoints((short) 16);
|
||||
titleStyle.setFont(titleFont);
|
||||
titleStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
titleCell.setCellStyle(titleStyle);
|
||||
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 40));
|
||||
Row header = sheet.createRow(2);
|
||||
String[] headers = {
|
||||
"Username", "Nombres", "Apellidos", "Estado", "Género", "Fecha de Nacimiento", "Edad",
|
||||
"Ciudad y País de Nacimiento", "Dirección de Residencia", "Departamento y Provincia de Residencia",
|
||||
"Estado Civil", "Número de Hijos", "CI", "Expedido en", "Teléfono", "E-mail Personal",
|
||||
"Teléfono Laboral",
|
||||
"E-mail Laboral", "Nombres y Apellidos de Contacto", "Dirección de Contacto",
|
||||
"Teléfono de Contacto",
|
||||
"Email de Contacto", "Código de Empleado", "Cargo", "Equipo", "Lead/Manager", "Fecha de Ingreso",
|
||||
"Fecha de Retiro", "Tipo de Contrato", "Tipo de Contrato Personalizado", "Antigüedad",
|
||||
"Salario Total",
|
||||
"Salario Básico", "Bono de Antigüedad", "Bono Profesional", "Banco", "Número de Cuenta",
|
||||
"Código Único de Asegurado (GPSS)", "Matrícula de Asegurado (SSS)", "Derechohabiente 1",
|
||||
"Derechohabiente 2"
|
||||
};
|
||||
CellStyle headerStyle = workbook.createCellStyle();
|
||||
Font headerFont = workbook.createFont();
|
||||
headerFont.setBold(true);
|
||||
headerStyle.setFont(headerFont);
|
||||
headerStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = header.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
cell.setCellStyle(headerStyle);
|
||||
}
|
||||
Row dataRow = sheet.createRow(3); // Fila 3
|
||||
String[] employeeData = {
|
||||
employee.getUsername(), employee.getFirstName(), employee.getLastName(),
|
||||
employee.getStatus().toString(),
|
||||
employee.getGender().toString(), employee.getBirthday().toString(),
|
||||
String.valueOf(employee.getAge()),
|
||||
employee.getBirthCity(), employee.getResidenceAddress(), employee.getLocalAddress(),
|
||||
employee.getMaritalStatus().toString(), String.valueOf(employee.getNumberOfChildren()),
|
||||
employee.getCi(),
|
||||
employee.getIssuedIn(), employee.getPhoneNumber(), employee.getPersonalEmail(),
|
||||
employee.getPhoneNumberProfessional(), employee.getProfessionalEmail(),
|
||||
employee.getEmergencyCName(),
|
||||
employee.getEmergencyCAddress(), employee.getEmergencyCPhone(), employee.getEmergencyCEmail(),
|
||||
employee.getCod(), employee.getPosition(), employee.getTeam().getName(), employee.getLeadManager(),
|
||||
employee.getDateOfEntry().toString(), employee.getDateOfExit() != null ? employee.getDateOfExit()
|
||||
.toString() : "",
|
||||
employee.getContractType().toString(), employee.getCustomContractType(),
|
||||
employee.getSeniority(), employee.getSalaryTotal().toString(), employee.getSalaryBasic().toString(),
|
||||
employee.getTenureBonus().toString(), employee.getProfessionalBonus().toString(),
|
||||
employee.getBankName(), employee.getAccountNumber(), employee.getGpss(), employee.getSss(),
|
||||
employee.getBeneficiarie1(), employee.getBeneficiarie2()
|
||||
};
|
||||
for (int i = 0; i < employeeData.length; i++) {
|
||||
dataRow.createCell(i).setCellValue(employeeData[i] != null ? employeeData[i] : "");
|
||||
}
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
workbook.write(outputStream);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error al generar el reporte Excel", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ public class TimeOffRequestService {
|
||||
}
|
||||
|
||||
public List<TimeOffRequest> findAllTimeOffRequests() {
|
||||
return timeOffRequestRepository.findByOrderByUpdatedDesc();
|
||||
return timeOffRequestRepository.findAll();
|
||||
}
|
||||
|
||||
public TimeOffRequest findTimeOffRequest(final UUID id) {
|
||||
|
@ -1,39 +0,0 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.TimeOffRequestType;
|
||||
import com.primefactorsolutions.model.TimeOff;
|
||||
import com.primefactorsolutions.repositories.TimeOffRepository;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class TimeOffService {
|
||||
private final TimeOffRepository timeOffRepository;
|
||||
|
||||
public TimeOff getTimeOff(final UUID id) {
|
||||
return timeOffRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public void saveTimeOff(final TimeOff timeOff) {
|
||||
timeOffRepository.save(timeOff);
|
||||
}
|
||||
|
||||
public TimeOff findVacationByCategory(final TimeOffRequestType category) {
|
||||
return timeOffRepository.findByCategory(category);
|
||||
}
|
||||
|
||||
public List<TimeOff> findVacations() {
|
||||
return timeOffRepository.findAll();
|
||||
}
|
||||
|
||||
public List<TimeOff> findTimeOffs(final Integer year) {
|
||||
final LocalDate from = LocalDate.of(year, 1, 1);
|
||||
final LocalDate to = LocalDate.of(year, 12, 31);
|
||||
return timeOffRepository.findByDateBetween(from, to);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import com.primefactorsolutions.repositories.TimesheetEntryRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class TimesheetService {
|
||||
private final TimesheetEntryRepository timesheetEntryRepository;
|
||||
|
||||
@Autowired
|
||||
public TimesheetService(final TimesheetEntryRepository timesheetEntryRepository) {
|
||||
this.timesheetEntryRepository = timesheetEntryRepository;
|
||||
}
|
||||
|
||||
public List<TimesheetEntry> findAll() {
|
||||
return timesheetEntryRepository.findAll();
|
||||
}
|
||||
|
||||
public TimesheetEntry save(final TimesheetEntry timesheetEntry) {
|
||||
return timesheetEntryRepository.save(timesheetEntry);
|
||||
}
|
||||
|
||||
public TimesheetEntry getTimesheetEntry(final UUID id) {
|
||||
final Optional<TimesheetEntry> hoursWorked = timesheetEntryRepository.findById(id);
|
||||
return hoursWorked.orElse(null);
|
||||
}
|
||||
|
||||
public List<TimesheetEntry> findListHoursWorkedEmployee(final LocalDate from,
|
||||
final LocalDate to) {
|
||||
return timesheetEntryRepository.findByDateBetween(from, to);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.TimeOffRequestType;
|
||||
import com.primefactorsolutions.model.Vacation;
|
||||
import com.primefactorsolutions.repositories.VacationRepository;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class VacationService {
|
||||
private final VacationRepository vacationRepository;
|
||||
|
||||
public Vacation findVacationByCategory(final TimeOffRequestType category) {
|
||||
return vacationRepository.findByCategory(category);
|
||||
}
|
||||
|
||||
public List<Vacation> findVacations() {
|
||||
return vacationRepository.findAll();
|
||||
}
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Candidate;
|
||||
import com.primefactorsolutions.model.Exam;
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import com.primefactorsolutions.model.Question;
|
||||
import com.primefactorsolutions.service.ExamService;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.primefactorsolutions.service.QuestionService;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ItemLabelGenerator;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
@ -17,34 +15,32 @@ import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.fields.SubListSelector;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Exams")
|
||||
@Route(value = "/exams", layout = MainLayout.class)
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/assessments", layout = MainLayout.class)
|
||||
@Uses(ComboBox.class)
|
||||
public class ExamView extends BaseEntityForm<Exam> implements HasUrlParameter<String> {
|
||||
private final ExamService examService;
|
||||
public class AssessmentView extends BeanValidationForm<Assessment> implements HasUrlParameter<String> {
|
||||
private final AssessmentService assessmentService;
|
||||
|
||||
private final ComboBox<Candidate> candidate;
|
||||
private final SubListSelector<Question> questions;
|
||||
private ComboBox<Candidate> candidate = null;
|
||||
private SubListSelector<Question> questions = null;
|
||||
|
||||
public ExamView(final AuthenticationContext authorizationContext,
|
||||
final ExamService examService,
|
||||
final QuestionService questionService,
|
||||
final CandidateService candidateService) {
|
||||
super(authorizationContext, Exam.class);
|
||||
public AssessmentView(final AssessmentService assessmentService, final QuestionService questionService,
|
||||
final CandidateService candidateService) {
|
||||
super(Assessment.class);
|
||||
|
||||
this.examService = examService;
|
||||
this.assessmentService = assessmentService;
|
||||
|
||||
candidate = new ComboBox<>("Candidate", candidateService.getCandidates());
|
||||
candidate.setItemLabelGenerator((ItemLabelGenerator<Candidate>) Candidate::getEmail);
|
||||
@ -55,20 +51,20 @@ public class ExamView extends BaseEntityForm<Exam> implements HasUrlParameter<St
|
||||
questions.setReadOnly(false);
|
||||
questions.setAvailableOptions(questionService.getQuestions());
|
||||
|
||||
setSavedHandler((SavedHandler<Exam>) exam -> {
|
||||
this.examService.saveExam(exam);
|
||||
goTo(ExamsListView.class);
|
||||
setSavedHandler((SavedHandler<Assessment>) assessment -> {
|
||||
final var saved = this.assessmentService.saveAssessment(assessment);
|
||||
setEntityWithEnabledSave(saved);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
|
||||
final var exam = examService.getExam(UUID.fromString(s));
|
||||
final var assessment = assessmentService.getAssessment(UUID.fromString(s));
|
||||
|
||||
setEntityWithEnabledSave(exam);
|
||||
setEntityWithEnabledSave(assessment);
|
||||
} else {
|
||||
setEntityWithEnabledSave(new Exam());
|
||||
setEntityWithEnabledSave(new Assessment());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,127 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
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.confirmdialog.ConfirmDialog;
|
||||
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.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.DataProvider;
|
||||
import com.vaadin.flow.data.provider.DataProviderListener;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.addon.stefan.clipboard.ClientsideClipboard;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/assessments", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class AssessmentsListView extends Main {
|
||||
|
||||
public AssessmentsListView(final AssessmentService assessmentService) {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addAssessment = new Button("Add Assessment");
|
||||
addAssessment.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.getUI().get().navigate(AssessmentView.class, "new");
|
||||
});
|
||||
hl.add(addAssessment);
|
||||
|
||||
final VGrid<Assessment> grid = new VGrid<>(Assessment.class);
|
||||
grid.setColumns("id", "candidate.email");
|
||||
final Grid.Column<Assessment> statusColumn = grid.addColumn((ValueProvider<Assessment, Object>) assessment ->
|
||||
assessment.getAssessmentEvents().isEmpty()
|
||||
? "N/A"
|
||||
: assessment.getAssessmentEvents().getLast().getStatus().name());
|
||||
statusColumn.setHeader("Status");
|
||||
|
||||
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> {
|
||||
var result = new Button("Result", event ->
|
||||
this.getUI().get().navigate(SubmissionView.class, assessment.getId().toString())
|
||||
);
|
||||
result.setEnabled(assessment.isCompleted());
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> new Button("Copy Link", event ->
|
||||
ClientsideClipboard.writeToClipboard(
|
||||
String.format("email: %s link: https://careers.primefactorsolutions.com/evaluation/%s",
|
||||
assessment.getCandidate().getEmail(),
|
||||
assessment.getId()))
|
||||
));
|
||||
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment ->
|
||||
new Button("Send Email", event -> {
|
||||
ConfirmDialog dialog = new ConfirmDialog();
|
||||
dialog.setHeader("Send Link Email");
|
||||
dialog.setText(String.format("Enviar link por email al candidato %s?",
|
||||
assessment.getCandidate().getEmail()));
|
||||
dialog.setCancelable(true);
|
||||
dialog.setConfirmText("Enviar");
|
||||
dialog.setConfirmButtonTheme("primary");
|
||||
dialog.addConfirmListener((ComponentEventListener<ConfirmDialog.ConfirmEvent>) confirmEvent -> {
|
||||
try {
|
||||
assessmentService.sendEmail(assessment);
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error sending email: " + e.getMessage(), 10_000,
|
||||
Notification.Position.TOP_CENTER);
|
||||
}
|
||||
});
|
||||
dialog.open();
|
||||
}));
|
||||
|
||||
grid.setDataProvider(new DataProvider<>() {
|
||||
@Override
|
||||
public boolean isInMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(final Query<Assessment, Object> query) {
|
||||
return assessmentService.getAssessments().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Assessment> fetch(final Query<Assessment, Object> query) {
|
||||
int limit = query.getLimit();
|
||||
int pagerSize = query.getPageSize();
|
||||
int page = query.getPage();
|
||||
|
||||
return assessmentService.getAssessments().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshItem(final Assessment assessment) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAll() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registration addDataProviderListener(final DataProviderListener<Assessment> dataProviderListener) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
grid.setAllRowsVisible(true);
|
||||
|
||||
add(hl, grid);
|
||||
}
|
||||
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.views.util.AuthUtils;
|
||||
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.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public abstract class BaseEntityForm<T> extends BeanValidationForm<T> {
|
||||
private final AuthenticationContext authenticationContext;
|
||||
private Button editButton;
|
||||
private EditHandler<T> editHandler;
|
||||
|
||||
public BaseEntityForm(final AuthenticationContext authenticationContext, final Class<T> entityType) {
|
||||
super(entityType);
|
||||
this.authenticationContext = authenticationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HorizontalLayout getToolbar() {
|
||||
return new HorizontalLayout(getCancelButton(), getEditButton(), getSaveButton());
|
||||
}
|
||||
|
||||
public Button getEditButton() {
|
||||
if (editButton == null) {
|
||||
editButton = new Button("Edit");
|
||||
editButton.setEnabled(false);
|
||||
editButton.addClickListener(__ -> {
|
||||
if (editHandler != null) {
|
||||
editHandler.onEdit(getEntity());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return editButton;
|
||||
}
|
||||
|
||||
public void setEditHandler(final EditHandler<T> editHandler) {
|
||||
this.editHandler = editHandler;
|
||||
getEditButton().setEnabled(editHandler != null);
|
||||
}
|
||||
|
||||
protected <V extends Component> void goTo(final Class<V> view) {
|
||||
getUI().ifPresent(ui -> ui.navigate(view));
|
||||
}
|
||||
|
||||
protected Button getCancelButton() {
|
||||
final Button cancelButton = new Button("Cancel");
|
||||
cancelButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
|
||||
UI.getCurrent().getPage().getHistory().back());
|
||||
|
||||
return cancelButton;
|
||||
}
|
||||
|
||||
protected boolean isRoleAdmin() {
|
||||
return AuthUtils.isAdmin(this.authenticationContext);
|
||||
}
|
||||
|
||||
protected Optional<UUID> getEmployeeId() {
|
||||
return AuthUtils.getEmployeeId(this.authenticationContext);
|
||||
}
|
||||
|
||||
public interface EditHandler<T> extends Serializable {
|
||||
|
||||
void onEdit(T entity);
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.views.util.AuthUtils;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public abstract class BaseView extends Main {
|
||||
|
||||
private final VerticalLayout currentPageLayout;
|
||||
private final AuthenticationContext authenticationContext;
|
||||
|
||||
public BaseView(final AuthenticationContext authenticationContext) {
|
||||
this.authenticationContext = authenticationContext;
|
||||
currentPageLayout = new VerticalLayout();
|
||||
add(currentPageLayout);
|
||||
}
|
||||
|
||||
protected boolean isRoleAdmin() {
|
||||
return AuthUtils.isAdmin(this.authenticationContext);
|
||||
}
|
||||
|
||||
protected Optional<UUID> getEmployeeId() {
|
||||
return AuthUtils.getEmployeeId(this.authenticationContext);
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
public class CalendarView {
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Candidate;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.textfield.EmailField;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
@ -11,35 +9,34 @@ import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Candidates")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/candidates", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class CandidateView extends BaseEntityForm<Candidate> implements HasUrlParameter<String> {
|
||||
@PermitAll
|
||||
public class CandidateView extends BeanValidationForm<Candidate> implements HasUrlParameter<String> {
|
||||
private final CandidateService candidateService;
|
||||
|
||||
private EmailField email = null;
|
||||
|
||||
public CandidateView(final AuthenticationContext authenticationContext,
|
||||
final CandidateService candidateService) {
|
||||
super(authenticationContext, Candidate.class);
|
||||
public CandidateView(final CandidateService candidateService) {
|
||||
super(Candidate.class);
|
||||
this.candidateService = candidateService;
|
||||
email = new EmailField();
|
||||
email.setWidthFull();
|
||||
email.setLabel("Email");
|
||||
|
||||
setSavedHandler((SavedHandler<Candidate>) candidate -> {
|
||||
candidateService.createOrUpdate(candidate);
|
||||
goTo(CandidatesListView.class);
|
||||
final Candidate saved = candidateService.createOrUpdate(candidate);
|
||||
setEntityWithEnabledSave(saved);
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Candidate;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
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.html.Main;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.DataProvider;
|
||||
import com.vaadin.flow.data.provider.DataProviderListener;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Candidates")
|
||||
@Route(value = "/candidates", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class CandidatesListView extends Main {
|
||||
private final CandidateService candidateService;
|
||||
|
||||
public CandidatesListView(final CandidateService candidateService) {
|
||||
this.candidateService = candidateService;
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addCandidate = new Button("Add Candidate");
|
||||
addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.getUI().get().navigate(CandidateView.class, "new");
|
||||
});
|
||||
hl.add(addCandidate);
|
||||
|
||||
final VGrid<Candidate> grid = new VGrid<>(Candidate.class);
|
||||
grid.setColumns("id", "email");
|
||||
grid.setAllRowsVisible(true);
|
||||
grid.addComponentColumn((ValueProvider<Candidate, Component>) candidate -> {
|
||||
final Button edit = new Button("Edit");
|
||||
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
|
||||
this.getUI().get().navigate(CandidateView.class, candidate.getId().toString()));
|
||||
return edit;
|
||||
});
|
||||
|
||||
grid.setDataProvider(new DataProvider<>() {
|
||||
@Override
|
||||
public boolean isInMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(final Query<Candidate, Object> query) {
|
||||
return candidateService.getCandidates().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Candidate> fetch(final Query<Candidate, Object> query) {
|
||||
int limit = query.getLimit();
|
||||
int pagerSize = query.getPageSize();
|
||||
int page = query.getPage();
|
||||
return candidateService.getCandidates().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshItem(final Candidate candidate) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAll() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registration addDataProviderListener(final DataProviderListener<Candidate> dataProviderListener) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
add(hl, grid);
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
public class Constants {
|
||||
public static final int PAGE_SIZE = 10;
|
||||
}
|
@ -1,16 +1,13 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Document;
|
||||
import com.primefactorsolutions.model.DocumentType;
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.DocumentService;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
@ -26,6 +23,7 @@ import elemental.json.JsonObject;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -40,29 +38,28 @@ import java.io.InputStream;
|
||||
@Scope("prototype")
|
||||
@PageTitle("Document")
|
||||
@Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class)
|
||||
public class DocumentView extends BaseEntityForm<Document> implements HasUrlParameter<String> {
|
||||
private final TextField fileName = new TextField("Nombre del documento");
|
||||
private final ComboBox<DocumentType> documentType = new ComboBox<>("Tipo de documento");
|
||||
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
|
||||
private final Span pdfOnlyMessage = new Span("Únicamente se permite la carga de archivos en formato PDF.");
|
||||
public class DocumentView extends BeanValidationForm<Document> implements HasUrlParameter<String> {
|
||||
private final TextField fileName = new TextField("Document Name");
|
||||
private final ComboBox<DocumentType> documentType = new ComboBox<>("Document Type");
|
||||
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Employee");
|
||||
private final MemoryBuffer buffer = new MemoryBuffer();
|
||||
private final Upload uploadButton = new Upload(buffer);
|
||||
private final DocumentService documentService;
|
||||
private final EmployeeService employeeService;
|
||||
private final AuthenticationContext authContext;
|
||||
private boolean fileUploaded = false;
|
||||
private Button saveButton;
|
||||
private Button viewDocumentButton;
|
||||
|
||||
public DocumentView(final AuthenticationContext authenticationContext,
|
||||
final DocumentService documentService,
|
||||
|
||||
public DocumentView(final DocumentService documentService,
|
||||
final EmployeeService employeeService,
|
||||
final AuthenticationContext authContext) {
|
||||
super(authenticationContext, Document.class);
|
||||
super(Document.class);
|
||||
this.documentService = documentService;
|
||||
this.employeeService = employeeService;
|
||||
this.authContext = authContext;
|
||||
initializeView();
|
||||
this.setSavedHandler(this::saveDocument);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
@ -70,8 +67,20 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
configureUploadButton();
|
||||
}
|
||||
|
||||
protected Button createSaveButton() {
|
||||
saveButton = new Button("Save");
|
||||
saveButton.addClickListener(event -> saveDocument());
|
||||
return saveButton;
|
||||
}
|
||||
|
||||
protected Button createCloseButton() {
|
||||
Button closeButton = new Button("Close");
|
||||
closeButton.addClickListener(event -> closeForm());
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
protected Button createViewDocumentButton() {
|
||||
viewDocumentButton = new Button("Ver documento");
|
||||
viewDocumentButton = new Button("View Document");
|
||||
viewDocumentButton.setEnabled(false);
|
||||
viewDocumentButton.addClickListener(event -> viewDocument());
|
||||
return viewDocumentButton;
|
||||
@ -121,12 +130,17 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
});
|
||||
} catch (IOException e) {
|
||||
Notification.show("Error al leer el archivo.");
|
||||
Notification.show("Error reading file.");
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDocument(final Document document) {
|
||||
private void navigateToDocumentsListView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(DocumentsListView.class));
|
||||
}
|
||||
|
||||
private void saveDocument() {
|
||||
if (isFormValid()) {
|
||||
Document document = getEntity();
|
||||
document.setFileName(fileName.getValue());
|
||||
document.setDocumentType(documentType.getValue());
|
||||
document.setEmployee(employeeComboBox.getValue());
|
||||
@ -134,12 +148,17 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
setDocumentCreator(document);
|
||||
|
||||
documentService.saveDocument(document);
|
||||
getUI().ifPresent(ui -> ui.navigate(DocumentsListView.class));
|
||||
Notification.show("File saved successfully.");
|
||||
clearForm();
|
||||
} else {
|
||||
Notification.show("Error al guardar: Por favor, complete todos los campos y cargue un archivo.");
|
||||
Notification.show("Save failed: Please complete all fields and upload a file.");
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
navigateToDocumentsListView();
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return !fileName.isEmpty()
|
||||
&& documentType.getValue() != null
|
||||
@ -147,11 +166,20 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
&& fileUploaded;
|
||||
}
|
||||
|
||||
private void clearForm() {
|
||||
fileName.clear();
|
||||
documentType.clear();
|
||||
employeeComboBox.clear();
|
||||
fileUploaded = false;
|
||||
uploadButton.getElement().setPropertyJson("files", Json.createArray());
|
||||
viewDocumentButton.setEnabled(false);
|
||||
}
|
||||
|
||||
private byte[] readFileData() {
|
||||
try {
|
||||
return buffer.getInputStream().readAllBytes();
|
||||
} catch (IOException e) {
|
||||
Notification.show("Error al leer los datos del archivo.");
|
||||
Notification.show("Error reading file data.");
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
@ -168,10 +196,23 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
fileUploaded = true;
|
||||
}
|
||||
|
||||
private void updateSaveButtonState() {
|
||||
boolean isModified = !fileName.getValue().equals(getEntity().getFileName())
|
||||
|| documentType.getValue() != getEntity().getDocumentType()
|
||||
|| employeeComboBox.getValue() != getEntity().getEmployee()
|
||||
|| fileUploaded;
|
||||
saveButton.setEnabled(isModified);
|
||||
}
|
||||
|
||||
private void configureComponents() {
|
||||
setFileNameProperties();
|
||||
setDocumentTypeProperties();
|
||||
setEmployeeComboBoxProperties();
|
||||
fileName.addValueChangeListener(e -> updateSaveButtonState());
|
||||
documentType.addValueChangeListener(e -> updateSaveButtonState());
|
||||
employeeComboBox.addValueChangeListener(e -> updateSaveButtonState());
|
||||
uploadButton.addSucceededListener(e -> updateSaveButtonState());
|
||||
uploadButton.getElement().addEventListener("file-remove", event -> updateSaveButtonState());
|
||||
}
|
||||
|
||||
private void configureUploadButton() {
|
||||
@ -179,16 +220,31 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
uploadButton.setAcceptedFileTypes(".pdf");
|
||||
uploadButton.addSucceededListener(event -> {
|
||||
fileUploaded = true;
|
||||
Notification.show("Archivo cargado correctamente.");
|
||||
Notification.show("File uploaded successfully.");
|
||||
viewDocumentButton.setEnabled(true);
|
||||
updateSaveButtonState();
|
||||
});
|
||||
uploadButton.getElement().addEventListener("file-remove", event -> {
|
||||
fileUploaded = false;
|
||||
Notification.show("Archivo eliminado.");
|
||||
Notification.show("File removed.");
|
||||
viewDocumentButton.setEnabled(false);
|
||||
updateSaveButtonState();
|
||||
});
|
||||
}
|
||||
|
||||
private void configureViewOrEditAction(final String action, final String documentIdString) {
|
||||
if ("edit".equals(action) && !documentIdString.isEmpty()) {
|
||||
setFieldsReadOnly(false);
|
||||
preLoadFile(getEntity());
|
||||
viewDocumentButton.setEnabled(true);
|
||||
} else if ("view".equals(action) && !documentIdString.isEmpty()) {
|
||||
setFieldsReadOnly(true);
|
||||
preLoadFile(getEntity());
|
||||
saveButton.setEnabled(false);
|
||||
viewDocumentButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
@ -200,30 +256,18 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
assert documentIdString != null;
|
||||
UUID documentId = UUID.fromString(documentIdString);
|
||||
Document document = documentService.getDocument(documentId);
|
||||
|
||||
setEntity(document);
|
||||
employeeComboBox.setValue(document.getEmployee());
|
||||
preLoadFile(document);
|
||||
|
||||
if ("edit".equals(action) && !documentIdString.isEmpty()) {
|
||||
setEntityWithEnabledSave(document);
|
||||
setFieldsReadOnly(false);
|
||||
preLoadFile(document);
|
||||
viewDocumentButton.setEnabled(true);
|
||||
} else if ("view".equals(action) && !documentIdString.isEmpty()) {
|
||||
setEntity(document);
|
||||
setFieldsReadOnly(true);
|
||||
preLoadFile(document);
|
||||
viewDocumentButton.setEnabled(true);
|
||||
}
|
||||
configureViewOrEditAction(action, documentIdString);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
final HorizontalLayout buttonLayout = new HorizontalLayout();
|
||||
HorizontalLayout buttonLayout = new HorizontalLayout();
|
||||
buttonLayout.add(uploadButton, createViewDocumentButton());
|
||||
buttonLayout.setSpacing(true);
|
||||
|
||||
return List.of(documentType, fileName, employeeComboBox, pdfOnlyMessage, buttonLayout);
|
||||
return List.of(fileName, documentType, employeeComboBox, buttonLayout, createCloseButton());
|
||||
}
|
||||
}
|
@ -1,36 +1,22 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.primefactorsolutions.model.Document;
|
||||
import com.primefactorsolutions.model.DocumentType;
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.DocumentService;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
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.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
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.html.Main;
|
||||
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.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
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.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
@ -38,14 +24,13 @@ import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.List;
|
||||
|
||||
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Documents")
|
||||
@Route(value = "/documents", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class DocumentsListView extends BaseView {
|
||||
public class DocumentsListView extends Main {
|
||||
|
||||
private final DocumentService documentService;
|
||||
private final EmployeeService employeeService;
|
||||
@ -53,10 +38,7 @@ public class DocumentsListView extends BaseView {
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<DocumentType> documentTypeFilter;
|
||||
|
||||
public DocumentsListView(final AuthenticationContext authenticationContext,
|
||||
final DocumentService documentService,
|
||||
final EmployeeService employeeService) {
|
||||
super(authenticationContext);
|
||||
public DocumentsListView(final DocumentService documentService, final EmployeeService employeeService) {
|
||||
this.documentService = documentService;
|
||||
this.employeeService = employeeService;
|
||||
initializeView();
|
||||
@ -64,24 +46,16 @@ public class DocumentsListView extends BaseView {
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
getCurrentPageLayout().add(createActionButton("Añadir documento", this::navigateToAddDocumentView, true));
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createDocumentTypeFilter());
|
||||
hl.add(createEmployeeFilter());
|
||||
|
||||
getCurrentPageLayout().add(hl);
|
||||
|
||||
configureDocumentGrid();
|
||||
getCurrentPageLayout().add(documentGrid);
|
||||
add(createActionButton("Add Document", this::navigateToAddDocumentView));
|
||||
add(createDocumentTypeFilter());
|
||||
add(createEmployeeFilter());
|
||||
add(documentGrid);
|
||||
}
|
||||
|
||||
private void configureDocumentGrid() {
|
||||
documentGrid.setColumns("fileName", "documentType", "creator");
|
||||
documentGrid.getColumnByKey("fileName").setHeader("Nombre archivo");
|
||||
documentGrid.getColumnByKey("documentType").setHeader("Tipo");
|
||||
documentGrid.getColumnByKey("creator").setHeader("Creador");
|
||||
documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Empleado");
|
||||
documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Employee");
|
||||
addActionColumns();
|
||||
configurePagination();
|
||||
}
|
||||
@ -93,38 +67,25 @@ public class DocumentsListView extends BaseView {
|
||||
}
|
||||
|
||||
private void addActionColumns() {
|
||||
documentGrid.addComponentColumn((ValueProvider<Document, Component>) document -> {
|
||||
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 ->
|
||||
navigateToDocumentView(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;
|
||||
});
|
||||
addDocumentActionColumn("View", this::navigateToDocumentView);
|
||||
addDocumentActionColumn("Edit", this::navigateToEditDocumentView);
|
||||
addDocumentActionColumn("Download", this::downloadDocument);
|
||||
}
|
||||
|
||||
private Button createActionButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
|
||||
private void addDocumentActionColumn(final String label, final DocumentActionHandler handler) {
|
||||
documentGrid.addComponentColumn(document -> createActionButton(label, () -> handler.handle(document)));
|
||||
}
|
||||
|
||||
private Button createActionButton(final String label, final Runnable onClickAction) {
|
||||
Button actionButton = new Button(label);
|
||||
|
||||
if (isPrimary) {
|
||||
actionButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
}
|
||||
|
||||
actionButton.addClickListener(event -> onClickAction.run());
|
||||
return actionButton;
|
||||
}
|
||||
|
||||
private ComboBox<DocumentType> createDocumentTypeFilter() {
|
||||
documentTypeFilter = new ComboBox<>("Tipo de documento");
|
||||
documentTypeFilter.setClearButtonVisible(true);
|
||||
documentTypeFilter.setPlaceholder("Seleccionar ...");
|
||||
documentTypeFilter = new ComboBox<>("Document Type");
|
||||
documentTypeFilter.setItems(DocumentType.values());
|
||||
documentTypeFilter.setValue(DocumentType.values()[0]);
|
||||
documentTypeFilter.addValueChangeListener(event -> {
|
||||
updateDocumentGrid(event.getValue(), employeeFilter.getValue());
|
||||
});
|
||||
@ -132,32 +93,26 @@ public class DocumentsListView extends BaseView {
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
|
||||
final List<Employee> employees;
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
employees = employeeService.findAllEmployees();
|
||||
employeeFilter.setItems(employees);
|
||||
} else {
|
||||
Employee employee = employeeService.getEmployee(getEmployeeId().get());
|
||||
employees = Lists.newArrayList(employee);
|
||||
employeeFilter.setItems(employees);
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
employeeFilter.setReadOnly(true);
|
||||
}
|
||||
|
||||
employeeFilter = new ComboBox<>("Employee");
|
||||
List<Employee> employees = employeeService.findAllEmployees();
|
||||
employees.addFirst(createAllEmployeesOption());
|
||||
employeeFilter.setItems(employees);
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeLabel);
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
employeeFilter.addValueChangeListener(event -> {
|
||||
updateDocumentGrid(documentTypeFilter.getValue(), event.getValue());
|
||||
});
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private Employee createAllEmployeesOption() {
|
||||
Employee allEmployeesOption = new Employee();
|
||||
allEmployeesOption.setFirstName("All");
|
||||
return allEmployeesOption;
|
||||
}
|
||||
|
||||
private String getEmployeeLabel(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
return employee.getFirstName().equals("All") ? "All" : employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private void navigateToEditDocumentView(final Document document) {
|
||||
@ -178,7 +133,7 @@ public class DocumentsListView extends BaseView {
|
||||
|
||||
private void configurePagination() {
|
||||
documentGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
documentGrid.setPageSize(PAGE_SIZE);
|
||||
documentGrid.setPageSize(5);
|
||||
}
|
||||
|
||||
private void updateDocumentGrid(final DocumentType documentType, final Employee employee) {
|
||||
@ -242,4 +197,9 @@ public class DocumentsListView extends BaseView {
|
||||
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface DocumentActionHandler {
|
||||
void handle(Document document);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
@ -6,8 +6,7 @@ import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.ReportService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.vaadin.componentfactory.pdfviewer.PdfViewer;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
@ -16,14 +15,13 @@ import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.dialog.Dialog;
|
||||
import com.vaadin.flow.component.html.*;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.html.Image;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.tabs.TabSheet;
|
||||
import com.vaadin.flow.component.textfield.BigDecimalField;
|
||||
import com.vaadin.flow.component.textfield.EmailField;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.component.upload.Upload;
|
||||
@ -32,17 +30,14 @@ import com.vaadin.flow.data.value.ValueChangeMode;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.datepicker.VDatePicker;
|
||||
import org.vaadin.firitin.fields.ElementCollectionField;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Base64;
|
||||
@ -54,148 +49,138 @@ import java.util.UUID;
|
||||
@Scope("prototype")
|
||||
@PageTitle("Employee")
|
||||
@Route(value = "/employees/:employeeId?/:action?", layout = MainLayout.class)
|
||||
@Slf4j
|
||||
public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlParameter<String> {
|
||||
public class EmployeeView extends BeanValidationForm<Employee> implements HasUrlParameter<String> {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
private final ReportService reportService;
|
||||
private final TimeOffRequestService requestService;
|
||||
private final TeamService teamService;
|
||||
private final TabSheet tabSheet = new TabSheet();
|
||||
|
||||
// TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente.
|
||||
private final TextField username = createTextField("Username", 30, true);
|
||||
private final TextField firstName = createTextField("Nombres", 30, true);
|
||||
private final TextField username = createTextField("Username: ", 30, true);
|
||||
private final TextField firstName = createTextField("Nombres: ", 30, true);
|
||||
private final TextField lastName = createTextField("Apellidos", 30, true);
|
||||
private final ComboBox<Employee.Status> status = createStatusComboBox();
|
||||
private final ComboBox<Employee.Gender> gender = createGenderComboBox();
|
||||
private final VDatePicker birthday = new VDatePicker("Fecha de Nacimiento");
|
||||
private final TextField age = createTextField("Edad", 3, false);
|
||||
private final TextField birthCity = createTextField("Ciudad y País de Nacimiento", 30, false);
|
||||
private final TextField birthCity = createTextField("Ciudad y País de Nacimiento ejemplo: (Ciudad, País) ",
|
||||
30, false);
|
||||
private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false);
|
||||
private final TextField localAddress = createTextField("Departamento y Provincia de Residencia", 30, false);
|
||||
private final TextField localAddress = createTextField("Departamento y Provincia de Residencia "
|
||||
+ " ejemplo: (Departamento-Provincia)", 30, false);
|
||||
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
|
||||
private final TextField numberOfChildren = createTextField("Numero de Hijos", 1, false);
|
||||
private final TextField numberOfChildren = createTextField("Numero de Hijos", 2, false);
|
||||
private final TextField ci = createTextField("CI", 10, false);
|
||||
private final TextField issuedIn = createTextField("Lugar de Expedicion", 10, false);
|
||||
private final TextField issuedIn = createTextField("Expedido en ", 10, false);
|
||||
private final TextField phoneNumber = createTextField("Teléfono", 8, false);
|
||||
private final EmailField personalEmail = createEmailField("E-mail");
|
||||
private final TextField phoneNumberProfessional = createTextField("Teléfono Laboral", 8, false);
|
||||
private final EmailField personalEmail = createEmailField("E-mail ejemplo: (ejemplo@gmail.com)");
|
||||
private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false);
|
||||
private final TextField emergencyCAddress = createTextField("Dirección de Contacto", 50, false);
|
||||
private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false);
|
||||
private final EmailField emergencyCEmail = createEmailField("Email de Contacto");
|
||||
private final EmailField emergencyCEmail = createEmailField("Email de Contacto ejemplo: (ejemplo@gmail.com)");
|
||||
|
||||
private final MemoryBuffer buffer = new MemoryBuffer();
|
||||
private final Upload upload = new Upload(buffer);
|
||||
private final Image profileImagePreview = new Image();
|
||||
private final ElementCollectionField<Certification> educationTitles =
|
||||
new ElementCollectionField<>(Certification.class);
|
||||
private final ElementCollectionField<Certification> certifications =
|
||||
new ElementCollectionField<>(Certification.class);
|
||||
|
||||
//INFORMACION PROFESIONAL
|
||||
private final TextField pTitle1 = createTextField("Título 1", 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 pStudy1 = createTextField("Estudio 1", 30, false);
|
||||
private final TextField pStudy2 = createTextField("Estudio 2", 30, false);
|
||||
private final TextField pStudy3 = createTextField("Estudio 3", 30, false);
|
||||
private final TextField certification1 = createTextField("Certificación 1", 30, false);
|
||||
private final TextField certification2 = createTextField("Certificación 2", 30, false);
|
||||
private final TextField certification3 = createTextField("Certificación 3", 30, false);
|
||||
private final TextField certification4 = createTextField("Certificación 4", 30, false);
|
||||
private final TextField recognition = createTextField("Reconocimientos", 30, false);
|
||||
private final TextField achievements = createTextField("Logros Profesionales", 30, false);
|
||||
private final ElementCollectionField<Language> languages = new ElementCollectionField<>(Language.class);
|
||||
private final TextField language = createTextField("Idioma", 50, false);
|
||||
private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false);
|
||||
|
||||
//INFORMACION ADMINISTRATIVA
|
||||
private final TextField cod = createTextField("Codigo de Empleado", 20, false);
|
||||
private final TextField position = createTextField("Cargo", 30, false);
|
||||
private final ComboBox<Team> team = new ComboBox<>("Equipo");
|
||||
private final TextField leadManager = createTextField("Lead/Manager", 30, false);
|
||||
private final TextField project = createTextField("Proyecto", 30, false);
|
||||
private final VDatePicker dateOfEntry = new VDatePicker("Fecha de Ingreso");
|
||||
private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro");
|
||||
private final ComboBox<Employee.ContractType> contractType = createContractTypeComboBox();
|
||||
private final TextField customContractType = createCustomContractTypeField();
|
||||
private final TextField seniority = createTextField("Antiguedad", 30, false);
|
||||
private final BigDecimalField salaryTotal = createBigDecimalField("Salario Total", false);
|
||||
private final BigDecimalField salaryBasic = createBigDecimalField("Salario Basico", false);
|
||||
private final BigDecimalField tenureBonus = createBigDecimalField("Bono de Antiguedad", false);
|
||||
private final BigDecimalField professionalBonus = createBigDecimalField("Bono Profesional", false);
|
||||
private final TextField bankName = createTextField("Banco", 30, false);
|
||||
private final TextField contractType = createTextField("Tipo de Contratación", 30, false);
|
||||
private final TextField seniority = createTextField("Antiguedad", 30, false);
|
||||
private final TextField salary = createTextField("Salario", 30, false);
|
||||
private final TextField bankName = createTextField("Banco", 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 sss = createTextField("Matricula de Asegurado (SSS)", 30, false);
|
||||
private final TextField beneficiarie1 = createTextField("Derechohabiente 1", 30, false);
|
||||
private final TextField beneficiarie2 = createTextField("Derechohabiente 2", 30, false);
|
||||
private final TextField beneficiaries = createTextField("Derechohabientes", 30, false);
|
||||
|
||||
private static final String SAVE_BUTTON_TEXT = "Save";
|
||||
private static final String EDIT_BUTTON_TEXT = "Edit";
|
||||
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 PHONE_NUMBER_ERROR_MESSAGE = "El teléfono debe contener solo números.";
|
||||
private final Button reportButton = new Button("Generar Ficha de Contratación");
|
||||
|
||||
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 reportButton = new Button("Generar Ficha");
|
||||
private final Dialog dialog = new Dialog();
|
||||
private final PdfViewer pdfViewer = new PdfViewer();
|
||||
private final Button excelReportButton = new Button("Información General del Empleado Excel",
|
||||
VaadinIcon.FILE_TABLE.create());
|
||||
|
||||
private final H3 generalInfo = new H3("Información General");
|
||||
private final H5 imagenSub = new H5("Insertar una imagen .jpg:");
|
||||
//TITULOS PARA INFORMACION PERSONAL
|
||||
private final H2 infoPer = new H2("Información Personal");
|
||||
private final H3 infoGenr = new H3("Información General");
|
||||
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 H3 titulos = new H3("Titulos Profesionales y Estudios Realizados");
|
||||
private final H3 certif = new H3("Certificaciones Profesionales");
|
||||
private final H3 logros = new H3("Otros Logros y Reconocimientos");
|
||||
private final H3 idioma = new H3("Dominio de Idiomas");
|
||||
|
||||
private final H2 titleAdminInfo = new H2("Información Administrativa");
|
||||
//TITULOS PARA INFORMACIÓN ADMINISTRATIVA
|
||||
private final H2 infoAdm = new H2("Información Administrativa");
|
||||
private final H3 infoCont = new H3("Información de Contratación");
|
||||
private final H3 datBanc = new H3("Datos Bancarios");
|
||||
private final H3 datGest = new H3("Datos Gestora Pública y Seguro Social");
|
||||
|
||||
public EmployeeView(final AuthenticationContext authenticationContext,
|
||||
final EmployeeService employeeService,
|
||||
public EmployeeView(final EmployeeService employeeService,
|
||||
final ReportService reportService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext, Employee.class);
|
||||
final TeamService teamService,
|
||||
final TimeOffRequestService requestService) {
|
||||
super(Employee.class);
|
||||
this.employeeService = employeeService;
|
||||
this.reportService = reportService;
|
||||
this.requestService = requestService;
|
||||
this.teamService = teamService;
|
||||
excelReportButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
|
||||
configureComponents();
|
||||
addClassName("main-layout");
|
||||
excelReportButton.addClickListener(e ->
|
||||
getUI().ifPresent(ui ->
|
||||
ui.navigate(EmployeeReportView.class, getEntity().getId().toString())
|
||||
)
|
||||
);
|
||||
setSavedHandler(this::saveEmployee);
|
||||
}
|
||||
|
||||
private void makeUpperCase(final TextField textField) {
|
||||
textField.addValueChangeListener(event -> {
|
||||
String value = event.getValue();
|
||||
if (value != null) {
|
||||
textField.setValue(value.toUpperCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void configureComponents() {
|
||||
tabSheet.setWidthFull();
|
||||
phoneNumber.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
phoneNumber.addValueChangeListener(e -> validatePhoneNumber(phoneNumber, e.getValue()));
|
||||
emergencyCPhone.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
emergencyCPhone.addValueChangeListener(e -> validatePhoneNumber(emergencyCPhone, e.getValue()));
|
||||
|
||||
firstName.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
firstName.addValueChangeListener(e -> validateNameField(firstName, e.getValue()));
|
||||
lastName.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
lastName.addValueChangeListener(e -> validateNameField(lastName, e.getValue()));
|
||||
createTeamComboBox();
|
||||
|
||||
configureUpload();
|
||||
saveButton.setVisible(true);
|
||||
editButton.setVisible(true);
|
||||
reportButton.setVisible(true);
|
||||
birthday.addValueChangeListener(event -> calculateAge());
|
||||
birthday.setMin(LocalDate.now().minusYears(100));
|
||||
birthday.setMax(LocalDate.now().minusYears(18));
|
||||
birthday.addValueChangeListener(event -> {
|
||||
LocalDate selectedDate = event.getValue();
|
||||
if (selectedDate != null && ChronoUnit.YEARS.between(selectedDate, LocalDate.now()) < 18) {
|
||||
birthday.setInvalid(true);
|
||||
birthday.setErrorMessage("El empleado debe ser mayor o tener 18 años");
|
||||
} else {
|
||||
birthday.setInvalid(false);
|
||||
}
|
||||
});
|
||||
salaryTotal.addValueChangeListener(event -> calculateSalaryTotal());
|
||||
birthday.setMax(java.time.LocalDate.now());
|
||||
dateOfEntry.addValueChangeListener(event -> calculateSeniority());
|
||||
dateOfExit.addValueChangeListener(event -> {
|
||||
if (event.getValue() != null) {
|
||||
status.setValue(Employee.Status.INACTIVE);
|
||||
} else {
|
||||
status.setValue(Employee.Status.ACTIVE);
|
||||
}
|
||||
});
|
||||
dateOfEntry.addValueChangeListener(event -> calculateSeniority());
|
||||
|
||||
reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
var employee = getEntity();
|
||||
byte[] pdfContent = reportService.writeAsPdf("ficha", employee);
|
||||
@ -204,27 +189,6 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
dialog.open();
|
||||
});
|
||||
|
||||
makeUpperCase(firstName);
|
||||
makeUpperCase(lastName);
|
||||
makeUpperCase(birthCity);
|
||||
makeUpperCase(residenceAddress);
|
||||
makeUpperCase(localAddress);
|
||||
makeUpperCase(position);
|
||||
makeUpperCase(emergencyCName);
|
||||
makeUpperCase(emergencyCAddress);
|
||||
makeUpperCase(ci);
|
||||
makeUpperCase(issuedIn);
|
||||
makeUpperCase(recognition);
|
||||
makeUpperCase(achievements);
|
||||
makeUpperCase(cod);
|
||||
makeUpperCase(leadManager);
|
||||
makeUpperCase(seniority);
|
||||
makeUpperCase(bankName);
|
||||
makeUpperCase(accountNumber);
|
||||
makeUpperCase(gpss);
|
||||
makeUpperCase(sss);
|
||||
makeUpperCase(beneficiarie1);
|
||||
makeUpperCase(beneficiarie2);
|
||||
initDialog();
|
||||
}
|
||||
|
||||
@ -243,13 +207,21 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
int birthYear = birthday.getValue().getYear();
|
||||
int ages = currentYear - birthYear;
|
||||
age.setValue(String.valueOf(ages));
|
||||
birthday.setInvalid(ages < 18);
|
||||
if (ages < 18) {
|
||||
birthday.setInvalid(true);
|
||||
birthday.setErrorMessage("La edad no puede ser menor a 18 años.");
|
||||
Notification.show("La edad ingresada no es válida, debe ser mayor o igual a 18 años.");
|
||||
} else {
|
||||
birthday.setInvalid(false);
|
||||
}
|
||||
System.out.println(age);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateSeniority() {
|
||||
LocalDate entryDate = dateOfEntry.getValue();
|
||||
LocalDate exitDate = dateOfExit.getValue() != null ? dateOfExit.getValue() : LocalDate.now();
|
||||
|
||||
if (entryDate != null) {
|
||||
long yearsOfService = ChronoUnit.YEARS.between(entryDate, exitDate);
|
||||
String seniorityValue = yearsOfService + " años ";
|
||||
@ -259,44 +231,6 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateSalaryTotal() {
|
||||
if (contractType.getValue() == Employee.ContractType.CONTRATO_LABORAL) {
|
||||
salaryBasic.setVisible(true);
|
||||
professionalBonus.setVisible(true);
|
||||
tenureBonus.setVisible(true);
|
||||
salaryTotal.setVisible(true);
|
||||
salaryBasic.addValueChangeListener(event -> updateTotalSalary());
|
||||
professionalBonus.addValueChangeListener(event -> updateTotalSalary());
|
||||
tenureBonus.addValueChangeListener(event -> updateTotalSalary());
|
||||
} else {
|
||||
salaryBasic.setVisible(false);
|
||||
professionalBonus.setVisible(false);
|
||||
tenureBonus.setVisible(false);
|
||||
salaryTotal.setVisible(true);
|
||||
}
|
||||
salaryTotal.getValue();
|
||||
}
|
||||
|
||||
private void updateTotalSalary() {
|
||||
try {
|
||||
final BigDecimal basic = salaryBasic.getValue();
|
||||
final BigDecimal bonus = professionalBonus.getValue();
|
||||
final BigDecimal seniorityBonus = tenureBonus.getValue();
|
||||
final BigDecimal totalSalary = basic.add(bonus).add(seniorityBonus);
|
||||
salaryTotal.setValue(totalSalary);
|
||||
} catch (Exception e) {
|
||||
salaryTotal.setValue(BigDecimal.valueOf(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() {
|
||||
upload.setAcceptedFileTypes("image/jpeg", "image/png");
|
||||
upload.setMaxFileSize(1024 * 1024);
|
||||
@ -305,16 +239,18 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
buffer.getInputStream().transferTo(outputStream);
|
||||
byte[] imageBytes = outputStream.toByteArray();
|
||||
String base64Image = Base64.getEncoder().encodeToString(imageBytes);
|
||||
|
||||
getEntity().setProfileImage(base64Image);
|
||||
|
||||
profileImagePreview.setSrc("data:image/png;base64," + base64Image);
|
||||
profileImagePreview.setMaxWidth("150px");
|
||||
profileImagePreview.setMaxHeight("150px");
|
||||
} catch (IOException e) {
|
||||
Notification.show("Error al subir la imagen: " + e.getMessage());
|
||||
log.error("Error uploading image", e);
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error en el servidor al procesar la imagen.");
|
||||
log.error("Error uploading image", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -330,9 +266,11 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
H2 headline = new H2("Ficha Empleado");
|
||||
headline.getStyle().set("margin", "var(--lumo-space-m) 0 0 0")
|
||||
.set("font-size", "1.5em").set("font-weight", "bold");
|
||||
|
||||
final Button cancelDialogButton = new Button("Close", e -> dialog.close());
|
||||
final HorizontalLayout buttonLayout = new HorizontalLayout(cancelDialogButton);
|
||||
buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
|
||||
|
||||
final VerticalLayout dialogLayout = new VerticalLayout(headline, pdfViewer, buttonLayout);
|
||||
dialogLayout.getStyle().set("height", "100%");
|
||||
dialogLayout.getStyle().set("overflow", "hidden");
|
||||
@ -342,6 +280,7 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
dialogLayout.setAlignItems(FlexComponent.Alignment.STRETCH);
|
||||
dialogLayout.getStyle().set("width", "700px").set("max-width", "100%");
|
||||
dialogLayout.getStyle().set("height", "800px").set("max-height", "100%");
|
||||
|
||||
dialog.add(dialogLayout);
|
||||
}
|
||||
|
||||
@ -360,35 +299,6 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
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.addValueChangeListener(event -> handleContractTypeChange(event.getValue()));
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
private TextField createCustomContractTypeField() {
|
||||
TextField textField = new TextField("Especificar Tipo de Contrato");
|
||||
textField.setPlaceholder("Ingrese el tipo de contrato...");
|
||||
textField.setVisible(false);
|
||||
textField.setWidth("300px");
|
||||
return textField;
|
||||
}
|
||||
|
||||
private void handleContractTypeChange(final Employee.ContractType selectedType) {
|
||||
if (selectedType == Employee.ContractType.OTROS) {
|
||||
customContractType.setVisible(true);
|
||||
customContractType.setRequired(true);
|
||||
} else {
|
||||
customContractType.setVisible(false);
|
||||
customContractType.clear();
|
||||
customContractType.setRequired(false);
|
||||
}
|
||||
}
|
||||
|
||||
private VerticalLayout createContentLayout() {
|
||||
VerticalLayout contentLayout = new VerticalLayout();
|
||||
contentLayout.setWidth("100%");
|
||||
@ -403,17 +313,10 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
return textField;
|
||||
}
|
||||
|
||||
private BigDecimalField createBigDecimalField(final String label, final boolean required) {
|
||||
BigDecimalField textField = new BigDecimalField(label);
|
||||
textField.setWidthFull();
|
||||
textField.setRequired(required);
|
||||
return textField;
|
||||
}
|
||||
|
||||
private EmailField createEmailField(final String label) {
|
||||
EmailField emailField = new EmailField(label);
|
||||
emailField.setWidthFull();
|
||||
emailField.setMaxLength(50);
|
||||
emailField.setMaxLength(30);
|
||||
return emailField;
|
||||
}
|
||||
|
||||
@ -424,6 +327,14 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
team.setWidthFull();
|
||||
}
|
||||
|
||||
private <T> ComboBox<T> createComboBox(final String label, final T[] items) {
|
||||
ComboBox<T> comboBox = new ComboBox<>(label);
|
||||
comboBox.setItems(items);
|
||||
comboBox.setItemLabelGenerator(Object::toString);
|
||||
comboBox.setWidthFull();
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
private ComboBox<Employee.Gender> createGenderComboBox() {
|
||||
ComboBox<Employee.Gender> comboBox = new ComboBox<>("Genero");
|
||||
comboBox.setItems(Employee.Gender.values());
|
||||
@ -456,20 +367,12 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
}
|
||||
}
|
||||
|
||||
private void saveEmployee(final Employee employee) {
|
||||
private void saveEmployee() {
|
||||
if (validateForm()) {
|
||||
Employee employee = getEntity();
|
||||
employee.setStatus(status.getValue());
|
||||
employee.setAge(age.getValue());
|
||||
employee.setSalaryBasic(salaryBasic.getValue());
|
||||
employee.setProfessionalBonus(professionalBonus.getValue());
|
||||
employee.setTenureBonus(tenureBonus.getValue());
|
||||
employee.setSalaryTotal((salaryTotal.getValue()));
|
||||
employee.setContractType(contractType.getValue());
|
||||
if (contractType.getValue() == Employee.ContractType.OTROS) {
|
||||
employee.setCustomContractType(customContractType.getValue());
|
||||
} else {
|
||||
employee.setCustomContractType(null);
|
||||
}
|
||||
|
||||
employeeService.createOrUpdate(employee);
|
||||
Notification.show(NOTIFICATION_SAVE_SUCCESS);
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeesListView.class));
|
||||
@ -480,6 +383,8 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
|
||||
private void enableEditMode() {
|
||||
setFieldsEditable();
|
||||
saveButton.setVisible(true);
|
||||
editButton.setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -489,35 +394,25 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
|
||||
if ("new".equals(action)) {
|
||||
setEntityWithEnabledSave(new Employee());
|
||||
saveButton.setVisible(true);
|
||||
editButton.setVisible(false);
|
||||
setFieldsEditable();
|
||||
upload.setVisible(true);
|
||||
profileImagePreview.setVisible(true);
|
||||
salaryTotal.setValue(BigDecimal.valueOf(0.0));
|
||||
} else {
|
||||
final Employee employee;
|
||||
UUID employeeId = UUID.fromString(s);
|
||||
var employee = employeeService.getEmployee(employeeId);
|
||||
setEntityWithEnabledSave(employee);
|
||||
|
||||
if (s != null) {
|
||||
final UUID employeeId = UUID.fromString(s);
|
||||
employee = employeeService.getEmployee(employeeId);
|
||||
} else {
|
||||
employee = employeeService.getEmployee(getEmployeeId().get());
|
||||
}
|
||||
|
||||
if ("edit".equals(action)) {
|
||||
setEntityWithEnabledSave(employee);
|
||||
setEditHandler(null);
|
||||
if ("edit".equals(action) && !s.isEmpty()) {
|
||||
saveButton.setVisible(true);
|
||||
editButton.setVisible(false);
|
||||
status.setValue(employee.getStatus());
|
||||
setFieldsEditable();
|
||||
upload.setVisible(true);
|
||||
displayProfileImage(employee);
|
||||
profileImagePreview.setVisible(true);
|
||||
salaryTotal.setValue(employee.getSalaryTotal());
|
||||
} else if ("view".equals(action) || "me".equals(action)) {
|
||||
setEntity(employee);
|
||||
setEditHandler(__ -> getUI().ifPresent(ui -> ui.navigate("/employees/" + employee.getId() + "/edit")));
|
||||
} else if ("view".equals(action) && !s.isEmpty()) {
|
||||
setFieldsReadOnly();
|
||||
saveButton.setVisible(false);
|
||||
editButton.setVisible(true);
|
||||
setFieldsReadOnly();
|
||||
displayProfileImage(employee);
|
||||
salaryTotal.setValue(employee.getSalaryTotal());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -528,6 +423,7 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
profileImagePreview.setVisible(true);
|
||||
profileImagePreview.setMaxWidth("250px");
|
||||
profileImagePreview.setMaxHeight("250px");
|
||||
|
||||
upload.setVisible(true);
|
||||
} else {
|
||||
profileImagePreview.setVisible(true);
|
||||
@ -536,7 +432,7 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
}
|
||||
|
||||
private void setFieldsReadOnly() {
|
||||
username.setReadOnly(true);
|
||||
username.setReadOnly(false);
|
||||
firstName.setReadOnly(true);
|
||||
lastName.setReadOnly(true);
|
||||
status.setReadOnly(true);
|
||||
@ -548,41 +444,46 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
numberOfChildren.setReadOnly(true);
|
||||
phoneNumber.setReadOnly(true);
|
||||
personalEmail.setReadOnly(true);
|
||||
phoneNumberProfessional.setReadOnly(true);
|
||||
position.setReadOnly(true);
|
||||
team.setReadOnly(true);
|
||||
emergencyCName.setReadOnly(true);
|
||||
emergencyCAddress.setReadOnly(true);
|
||||
emergencyCPhone.setReadOnly(true);
|
||||
emergencyCEmail.setReadOnly(true);
|
||||
upload.setVisible(true);
|
||||
profileImagePreview.setVisible(true);
|
||||
age.setReadOnly(true);
|
||||
gender.setReadOnly(true);
|
||||
status.setReadOnly(true);
|
||||
ci.setReadOnly(true);
|
||||
issuedIn.setReadOnly(true);
|
||||
educationTitles.setReadOnly(true);
|
||||
certifications.setReadOnly(true);
|
||||
pTitle1.setReadOnly(true);
|
||||
pTitle2.setReadOnly(true);
|
||||
pTitle3.setReadOnly(true);
|
||||
pStudy1.setReadOnly(true);
|
||||
pStudy2.setReadOnly(true);
|
||||
pStudy3.setReadOnly(true);
|
||||
certification1.setReadOnly(true);
|
||||
certification2.setReadOnly(true);
|
||||
certification3.setReadOnly(true);
|
||||
certification4.setReadOnly(true);
|
||||
recognition.setReadOnly(true);
|
||||
achievements.setReadOnly(true);
|
||||
languages.setReadOnly(true);
|
||||
language.setReadOnly(true);
|
||||
languageLevel.setReadOnly(true);
|
||||
cod.setReadOnly(true);
|
||||
leadManager.setReadOnly(true);
|
||||
project.setReadOnly(true);
|
||||
dateOfEntry.setReadOnly(true);
|
||||
dateOfExit.setReadOnly(true);
|
||||
contractType.setReadOnly(true);
|
||||
customContractType.setReadOnly(true);
|
||||
seniority.setReadOnly(true);
|
||||
salaryTotal.setReadOnly(true);
|
||||
salaryBasic.setReadOnly(true);
|
||||
professionalBonus.setReadOnly(true);
|
||||
tenureBonus.setReadOnly(true);
|
||||
salary.setReadOnly(true);
|
||||
bankName.setReadOnly(true);
|
||||
accountNumber.setReadOnly(true);
|
||||
gpss.setReadOnly(true);
|
||||
sss.setReadOnly(true);
|
||||
beneficiarie1.setReadOnly(true);
|
||||
beneficiarie2.setReadOnly(true);
|
||||
beneficiaries.setReadOnly(true);
|
||||
}
|
||||
|
||||
private void setFieldsEditable() {
|
||||
@ -598,117 +499,73 @@ public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlPara
|
||||
numberOfChildren.setReadOnly(false);
|
||||
phoneNumber.setReadOnly(false);
|
||||
personalEmail.setReadOnly(false);
|
||||
phoneNumberProfessional.setReadOnly(false);
|
||||
position.setReadOnly(false);
|
||||
team.setReadOnly(false);
|
||||
emergencyCName.setReadOnly(false);
|
||||
emergencyCAddress.setReadOnly(false);
|
||||
emergencyCPhone.setReadOnly(false);
|
||||
emergencyCEmail.setReadOnly(false);
|
||||
upload.setVisible(false);
|
||||
profileImagePreview.setVisible(true);
|
||||
age.setReadOnly(false);
|
||||
gender.setReadOnly(false);
|
||||
status.setReadOnly(false);
|
||||
ci.setReadOnly(false);
|
||||
issuedIn.setReadOnly(false);
|
||||
educationTitles.setReadOnly(false);
|
||||
certifications.setReadOnly(false);
|
||||
pTitle1.setReadOnly(false);
|
||||
pTitle2.setReadOnly(false);
|
||||
pTitle3.setReadOnly(false);
|
||||
pStudy1.setReadOnly(false);
|
||||
pStudy2.setReadOnly(false);
|
||||
pStudy3.setReadOnly(false);
|
||||
certification1.setReadOnly(false);
|
||||
certification2.setReadOnly(false);
|
||||
certification3.setReadOnly(false);
|
||||
certification4.setReadOnly(false);
|
||||
recognition.setReadOnly(false);
|
||||
achievements.setReadOnly(false);
|
||||
languages.setReadOnly(false);
|
||||
language.setReadOnly(false);
|
||||
languageLevel.setReadOnly(false);
|
||||
cod.setReadOnly(false);
|
||||
leadManager.setReadOnly(false);
|
||||
project.setReadOnly(false);
|
||||
dateOfEntry.setReadOnly(false);
|
||||
dateOfExit.setReadOnly(false);
|
||||
contractType.setReadOnly(false);
|
||||
customContractType.setReadOnly(false);
|
||||
seniority.setReadOnly(false);
|
||||
salaryTotal.setReadOnly(false);
|
||||
salaryBasic.setReadOnly(false);
|
||||
professionalBonus.setReadOnly(false);
|
||||
tenureBonus.setReadOnly(false);
|
||||
salary.setReadOnly(false);
|
||||
bankName.setReadOnly(false);
|
||||
accountNumber.setReadOnly(false);
|
||||
gpss.setReadOnly(false);
|
||||
sss.setReadOnly(false);
|
||||
beneficiarie1.setReadOnly(false);
|
||||
beneficiarie2.setReadOnly(false);
|
||||
beneficiaries.setReadOnly(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
tabSheet.add("Info Personal", new VerticalLayout(
|
||||
generalInfo,
|
||||
imagenSub,
|
||||
upload,
|
||||
profileImagePreview,
|
||||
firstName,
|
||||
lastName,
|
||||
gender,
|
||||
status,
|
||||
birthday,
|
||||
age,
|
||||
birthCity,
|
||||
residenceAddress,
|
||||
localAddress,
|
||||
maritalStatus,
|
||||
ci,
|
||||
issuedIn,
|
||||
numberOfChildren,
|
||||
phoneNumber,
|
||||
personalEmail,
|
||||
phoneNumberProfessional,
|
||||
contEmerg,
|
||||
emergencyCName,
|
||||
emergencyCAddress,
|
||||
emergencyCPhone,
|
||||
emergencyCEmail
|
||||
));
|
||||
|
||||
tabSheet.add("Info Profesional", new VerticalLayout(
|
||||
titulos,
|
||||
educationTitles,
|
||||
certif,
|
||||
certifications,
|
||||
logros,
|
||||
recognition,
|
||||
achievements,
|
||||
idioma,
|
||||
languages
|
||||
));
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
tabSheet.add("Info Administrativa", new VerticalLayout(
|
||||
cod,
|
||||
position,
|
||||
team,
|
||||
leadManager,
|
||||
infoCont,
|
||||
dateOfEntry,
|
||||
dateOfExit,
|
||||
seniority,
|
||||
contractType,
|
||||
customContractType,
|
||||
salaryBasic,
|
||||
professionalBonus,
|
||||
tenureBonus,
|
||||
salaryTotal,
|
||||
datBanc,
|
||||
bankName,
|
||||
accountNumber,
|
||||
datGest,
|
||||
gpss,
|
||||
sss,
|
||||
beneficiarie1,
|
||||
beneficiarie2
|
||||
));
|
||||
}
|
||||
|
||||
return List.of(
|
||||
new HorizontalLayout(reportButton, excelReportButton),
|
||||
username,
|
||||
tabSheet,
|
||||
dialog
|
||||
infoPer,
|
||||
infoGenr,
|
||||
upload, profileImagePreview,
|
||||
firstName, lastName,
|
||||
gender, status,
|
||||
birthday, age,
|
||||
birthCity, residenceAddress, localAddress,
|
||||
maritalStatus, ci, issuedIn, numberOfChildren,
|
||||
phoneNumber, personalEmail,
|
||||
contEmerg, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail,
|
||||
infProf,
|
||||
titulos, pTitle1, pTitle2, pTitle3, pStudy1, pStudy2, pStudy3,
|
||||
certif, certification1, certification2, certification3, certification4,
|
||||
logros, recognition, achievements,
|
||||
idioma, language, languageLevel,
|
||||
infoAdm,
|
||||
cod, position, team, leadManager, project,
|
||||
infoCont, dateOfEntry, dateOfExit, contractType, seniority, salary,
|
||||
datBanc, bankName, accountNumber,
|
||||
datGest, gpss, sss, beneficiaries,
|
||||
saveButton, editButton, reportButton, dialog
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
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.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
import com.vaadin.flow.component.grid.GridSortOrder;
|
||||
import com.vaadin.flow.data.provider.SortDirection;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Employees")
|
||||
@Route(value = "/employees", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class EmployeesListView extends Main {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class);
|
||||
|
||||
public EmployeesListView(final EmployeeService employeeService) {
|
||||
this.employeeService = employeeService;
|
||||
setupView();
|
||||
refreshGrid();
|
||||
}
|
||||
|
||||
private void setupView() {
|
||||
add(new H2("Employee List"));
|
||||
configureTable();
|
||||
add(createAddEmployeeButton());
|
||||
add(table);
|
||||
}
|
||||
|
||||
private void configureTable() {
|
||||
table.setColumns("firstName", "lastName", "status");
|
||||
addEditButtonColumn("View", this::navigateToEmployeeView);
|
||||
addEditButtonColumn("Edit", this::navigateToEditView);
|
||||
setupPagingGrid();
|
||||
}
|
||||
|
||||
private void addEditButtonColumn(final String label, final ButtonClickHandler handler) {
|
||||
table.addComponentColumn(employee -> createButton(label, () -> handler.handle(employee)));
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction) {
|
||||
Button button = new Button(label);
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddEmployeeButton() {
|
||||
return createButton("Add Employee", this::navigateToAddEmployeeView);
|
||||
}
|
||||
|
||||
private void navigateToEditView(final Employee employee) {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/edit"));
|
||||
}
|
||||
|
||||
private void navigateToEmployeeView(final Employee employee) {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/view"));
|
||||
}
|
||||
|
||||
private void navigateToAddEmployeeView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, "new"));
|
||||
}
|
||||
|
||||
private void setupPagingGrid() {
|
||||
table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
table.setPageSize(5);
|
||||
}
|
||||
|
||||
private void refreshGrid() {
|
||||
table.setPagingDataProvider((page, pageSize) -> fetchEmployees((int) page, pageSize));
|
||||
}
|
||||
|
||||
private List<Employee> fetchEmployees(final int page, final int pageSize) {
|
||||
int start = page * pageSize;
|
||||
if (hasSortOrder()) {
|
||||
return fetchSortedEmployees(start, pageSize);
|
||||
}
|
||||
return employeeService.findEmployees(start, pageSize);
|
||||
}
|
||||
|
||||
private boolean hasSortOrder() {
|
||||
return !table.getSortOrder().isEmpty();
|
||||
}
|
||||
|
||||
private List<Employee> fetchSortedEmployees(final int start, final int pageSize) {
|
||||
GridSortOrder<Employee> sortOrder = table.getSortOrder().getFirst();
|
||||
return employeeService.findEmployees(start, pageSize,
|
||||
sortOrder.getSorted().getKey(),
|
||||
sortOrder.getDirection() == SortDirection.ASCENDING);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ButtonClickHandler {
|
||||
void handle(Employee employee);
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import com.hilerio.ace.AceEditor;
|
||||
import com.hilerio.ace.AceMode;
|
||||
import com.hilerio.ace.AceTheme;
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.ExamService;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.primefactorsolutions.service.CompilerService;
|
||||
import com.vaadin.flow.component.*;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
@ -27,15 +27,19 @@ import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Background;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.BoxSizing;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Display;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Flex;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FlexDirection;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FontSize;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FontWeight;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Gap;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Height;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Margin;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Overflow;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Padding;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.TextColor;
|
||||
import io.overcoded.vaadin.panel.Panel;
|
||||
import io.overcoded.vaadin.panel.PanelConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@ -45,23 +49,24 @@ import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@PageTitle("Evaluacion")
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@Route(value = "/candidate-exam", layout = MainLayout.class)
|
||||
@Route(value = "/evaluation", layout = MainLayout.class)
|
||||
@AnonymousAllowed
|
||||
@Slf4j
|
||||
public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
public class EvaluationView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
private final CompilerService compilerService;
|
||||
private final ExamService examService;
|
||||
private final AssessmentService assessmentService;
|
||||
|
||||
private AceEditor questionEditor = null;
|
||||
private Dialog dialog = null;
|
||||
private Dialog completeDialog = null;
|
||||
private AceEditor result = null;
|
||||
private Exam exam = null;
|
||||
private Assessment assessment = null;
|
||||
private Submission currSubmission = null;
|
||||
private Boolean isCompleted = false;
|
||||
|
||||
@ -69,48 +74,33 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
private MenuItem prev = null;
|
||||
private MenuItem next = null;
|
||||
private MenuItem reset = null;
|
||||
private MenuItem finish = null;
|
||||
private Panel candidatePanel = null;
|
||||
private Section sidebar = null;
|
||||
private SimpleTimer timer = null;
|
||||
private DescriptionList dl = null;
|
||||
private Section editorSection = null;
|
||||
private Section startSection = null;
|
||||
private Section completedSection = null;
|
||||
private H3 questionTitle = null;
|
||||
private Text questionDescription = null;
|
||||
|
||||
public CandidateExamView(final CompilerService compilerService, final ExamService examService) {
|
||||
public EvaluationView(final CompilerService compilerService, final AssessmentService assessmentService) {
|
||||
this.compilerService = compilerService;
|
||||
this.examService = examService;
|
||||
this.assessmentService = assessmentService;
|
||||
|
||||
addClassNames(Display.FLEX, Flex.GROW, Height.FULL);
|
||||
|
||||
initStartSection();
|
||||
initCompletedSection();
|
||||
initEditorSection();
|
||||
initResultDialog();
|
||||
initCompleteDialog();
|
||||
initTimer(examService);
|
||||
initCandidatePanel();
|
||||
initSidebar();
|
||||
|
||||
VerticalLayout vl = new VerticalLayout();
|
||||
vl.add(completedSection, candidatePanel, startSection, editorSection, dialog);
|
||||
add(vl);
|
||||
add(completedSection, startSection, editorSection, sidebar, dialog);
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private void initTimer(final ExamService examService) {
|
||||
timer = new SimpleTimer(0);
|
||||
timer.setMinutes(true);
|
||||
timer.addTimerEndEvent((ComponentEventListener<SimpleTimer.TimerEndedEvent>) timerEndedEvent -> {
|
||||
Notification.show("Tiempo completado.", 5_000, Notification.Position.TOP_CENTER);
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.examService.saveSubmission(exam.getId(), this.currSubmission);
|
||||
this.exam = examService.completeExam(exam.getId());
|
||||
goToCompleted();
|
||||
updateUI();
|
||||
});
|
||||
timer.setFractions(false);
|
||||
}
|
||||
|
||||
private void initResultDialog() {
|
||||
dialog = new Dialog();
|
||||
dialog.setHeaderTitle("Resultados");
|
||||
@ -138,8 +128,8 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
dialog.add(dialogLayout);
|
||||
|
||||
final Button saveButton = new Button("Guardar y Siguiente", e -> {
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.examService.saveSubmission(exam.getId(), this.currSubmission);
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
|
||||
dialog.close();
|
||||
goToNext();
|
||||
});
|
||||
@ -166,9 +156,9 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
final Button completeButton = new Button("Terminar", e -> {
|
||||
completeDialog.close();
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.examService.saveSubmission(exam.getId(), this.currSubmission);
|
||||
this.exam = examService.completeExam(exam.getId());
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
|
||||
this.assessment = assessmentService.completeAssessment(assessment.getId());
|
||||
goToCompleted();
|
||||
updateUI();
|
||||
});
|
||||
@ -205,26 +195,23 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
final MenuBar navMenuBar = new MenuBar();
|
||||
prev = navMenuBar.addItem("Anterior pregunta",
|
||||
menuItemClickEvent -> {
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.examService.saveSubmission(exam.getId(), this.currSubmission);
|
||||
this.currSubmission = this.examService.getPrevSubmission(exam.getId(), this.currSubmission);
|
||||
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
|
||||
this.currSubmission = this.assessmentService.getPrevSubmission(assessment.getId(), this.currSubmission);
|
||||
updateUI();
|
||||
});
|
||||
next = navMenuBar.addItem("Siguiente pregunta",
|
||||
menuItemClickEvent -> {
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.examService.saveSubmission(exam.getId(), this.currSubmission);
|
||||
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
|
||||
goToNext();
|
||||
});
|
||||
reset = navMenuBar.addItem("Reiniciar pregunta (deshacer todos los cambios)",
|
||||
menuItemClickEvent -> {
|
||||
this.currSubmission.setText(this.currSubmission.getQuestion().getContent());
|
||||
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
this.currSubmission.setResponse(this.currSubmission.getQuestion().getContent());
|
||||
this.questionEditor.setValue(this.currSubmission.getQuestion().getContent());
|
||||
});
|
||||
finish = navMenuBar.addItem("Terminar evaluacion", menuItemClickEvent -> {
|
||||
this.completeDialog.open();
|
||||
});
|
||||
|
||||
final Div menuBar = new Div();
|
||||
menuBar.add(runMenuBar, navMenuBar);
|
||||
@ -265,9 +252,9 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
start = new Button("Empezar");
|
||||
start.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
||||
start.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.exam = this.examService.startExam(this.exam.getId());
|
||||
this.assessment = this.assessmentService.startAssessment(this.assessment.getId());
|
||||
|
||||
if (tf.getValue().trim().equalsIgnoreCase(this.exam.getCandidate().getEmail())) {
|
||||
if (tf.getValue().trim().equalsIgnoreCase(this.assessment.getCandidate().getEmail())) {
|
||||
this.getUI().get().getPage().reload();
|
||||
} else {
|
||||
Notification notification = new Notification();
|
||||
@ -329,7 +316,7 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
}
|
||||
|
||||
private void goToNext() {
|
||||
Submission found = this.examService.getNextSubmission(exam.getId(),
|
||||
Submission found = this.assessmentService.getNextSubmission(assessment.getId(),
|
||||
this.currSubmission.getId());
|
||||
|
||||
if (found == null) {
|
||||
@ -340,44 +327,78 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
}
|
||||
}
|
||||
|
||||
private void initCandidatePanel() {
|
||||
final PanelConfig config = PanelConfig.builder()
|
||||
.closeable(false)
|
||||
.collapsable(false)
|
||||
.build();
|
||||
private void initSidebar() {
|
||||
sidebar = new Section();
|
||||
sidebar.addClassNames(Background.CONTRAST_5, BoxSizing.BORDER, Display.FLEX, FlexDirection.COLUMN,
|
||||
Flex.SHRINK_NONE, Overflow.AUTO, Padding.LARGE);
|
||||
sidebar.setWidth("256px");
|
||||
|
||||
candidatePanel = new Panel(config, "", new HorizontalLayout());
|
||||
candidatePanel.setVisible(false);
|
||||
dl = new DescriptionList();
|
||||
dl.addClassNames(Display.FLEX, FlexDirection.COLUMN, Gap.LARGE, Margin.Bottom.SMALL, Margin.Top.NONE,
|
||||
FontSize.SMALL);
|
||||
|
||||
final Text text = new Text("Tiempo restante:");
|
||||
|
||||
timer = new SimpleTimer(0);
|
||||
timer.setMinutes(true);
|
||||
timer.addTimerEndEvent((ComponentEventListener<SimpleTimer.TimerEndedEvent>) timerEndedEvent -> {
|
||||
Notification.show("Tiempo completado.", 5_000, Notification.Position.TOP_CENTER);
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
|
||||
this.assessment = assessmentService.completeAssessment(assessment.getId());
|
||||
goToCompleted();
|
||||
updateUI();
|
||||
});
|
||||
timer.setFractions(false);
|
||||
|
||||
final Button completeButton = new Button("Terminar evaluacion");
|
||||
completeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
|
||||
completeButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.completeDialog.open();
|
||||
});
|
||||
|
||||
// Add it all together
|
||||
sidebar.add(dl, text, timer, completeButton);
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if (exam == null || !exam.isStarted()) {
|
||||
if (assessment == null || !assessment.isStarted()) {
|
||||
editorSection.setVisible(false);
|
||||
startSection.setVisible(true);
|
||||
sidebar.setVisible(false);
|
||||
} else {
|
||||
if (currSubmission != null) {
|
||||
questionEditor.setValue(this.currSubmission.getText());
|
||||
questionEditor.setValue(this.currSubmission.getResponse());
|
||||
questionTitle.setText(this.currSubmission.getQuestion().getTitle());
|
||||
questionDescription.setText(this.currSubmission.getQuestion().getDescription());
|
||||
}
|
||||
|
||||
editorSection.setVisible(true);
|
||||
startSection.setVisible(false);
|
||||
sidebar.setVisible(true);
|
||||
|
||||
updateCandidatePanel();
|
||||
prev.setEnabled(currSubmission != null && !assessment.isFirst(currSubmission));
|
||||
next.setEnabled(currSubmission == null || !assessment.isLast(currSubmission));
|
||||
|
||||
prev.setEnabled(currSubmission != null && !exam.isFirst(currSubmission));
|
||||
next.setEnabled(currSubmission == null || !exam.isLast(currSubmission));
|
||||
|
||||
if (this.exam.isCompleted()) {
|
||||
if (this.assessment.isCompleted()) {
|
||||
goToCompleted();
|
||||
this.editorSection.setVisible(false);
|
||||
this.candidatePanel.setVisible(false);
|
||||
this.sidebar.setVisible(false);
|
||||
this.startSection.setVisible(false);
|
||||
this.completedSection.setVisible(true);
|
||||
}
|
||||
|
||||
final Long remainingTime = this.exam.getRemainingTimeSeconds();
|
||||
if (dl.getChildren().collect(Collectors.toList()).isEmpty()) {
|
||||
dl.add(
|
||||
createItem("Candidato:", assessment.getCandidate().getEmail()),
|
||||
createItem("Hora de inicio:", Optional.ofNullable(assessment.getStartingTime())
|
||||
.map(t -> ZonedDateTime.ofInstant(t,
|
||||
ZoneId.of("GMT-4")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
|
||||
.orElse("N/A"))
|
||||
);
|
||||
}
|
||||
|
||||
final Long remainingTime = this.assessment.getRemainingTimeSeconds();
|
||||
timer.pause();
|
||||
timer.setStartTime(remainingTime > 0 ? remainingTime : 3);
|
||||
timer.setMinutes(true);
|
||||
@ -386,19 +407,6 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCandidatePanel() {
|
||||
final Text candidateName = new Text("Candidato: " + exam.getCandidate().getEmail() + ", ");
|
||||
final Text startTime = new Text("Hora de inicio: " + Optional.ofNullable(exam.getStartingTime())
|
||||
.map(t -> ZonedDateTime.ofInstant(t,
|
||||
ZoneId.of("GMT-4")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
|
||||
.orElse("N/A") + ", ");
|
||||
final Text text = new Text("Tiempo restante:");
|
||||
final HorizontalLayout layout = new HorizontalLayout(candidateName, startTime, text, timer);
|
||||
layout.setWidthFull();
|
||||
candidatePanel.setContent(layout);
|
||||
candidatePanel.setVisible(true);
|
||||
}
|
||||
|
||||
private Div createItem(final String label, final String value) {
|
||||
return new Div(createTerm(label), createDescription(value));
|
||||
}
|
||||
@ -426,18 +434,18 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
this.exam = this.examService.getExam(UUID.fromString(s));
|
||||
this.assessment = this.assessmentService.getAssessment(UUID.fromString(s));
|
||||
|
||||
if (this.exam == null) {
|
||||
if (this.assessment == null) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (this.exam.isCompleted()) {
|
||||
if (this.assessment.isCompleted()) {
|
||||
goToCompleted();
|
||||
}
|
||||
|
||||
this.currSubmission = this.exam.isStarted()
|
||||
? this.examService.getNextSubmission(exam.getId(), null)
|
||||
this.currSubmission = this.assessment.isStarted()
|
||||
? this.assessmentService.getNextSubmission(assessment.getId(), null)
|
||||
: null;
|
||||
|
||||
updateUI();
|
@ -0,0 +1,252 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.model.HoursWorked;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.HoursWorkedService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.IsoFields;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Registro de Horas Trabajadas")
|
||||
@Route(value = "/hours-worked-list", layout = MainLayout.class)
|
||||
public class HoursWorkedListView extends Main {
|
||||
|
||||
private final HoursWorkedService hoursWorkedService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final PagingGrid<HoursWorked> hoursWorkedGrid = new PagingGrid<>();
|
||||
private List<Employee> employees = Collections.emptyList();
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<Team> teamFilter;
|
||||
private UUID selectedEmployeeId;
|
||||
|
||||
|
||||
public HoursWorkedListView(final HoursWorkedService hoursWorkedService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
this.hoursWorkedService = hoursWorkedService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
this.employees = employeeService.findAllEmployees();
|
||||
|
||||
initializeView();
|
||||
refreshGridListHoursWorked(null, null);
|
||||
}
|
||||
|
||||
private void refreshGridListHoursWorked(final Employee employee,
|
||||
final Team team) {
|
||||
hoursWorkedGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
int start = (int) (page * hoursWorkedGrid.getPageSize());
|
||||
List<HoursWorked> hoursWorkedList = fetchFilteredHoursWorked(start, pageSize, employee, team);
|
||||
|
||||
double totalHours = hoursWorkedList.stream()
|
||||
.mapToDouble(HoursWorked::getTotalHours)
|
||||
.sum();
|
||||
|
||||
Notification.show("Total de horas trabajadas: " + totalHours,
|
||||
3000, Notification.Position.BOTTOM_CENTER);
|
||||
|
||||
return hoursWorkedList;
|
||||
});
|
||||
hoursWorkedGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<HoursWorked> fetchFilteredHoursWorked(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team) {
|
||||
List<HoursWorked> filteredHoursWorked = hoursWorkedService.findAll();
|
||||
|
||||
if (employee != null && !"TODOS".equals(employee.getFirstName())) {
|
||||
filteredHoursWorked = filteredHoursWorked.stream()
|
||||
.filter(hw -> hw.getEmployee().getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null && !"TODOS".equals(team.getName())) {
|
||||
filteredHoursWorked = filteredHoursWorked.stream()
|
||||
.filter(hw -> hw.getEmployee().getTeam() != null
|
||||
&& hw.getEmployee().getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
for (HoursWorked hoursWorked : filteredHoursWorked) {
|
||||
if (employee != null && hoursWorked.getEmployee().getId().equals(employee.getId())) {
|
||||
LocalDate date = hoursWorked.getDate();
|
||||
int currentWeek = date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
|
||||
double totalWorkedInSameWeek = filteredHoursWorked.stream()
|
||||
.filter(hw -> hw.getEmployee().getId().equals(employee.getId())
|
||||
&&
|
||||
hw.getDate().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR) == currentWeek)
|
||||
.mapToDouble(HoursWorked::getHours)
|
||||
.sum();
|
||||
|
||||
double updatedPendingHours = totalWorkedInSameWeek - hoursWorked.getHours();
|
||||
hoursWorked.setHoraspendientes(updatedPendingHours);
|
||||
}
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredHoursWorked.size());
|
||||
return filteredHoursWorked.subList(start, end);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
setupFilters();
|
||||
add(createAddHoursWorked());
|
||||
setupListHoursWorkedGrid();
|
||||
add(hoursWorkedGrid);
|
||||
add(createActionButtons());
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
add(createEmployeeFilter());
|
||||
add(createTeamFilter());
|
||||
}
|
||||
|
||||
private void setupListHoursWorkedGrid() {
|
||||
hoursWorkedGrid.addColumn(hw -> hw.getDate() != null ? hw.getDate().toString() : "")
|
||||
.setHeader("Fecha")
|
||||
.setSortable(true);
|
||||
hoursWorkedGrid.addColumn(hw -> hw.getWeekNumber())
|
||||
.setHeader("Semana")
|
||||
.setSortable(true);
|
||||
hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getFirstName() + " " + hw.getEmployee().getLastName())
|
||||
.setHeader("Empleado");
|
||||
hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getTeam() != null ? hw.getEmployee().getTeam()
|
||||
.getName() : "Sin asignar")
|
||||
.setHeader("Equipo");
|
||||
hoursWorkedGrid.addColumn(HoursWorked::getActividad).setHeader("Actividad");
|
||||
hoursWorkedGrid.addColumn(hw -> hw.getHours()).setHeader("Total Horas").setSortable(true);
|
||||
hoursWorkedGrid.addColumn(hw -> hw.getHoraspendientes() - calcularTotal(hw)).setHeader("Horas Pendientes")
|
||||
.setSortable(true);
|
||||
|
||||
hoursWorkedGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
hoursWorkedGrid.setPageSize(5);
|
||||
hoursWorkedGrid.asSingleSelect().addValueChangeListener(event -> {
|
||||
HoursWorked selectedHoursWorked = event.getValue();
|
||||
if (selectedHoursWorked != null) {
|
||||
selectedEmployeeId = selectedHoursWorked.getEmployee().getId();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private double calcularTotal(final HoursWorked hoursWorked) {
|
||||
List<HoursWorked> listHoursworkedemploye = hoursWorkedService.findListHoursWorkedEmployee(
|
||||
hoursWorked.getEmployee().getId(), hoursWorked.getWeekNumber());
|
||||
return calculateTotalUtilized(listHoursworkedemploye);
|
||||
}
|
||||
|
||||
private double calculateTotalUtilized(final List<HoursWorked> employeeRequests) {
|
||||
return employeeRequests.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.mapToDouble(hoursworked -> hoursworked.getHours())
|
||||
.sum();
|
||||
}
|
||||
|
||||
private HorizontalLayout createActionButtons() {
|
||||
Button viewButton = new Button("Ver", event -> {
|
||||
if (selectedEmployeeId != null) {
|
||||
navigateToHoursWorkedView(selectedEmployeeId);
|
||||
} else {
|
||||
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
|
||||
}
|
||||
});
|
||||
Button closeButton = new Button("Salir", event -> navigateToListView());
|
||||
return new HorizontalLayout(viewButton, closeButton);
|
||||
}
|
||||
|
||||
private void navigateToListView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(MainView.class));
|
||||
}
|
||||
|
||||
private void navigateToHoursWorkedView(final UUID idEmployee) {
|
||||
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + idEmployee.toString()));
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction) {
|
||||
Button button = new Button(label);
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddHoursWorked() {
|
||||
return createButton("Agregar Actividad", this::navigateToHours);
|
||||
}
|
||||
|
||||
private void navigateToHours() {
|
||||
getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, "new"));
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
employees.addFirst(createAllEmployeesOption());
|
||||
employeeFilter.setItems(employees);
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(
|
||||
event.getValue(),
|
||||
teamFilter.getValue()
|
||||
)
|
||||
);
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final Employee employee) {
|
||||
return "TODOS".equals(employee.getFirstName())
|
||||
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teams.addFirst(createAllTeamsOption());
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(team -> getTeamLabel(team));
|
||||
teamFilter.setValue(teams.getFirst());
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return team != null && !"TODOS".equals(team.getName()) ? team.getName() : "TODOS";
|
||||
}
|
||||
|
||||
private Employee createAllEmployeesOption() {
|
||||
Employee allEmployeesOption = new Employee();
|
||||
allEmployeesOption.setFirstName("TODOS");
|
||||
return allEmployeesOption;
|
||||
}
|
||||
|
||||
private Team createAllTeamsOption() {
|
||||
Team allTeamsOption = new Team();
|
||||
allTeamsOption.setName("TODOS");
|
||||
return allTeamsOption;
|
||||
}
|
||||
}
|
@ -0,0 +1,255 @@
|
||||
package com.primefactorsolutions.views;
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.model.HoursWorked;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.HoursWorkedService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.Label;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.datepicker.VDatePicker;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.time.temporal.IsoFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Horas Trabajadas")
|
||||
@Route(value = "/hours-worked-list/:hours-workedId?/:action?", layout = MainLayout.class)
|
||||
public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements HasUrlParameter<String> {
|
||||
private final VDatePicker dateField = new VDatePicker("Fecha");
|
||||
private final ComboBox<Team> teamField = new ComboBox<>("Equipo");
|
||||
private ComboBox<Employee> employeeField;
|
||||
private final ComboBox<String> tareasEspecificasDropdown = new ComboBox<>("Tarea Específica");
|
||||
private final TextField tareaEspecificaInput = new TextField("Otra Tarea Específica");
|
||||
private final TextField horasTareaEspecificaField = new TextField("Horas Tarea Específica");
|
||||
private final TextField activityField = new TextField("Actividad");
|
||||
private final TextField hoursField = new TextField("Horas");
|
||||
|
||||
private final H2 equipoLabel = new H2("Tareas del Cliente/Equipo");
|
||||
private final H2 empresaLabel = new H2("Tareas de la Empresa");
|
||||
private final Label totalCompletadoLabel = new Label();
|
||||
|
||||
private final HoursWorkedService hoursWorkedService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private HoursWorked hoursWorked;
|
||||
private Employee employee;
|
||||
|
||||
private Button saveButton;
|
||||
|
||||
public HoursWorkedView(final HoursWorkedService hoursWorkedService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
super(HoursWorked.class);
|
||||
this.hoursWorkedService = hoursWorkedService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
|
||||
initializeDateField();
|
||||
initializeTeamField();
|
||||
initializeEmployeeField();
|
||||
configureTareasEspecificas();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
final String s = params.get("hours-workedId").orElse(null);
|
||||
|
||||
if ("new".equals(action)) {
|
||||
setEntityWithEnabledSave(new HoursWorked());
|
||||
} else {
|
||||
UUID hoursWorkedId = UUID.fromString(s);
|
||||
var hoursWorked = hoursWorkedService.getHoursWorked(hoursWorkedId);
|
||||
setEntityWithEnabledSave(hoursWorked);
|
||||
|
||||
if ("edit".equals(action) && !s.isEmpty()) {
|
||||
saveButton.setVisible(true);
|
||||
} else if ("view".equals(action) && !s.isEmpty()) {
|
||||
saveButton.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
dateField,
|
||||
teamField,
|
||||
employeeField,
|
||||
equipoLabel,
|
||||
activityField,
|
||||
hoursField,
|
||||
empresaLabel,
|
||||
tareasEspecificasDropdown,
|
||||
tareaEspecificaInput,
|
||||
horasTareaEspecificaField,
|
||||
createCloseButton()
|
||||
);
|
||||
}
|
||||
|
||||
private void configureTareasEspecificas() {
|
||||
tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones",
|
||||
"Colaboraciones", "Aprendizajes", "Proyectos PFS", "Otros");
|
||||
tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea...");
|
||||
|
||||
tareasEspecificasDropdown.addValueChangeListener(event -> {
|
||||
String selected = event.getValue();
|
||||
boolean isOtros = "Otros".equals(selected);
|
||||
tareaEspecificaInput.setVisible(isOtros);
|
||||
horasTareaEspecificaField.setVisible(true);
|
||||
if (!isOtros) {
|
||||
tareaEspecificaInput.clear();
|
||||
horasTareaEspecificaField.clear();
|
||||
}
|
||||
});
|
||||
tareaEspecificaInput.setVisible(false);
|
||||
horasTareaEspecificaField.setVisible(false);
|
||||
}
|
||||
|
||||
protected Button createSaveButton() {
|
||||
saveButton = new Button("Guardar");
|
||||
saveButton.addClickListener(event -> saveHoursWorked());
|
||||
return saveButton;
|
||||
}
|
||||
|
||||
protected Button createCloseButton() {
|
||||
Button closeButton = new Button("Cerrar");
|
||||
closeButton.addClickListener(event -> closeForm());
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
private void initializeTeamField() {
|
||||
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teamField.setItems(teamService.findAllTeams());
|
||||
teamField.setItemLabelGenerator(Team::getName);
|
||||
teamField.setValue(teams.getFirst());
|
||||
teamField.addValueChangeListener(event -> {
|
||||
if (teams != null) {
|
||||
employeeField.getValue();
|
||||
event.getValue();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private ComboBox<Employee> initializeEmployeeField() {
|
||||
employeeField = new ComboBox<>("Empleado");
|
||||
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
employeeField.setItems(employees);
|
||||
employeeField.setItemLabelGenerator(this::getEmployeeFullName);
|
||||
employeeField.setValue(employees.getFirst());
|
||||
return employeeField;
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final Employee employee) {
|
||||
return "TODOS".equals(employee.getFirstName())
|
||||
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private void initializeDateField() {
|
||||
LocalDate today = LocalDate.now();
|
||||
YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth());
|
||||
|
||||
LocalDate startOfMonth = currentMonth.atDay(1);
|
||||
|
||||
LocalDate maxSelectableDate = today;
|
||||
|
||||
dateField.setMin(startOfMonth);
|
||||
dateField.setMax(maxSelectableDate);
|
||||
dateField.setValue(today);
|
||||
|
||||
dateField.addValueChangeListener(event -> {
|
||||
LocalDate selectedDate = event.getValue();
|
||||
if (selectedDate != null) {
|
||||
int weekNumber = selectedDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
Notification.show("Número de la semana: " + weekNumber, 3000, Notification.Position.BOTTOM_CENTER);
|
||||
if (hoursWorked != null) {
|
||||
hoursWorked.setWeekNumber(weekNumber);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveHoursWorked() {
|
||||
if (isFormValid()) {
|
||||
HoursWorked hoursWorked = getEntity();
|
||||
setFieldValues(hoursWorked);
|
||||
hoursWorkedService.save(hoursWorked);
|
||||
Notification.show("Horas trabajadas guardadas correctamente.");
|
||||
closeForm();
|
||||
}
|
||||
}
|
||||
|
||||
private void setFieldValues(final HoursWorked hoursWorked) {
|
||||
hoursWorked.setDate(dateField.getValue());
|
||||
hoursWorked.setTeam(teamField.getValue());
|
||||
hoursWorked.setEmployee(employeeField.getValue());
|
||||
hoursWorked.setActividad(activityField.getValue());
|
||||
try {
|
||||
double hours = Double.parseDouble(hoursField.getValue());
|
||||
hoursWorked.setHours(hours);
|
||||
} catch (NumberFormatException e) {
|
||||
Notification.show("Por favor, ingrese un número válido para las horas.");
|
||||
}
|
||||
if ("Otros".equals(tareasEspecificasDropdown.getValue())) {
|
||||
hoursWorked.setTareasEspecificas(tareaEspecificaInput.getValue());
|
||||
try {
|
||||
double horasEspecifica = Double.parseDouble(horasTareaEspecificaField.getValue());
|
||||
hoursWorked.setHorasTareasEspecificas(horasEspecifica);
|
||||
double totalHoras = hoursWorked.getHours() + horasEspecifica;
|
||||
hoursWorked.setTotalHours(totalHoras);
|
||||
} catch (NumberFormatException e) {
|
||||
Notification.show("Por favor, ingrese un número válido para las horas de la tarea específica.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + hoursWorked.getId().toString()));
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return dateField.getValue() != null
|
||||
&&
|
||||
teamField.getValue() != null
|
||||
&&
|
||||
employeeField.getValue() != null
|
||||
&&
|
||||
!activityField.isEmpty();
|
||||
|
||||
}
|
||||
|
||||
private void configureViewOrEditAction(final String action) {
|
||||
if ("edit".equals(action) && hoursWorked != null) {
|
||||
setFieldsReadOnly(false);
|
||||
} else if ("view".equals(action) && hoursWorked != null) {
|
||||
setFieldsReadOnly(true);
|
||||
saveButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setFieldsReadOnly(final boolean readOnly) {
|
||||
dateField.setReadOnly(readOnly);
|
||||
teamField.setReadOnly(readOnly);
|
||||
employeeField.setReadOnly(readOnly);
|
||||
activityField.setReadOnly(readOnly);
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@ import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Route("init-account")
|
||||
@PageTitle("PFS Intra")
|
||||
@AnonymousAllowed
|
||||
|
@ -1,18 +1,6 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.views.assessment.EvaluationsListView;
|
||||
import com.primefactorsolutions.views.employee.DocumentsListView;
|
||||
import com.primefactorsolutions.views.employee.EmployeesListView;
|
||||
import com.primefactorsolutions.views.admin.TimeOffListView;
|
||||
import com.primefactorsolutions.views.assessment.ExamsListView;
|
||||
import com.primefactorsolutions.views.assessment.CandidatesListView;
|
||||
import com.primefactorsolutions.views.assessment.QuestionsListView;
|
||||
import com.primefactorsolutions.views.timeoff.TimeOffRequestsListView;
|
||||
import com.primefactorsolutions.views.timeoff.TimeOffSummaryListView;
|
||||
import com.primefactorsolutions.views.timesheet.TimesheetListView;
|
||||
import com.primefactorsolutions.views.timesheet.TimesheetReportView;
|
||||
import com.primefactorsolutions.views.util.AuthUtils;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.Text;
|
||||
import com.vaadin.flow.component.applayout.AppLayout;
|
||||
@ -138,63 +126,48 @@ public class MainLayout extends AppLayout {
|
||||
private SideNav createNavigation() {
|
||||
final SideNav nav = new SideNav();
|
||||
|
||||
if (AuthUtils.isUser(authContext)) {
|
||||
nav.addItem(new SideNavItem("Home", MainView.class, LineAwesomeIcon.HOME_SOLID.create()));
|
||||
authContext.getAuthenticatedUser(UserDetails.class).ifPresent(u -> {
|
||||
SideNavItem recruiting = new SideNavItem("Recruiting");
|
||||
recruiting.setPrefixComponent(LineAwesomeIcon.BUSINESS_TIME_SOLID.create());
|
||||
recruiting.addItem(new SideNavItem("Assessments", AssessmentsListView.class,
|
||||
LineAwesomeIcon.RIBBON_SOLID.create()));
|
||||
recruiting.addItem(new SideNavItem("Candidates", CandidatesListView.class,
|
||||
LineAwesomeIcon.USER.create()));
|
||||
recruiting.addItem(new SideNavItem("Questions", QuestionsListView.class,
|
||||
LineAwesomeIcon.QUESTION_SOLID.create()));
|
||||
|
||||
if (AuthUtils.isAdmin(authContext)) {
|
||||
SideNavItem admin = new SideNavItem("Admin");
|
||||
admin.setPrefixComponent(LineAwesomeIcon.BUILDING.create());
|
||||
admin.addItem(new SideNavItem("Calendario", TimeOffListView.class,
|
||||
LineAwesomeIcon.CALENDAR.create()));
|
||||
nav.addItem(admin);
|
||||
|
||||
SideNavItem recruiting = new SideNavItem("Recruiting");
|
||||
recruiting.setPrefixComponent(LineAwesomeIcon.BUSINESS_TIME_SOLID.create());
|
||||
recruiting.addItem(new SideNavItem("Candidates", CandidatesListView.class,
|
||||
LineAwesomeIcon.USER.create()));
|
||||
recruiting.addItem(new SideNavItem("Evaluations", EvaluationsListView.class,
|
||||
LineAwesomeIcon.BOOK_READER_SOLID.create()));
|
||||
recruiting.addItem(new SideNavItem("Exams", ExamsListView.class,
|
||||
LineAwesomeIcon.PEN_NIB_SOLID.create()));
|
||||
recruiting.addItem(new SideNavItem("Questions", QuestionsListView.class,
|
||||
LineAwesomeIcon.QUESTION_SOLID.create()));
|
||||
nav.addItem(recruiting);
|
||||
}
|
||||
|
||||
final SideNavItem timeOff = new SideNavItem("Time-off");
|
||||
timeOff.setPrefixComponent(LineAwesomeIcon.PLANE_DEPARTURE_SOLID.create());
|
||||
timeOff.addItem(new SideNavItem("Vacations", TimeOffSummaryListView.class,
|
||||
LineAwesomeIcon.UMBRELLA_BEACH_SOLID.create()));
|
||||
timeOff.addItem(new SideNavItem("Requests", TimeOffRequestsListView.class,
|
||||
LineAwesomeIcon.LIST_ALT.create()));
|
||||
|
||||
final SideNavItem timesheet = new SideNavItem("Timesheet");
|
||||
timesheet.setPrefixComponent(LineAwesomeIcon.HOURGLASS_START_SOLID.create());
|
||||
timesheet.addItem(new SideNavItem("Registro de Horas Trabajadas", TimesheetListView.class,
|
||||
LineAwesomeIcon.ID_CARD_SOLID.create()));
|
||||
|
||||
if (AuthUtils.isAdmin(authContext)) {
|
||||
timesheet.addItem(new SideNavItem("Reporte Horas Trabajadas", TimesheetReportView.class,
|
||||
LineAwesomeIcon.ID_CARD_SOLID.create()));
|
||||
}
|
||||
|
||||
final SideNavItem profile = new SideNavItem("Employee");
|
||||
profile.setPrefixComponent(LineAwesomeIcon.USER_TIE_SOLID.create());
|
||||
|
||||
if (AuthUtils.isAdmin(authContext)) {
|
||||
profile.addItem(new SideNavItem("Profiles", EmployeesListView.class,
|
||||
LineAwesomeIcon.USER_FRIENDS_SOLID.create()));
|
||||
}
|
||||
|
||||
profile.addItem(new SideNavItem("My Profile", "/employees/me",
|
||||
SideNavItem admin = new SideNavItem("Admin");
|
||||
admin.setPrefixComponent(LineAwesomeIcon.BUILDING.create());
|
||||
admin.addItem(new SideNavItem("Employees", EmployeesListView.class,
|
||||
LineAwesomeIcon.USER_EDIT_SOLID.create()));
|
||||
profile.addItem(new SideNavItem("Documents", DocumentsListView.class,
|
||||
admin.addItem(new SideNavItem("Documents", DocumentsListView.class,
|
||||
LineAwesomeIcon.FILE_ALT_SOLID.create()));
|
||||
|
||||
SideNavItem timeOff = new SideNavItem("My Time-off", TimeoffView.class,
|
||||
LineAwesomeIcon.PLANE_DEPARTURE_SOLID.create());
|
||||
timeOff.addItem(new SideNavItem("Vacations", RequestsListView.class,
|
||||
LineAwesomeIcon.UMBRELLA_BEACH_SOLID.create()));
|
||||
timeOff.addItem(new SideNavItem("Add Vacation", RequestRegisterView.class,
|
||||
LineAwesomeIcon.CALENDAR_PLUS.create()));
|
||||
timeOff.addItem(new SideNavItem("Pending Requests", PendingRequestsListView.class,
|
||||
LineAwesomeIcon.LIST_ALT.create()));
|
||||
SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class,
|
||||
LineAwesomeIcon.HOURGLASS_START_SOLID.create());
|
||||
timesheet.addItem(new SideNavItem("Registro de Horas Trabajadas", HoursWorkedListView.class,
|
||||
LineAwesomeIcon.ID_CARD_SOLID.create()));
|
||||
timesheet.addItem(new SideNavItem("Reporte Horas Trabajadas", ReporteView.class,
|
||||
LineAwesomeIcon.ID_CARD_SOLID.create()));
|
||||
|
||||
SideNavItem profile = new SideNavItem("My Profile", ProfileView.class,
|
||||
LineAwesomeIcon.USER_EDIT_SOLID.create());
|
||||
|
||||
nav.addItem(new SideNavItem("Home", MainView.class, LineAwesomeIcon.HOME_SOLID.create()));
|
||||
nav.addItem(admin);
|
||||
nav.addItem(recruiting);
|
||||
nav.addItem(profile);
|
||||
nav.addItem(timesheet);
|
||||
nav.addItem(timeOff);
|
||||
}
|
||||
});
|
||||
|
||||
return nav;
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.Text;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
@ -13,6 +12,6 @@ import jakarta.annotation.security.PermitAll;
|
||||
public class MainView extends Main {
|
||||
|
||||
public MainView() {
|
||||
add(new VerticalLayout(new Text("Welcome to PFS!")));
|
||||
add(new Text("Welcome"));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,227 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("PendingRequests")
|
||||
@Route(value = "/pending-requests", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class PendingRequestsListView extends Main {
|
||||
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final PagingGrid<TimeOffRequest> pendingRequestsGrid = new PagingGrid<>();
|
||||
|
||||
private List<Employee> employees = Collections.emptyList();
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<Team> teamFilter;
|
||||
private ComboBox<TimeOffRequestType> categoryFilter;
|
||||
private UUID selectedRequestId;
|
||||
|
||||
|
||||
public PendingRequestsListView(final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
this.employees = employeeService.findAllEmployees();
|
||||
initializeView();
|
||||
refreshGeneralPendingRequestsGrid(null, null, null);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
setupFilters();
|
||||
setupPendingRequestsGrid();
|
||||
add(pendingRequestsGrid);
|
||||
add(createActionButtons());
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
add(createEmployeeFilter());
|
||||
add(createTeamFilter());
|
||||
add(createCategoryFilter());
|
||||
}
|
||||
|
||||
private void setupPendingRequestsGrid() {
|
||||
pendingRequestsGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
|
||||
pendingRequestsGrid.addColumn(this::getTeamName).setHeader("Equipo");
|
||||
pendingRequestsGrid.addColumn(this::getCategory).setHeader("Categoría");
|
||||
|
||||
pendingRequestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
pendingRequestsGrid.setPageSize(5);
|
||||
pendingRequestsGrid.asSingleSelect().addValueChangeListener(event -> {
|
||||
TimeOffRequest selectedRequest = event.getValue();
|
||||
if (selectedRequest != null) {
|
||||
selectedRequestId = selectedRequest.getId();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private HorizontalLayout createActionButtons() {
|
||||
Button approveButton = createActionButton("Aprobar", TimeOffRequestStatus.APROBADO);
|
||||
Button rejectButton = createActionButton("Rechazar", TimeOffRequestStatus.RECHAZADO);
|
||||
Button closeButton = new Button("Salir", event -> navigateToMainView());
|
||||
return new HorizontalLayout(approveButton, rejectButton, closeButton);
|
||||
}
|
||||
|
||||
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,
|
||||
final Team team,
|
||||
final TimeOffRequestType category) {
|
||||
pendingRequestsGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
int start = (int) (page * pendingRequestsGrid.getPageSize());
|
||||
return fetchFilteredPendingRequests(start, pageSize, employee, team, category);
|
||||
});
|
||||
pendingRequestsGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<TimeOffRequest> fetchFilteredPendingRequests(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final TimeOffRequestType category) {
|
||||
List<TimeOffRequest> filteredPendingRequests
|
||||
= requestService.findRequestsByState(TimeOffRequestStatus.SOLICITADO);
|
||||
|
||||
if (employee != null && !"TODOS".equals(employee.getFirstName())) {
|
||||
filteredPendingRequests = filteredPendingRequests.stream()
|
||||
.filter(emp -> emp.getEmployee().getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null && !"TODOS".equals(team.getName())) {
|
||||
filteredPendingRequests = filteredPendingRequests.stream()
|
||||
.filter(emp -> emp.getEmployee().getTeam() != null
|
||||
&& emp.getEmployee().getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (category != null && category != TimeOffRequestType.TODOS) {
|
||||
filteredPendingRequests = filteredPendingRequests.stream()
|
||||
.filter(emp -> emp.getCategory().equals(category))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredPendingRequests.size());
|
||||
return filteredPendingRequests.subList(start, end);
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final TimeOffRequest request) {
|
||||
Employee employee = request.getEmployee();
|
||||
return getEmployeeFullNameLabel(employee);
|
||||
}
|
||||
|
||||
private String getEmployeeFullNameLabel(final Employee employee) {
|
||||
return "TODOS".equals(employee.getFirstName())
|
||||
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private String getTeamName(final TimeOffRequest request) {
|
||||
Team team = request.getEmployee().getTeam();
|
||||
return team != null ? team.getName() : "Sin asignar";
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return "TODOS".equals(team.getName()) ? "TODOS" : team.getName();
|
||||
}
|
||||
|
||||
private String getCategory(final TimeOffRequest request) {
|
||||
return String.valueOf(request.getCategory());
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
employees.addFirst(createAllEmployeesOption());
|
||||
employeeFilter.setItems(employees);
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullNameLabel);
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGeneralPendingRequestsGrid(
|
||||
event.getValue(),
|
||||
teamFilter.getValue(),
|
||||
categoryFilter.getValue()
|
||||
)
|
||||
);
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teams.addFirst(createAllTeamsOption());
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
teamFilter.setValue(teams.getFirst());
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGeneralPendingRequestsGrid(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue(),
|
||||
categoryFilter.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private ComboBox<TimeOffRequestType> createCategoryFilter() {
|
||||
categoryFilter = new ComboBox<>("Categoría");
|
||||
categoryFilter.setItems(TimeOffRequestType.values());
|
||||
categoryFilter.setValue(TimeOffRequestType.values()[0]);
|
||||
categoryFilter.addValueChangeListener(event ->
|
||||
refreshGeneralPendingRequestsGrid(
|
||||
employeeFilter.getValue(),
|
||||
teamFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
return categoryFilter;
|
||||
}
|
||||
|
||||
private Employee createAllEmployeesOption() {
|
||||
Employee allEmployeesOption = new Employee();
|
||||
allEmployeesOption.setFirstName("TODOS");
|
||||
return allEmployeesOption;
|
||||
}
|
||||
|
||||
private Team createAllTeamsOption() {
|
||||
Team allTeamsOption = new Team();
|
||||
allTeamsOption.setName("TODOS");
|
||||
return allTeamsOption;
|
||||
}
|
||||
|
||||
private void navigateToMainView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(MainView.class));
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Profile")
|
||||
@Route(value = "/profiles", layout = MainLayout.class)
|
||||
public class ProfileView extends Main {
|
||||
}
|
||||
|
@ -1,29 +1,27 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Question;
|
||||
import com.primefactorsolutions.service.QuestionService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.textfield.IntegerField;
|
||||
import com.vaadin.flow.component.textfield.TextArea;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Questions")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/questions", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class QuestionView extends BaseEntityForm<Question> implements HasUrlParameter<String> {
|
||||
@PermitAll
|
||||
public class QuestionView extends BeanValidationForm<Question> implements HasUrlParameter<String> {
|
||||
private final QuestionService questionService;
|
||||
|
||||
private TextField title = null;
|
||||
@ -31,9 +29,8 @@ public class QuestionView extends BaseEntityForm<Question> implements HasUrlPara
|
||||
private TextArea content = null;
|
||||
private IntegerField timeMinutes = null;
|
||||
|
||||
public QuestionView(final AuthenticationContext authenticationContext,
|
||||
final QuestionService questionService) {
|
||||
super(authenticationContext, Question.class);
|
||||
public QuestionView(final QuestionService questionService) {
|
||||
super(Question.class);
|
||||
this.questionService = questionService;
|
||||
title = new TextField();
|
||||
title.setWidthFull();
|
||||
@ -51,8 +48,8 @@ public class QuestionView extends BaseEntityForm<Question> implements HasUrlPara
|
||||
content.setLabel("Content");
|
||||
|
||||
setSavedHandler((SavedHandler<Question>) question -> {
|
||||
questionService.createOrUpdate(question);
|
||||
goTo(QuestionsListView.class);
|
||||
final Question saved = questionService.createOrUpdate(question);
|
||||
setEntityWithEnabledSave(saved);
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Question;
|
||||
import com.primefactorsolutions.service.QuestionService;
|
||||
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.html.Main;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.DataProvider;
|
||||
import com.vaadin.flow.data.provider.DataProviderListener;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Questions")
|
||||
@Route(value = "/questions", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class QuestionsListView extends Main {
|
||||
private final QuestionService questionService;
|
||||
|
||||
public QuestionsListView(final QuestionService questionService) {
|
||||
this.questionService = questionService;
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addQuestion = new Button("Add Question");
|
||||
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.getUI().get().navigate(QuestionView.class, "new");
|
||||
});
|
||||
hl.add(addQuestion);
|
||||
|
||||
final VGrid<Question> grid = new VGrid<>(Question.class);
|
||||
grid.setColumns("id", "title");
|
||||
grid.addComponentColumn((ValueProvider<Question, Component>) question -> {
|
||||
final Button edit = new Button("Edit");
|
||||
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
|
||||
this.getUI().get().navigate(QuestionView.class, question.getId().toString()));
|
||||
return edit;
|
||||
});
|
||||
grid.setDataProvider(new DataProvider<>() {
|
||||
@Override
|
||||
public boolean isInMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(final Query<Question, Object> query) {
|
||||
return questionService.getQuestions().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Question> fetch(final Query<Question, Object> query) {
|
||||
int limit = query.getLimit();
|
||||
int pagerSize = query.getPageSize();
|
||||
int page = query.getPage();
|
||||
return questionService.getQuestions().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshItem(final Question question) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAll() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registration addDataProviderListener(final DataProviderListener<Question> dataProviderListener) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
grid.setAllRowsVisible(true);
|
||||
|
||||
add(hl, grid);
|
||||
}
|
||||
}
|
234
src/main/java/com/primefactorsolutions/views/ReporteView.java
Normal file
234
src/main/java/com/primefactorsolutions/views/ReporteView.java
Normal file
@ -0,0 +1,234 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.HoursWorked;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.service.HoursWorkedService;
|
||||
import com.primefactorsolutions.service.ReportService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.html.Anchor;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.TextStyle;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@PermitAll
|
||||
@Route(value = "/reportes", layout = MainLayout.class)
|
||||
@PageTitle("Reporte de Horas Trabajadas")
|
||||
public class ReporteView extends VerticalLayout {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
private final HoursWorkedService hoursWorkedService;
|
||||
private final ReportService reportService;
|
||||
private final TeamService teamService;
|
||||
|
||||
private final ComboBox<Team> equipoComboBox = new ComboBox<>("Seleccionar Equipo");
|
||||
private final ComboBox<String> semanaComboBox = new ComboBox<>("Seleccionar Semana");
|
||||
private final Grid<Map<String, Object>> grid = new Grid<>();
|
||||
private final VerticalLayout headerLayout = new VerticalLayout();
|
||||
private Anchor downloadLink;
|
||||
|
||||
private final Span semanaInfoSpan = new Span();
|
||||
|
||||
// Obtener el año actual
|
||||
private int currentYear = LocalDate.now().getYear();
|
||||
|
||||
@Autowired
|
||||
public ReporteView(final HoursWorkedService hoursWorkedService,
|
||||
final ReportService reportService, final TeamService teamService,
|
||||
final EmployeeService employeeService) {
|
||||
this.hoursWorkedService = hoursWorkedService;
|
||||
this.reportService = reportService;
|
||||
this.teamService = teamService;
|
||||
this.employeeService = employeeService;
|
||||
|
||||
H2 title = new H2("Reporte de Horas Trabajadas");
|
||||
add(title);
|
||||
|
||||
List<Team> teams = teamService.findAllTeams();
|
||||
equipoComboBox.setItems(teams);
|
||||
equipoComboBox.setItemLabelGenerator(Team::getName);
|
||||
|
||||
// Configurar el ComboBox de semanas
|
||||
initializeSemanaComboBox();
|
||||
|
||||
// Listener para actualizar `semanaInfoSpan` con la selección del usuario en `semanaComboBox`
|
||||
semanaComboBox.addValueChangeListener(event -> {
|
||||
String selectedWeek = event.getValue();
|
||||
semanaInfoSpan.setText(selectedWeek != null ? selectedWeek : "Selecciona una semana");
|
||||
});
|
||||
|
||||
Button reportButton = new Button("Generar Reporte de Horas Trabajadas",
|
||||
event -> generateHoursWorkedReport());
|
||||
HorizontalLayout filtersLayout = new HorizontalLayout(equipoComboBox, semanaComboBox, reportButton);
|
||||
add(filtersLayout);
|
||||
|
||||
// Añadir `headerLayout` al diseño principal para el encabezado dinámico
|
||||
add(headerLayout);
|
||||
updateHeaderLayout(null, null);
|
||||
|
||||
grid.addColumn(map -> map.get("Empleado")).setHeader("Empleado");
|
||||
grid.addColumn(map -> map.get("Horas Trabajadas")).setHeader("Horas Trabajadas");
|
||||
grid.addColumn(map -> map.get("Horas Pendientes")).setHeader("Horas Pendientes");
|
||||
grid.addColumn(map -> map.get("Observaciones")).setHeader("Observaciones");
|
||||
|
||||
add(grid);
|
||||
}
|
||||
|
||||
private void initializeSemanaComboBox() {
|
||||
int year = LocalDate.now().getYear();
|
||||
LocalDate startOfYear = LocalDate.of(year, 1, 5); // Suponemos que la semana comienza el 5 de enero.
|
||||
|
||||
List<String> semanas = startOfYear.datesUntil(LocalDate.of(year + 1, 1, 1),
|
||||
java.time.Period.ofWeeks(1))
|
||||
.map(date -> {
|
||||
int weekNumber = date.get(WeekFields.of(DayOfWeek.MONDAY, 1)
|
||||
.weekOfWeekBasedYear());
|
||||
LocalDate startOfWeek = date;
|
||||
LocalDate endOfWeek = startOfWeek.plusDays(6);
|
||||
|
||||
return String.format("Semana %d: %s - %s",
|
||||
weekNumber,
|
||||
startOfWeek.getDayOfMonth() + " de " + startOfWeek.getMonth()
|
||||
.getDisplayName(TextStyle.FULL, Locale.getDefault()),
|
||||
endOfWeek.getDayOfMonth() + " de " + endOfWeek.getMonth()
|
||||
.getDisplayName(TextStyle.FULL, Locale.getDefault())
|
||||
);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
semanaComboBox.setItems(semanas);
|
||||
semanaComboBox.setPlaceholder("Seleccione una semana");
|
||||
}
|
||||
|
||||
private void generateHoursWorkedReport() {
|
||||
Team selectedEquipo = equipoComboBox.getValue();
|
||||
String selectedWeek = semanaComboBox.getValue();
|
||||
if (selectedEquipo == null || selectedWeek == null) {
|
||||
Notification.show("Por favor, selecciona un equipo y una semana para generar el reporte.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
int weekNumber = Integer.parseInt(selectedWeek.split(" ")[1].replace(":", ""));
|
||||
LocalDate selectedDate = LocalDate.now().with(WeekFields.of(DayOfWeek.FRIDAY, 1)
|
||||
.weekOfWeekBasedYear(), weekNumber);
|
||||
updateHeaderLayout(selectedEquipo, selectedDate);
|
||||
|
||||
List<HoursWorked> hoursWorkedList = hoursWorkedService.findAll().stream()
|
||||
.filter(hw -> hw.getEmployee().getTeam().getId().equals(selectedEquipo
|
||||
.getId()) && hw.getWeekNumber() == weekNumber)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
System.out.println(hoursWorkedList);
|
||||
if (hoursWorkedList.isEmpty()) {
|
||||
|
||||
Notification.show("No hay horas trabajadas disponibles para generar el reporte.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> data = hoursWorkedList.stream()
|
||||
.map(hoursWorked -> {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("ID", hoursWorked.getId().toString());
|
||||
map.put("Employee ID", hoursWorked.getEmployee().getId().toString());
|
||||
map.put("Empleado", hoursWorked.getEmployee().getFirstName() + " "
|
||||
+ hoursWorked.getEmployee().getLastName());
|
||||
map.put("Horas Trabajadas", hoursWorked.getTotalHours());
|
||||
map.put("Horas Pendientes", 40 - hoursWorked.getTotalHours());
|
||||
map.put("Observaciones", "");
|
||||
return map;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
grid.setItems(data);
|
||||
generateExcelDownloadLink(data, weekNumber);
|
||||
}
|
||||
|
||||
private void updateHeaderLayout(final Team team, final LocalDate dateInWeek) {
|
||||
headerLayout.removeAll();
|
||||
|
||||
if (team != null && dateInWeek != null) {
|
||||
LocalDate startOfWeek = dateInWeek.with(DayOfWeek.FRIDAY);
|
||||
LocalDate endOfWeek = dateInWeek.with(DayOfWeek.THURSDAY);
|
||||
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 "
|
||||
+ String.format("%03d", weekNumber) + "/" + currentYear) {{
|
||||
getStyle().set("font-size", "24px");
|
||||
getStyle().set("font-weight", "bold");
|
||||
}});
|
||||
|
||||
String teamLeadName = employeeService.getTeamLeadName(team.getId());
|
||||
headerLayout.add(
|
||||
new Span("Asunto: Informe Semanal de Horas Trabajadas") {{
|
||||
getStyle().set("font-size", "18px");
|
||||
}},
|
||||
semanaInfoSpan,
|
||||
new Span("Horas a cumplir: 40 horas") {{
|
||||
getStyle().set("font-size", "18px");
|
||||
}},
|
||||
new Span("Equipo: " + team.getName()) {{
|
||||
getStyle().set("font-size", "18px");
|
||||
}},
|
||||
new Span("Team Lead: " + teamLeadName) {{
|
||||
getStyle().set("font-size", "18px");
|
||||
}}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateExcelDownloadLink(final List<Map<String, Object>> data, final int weekNumber) {
|
||||
try {
|
||||
List<String> headers = List.of("Empleado",
|
||||
"Horas Trabajadas", "Horas Pendientes", "Observaciones");
|
||||
String selectedTeam = equipoComboBox.getValue().getName();
|
||||
byte[] excelBytes = reportService.writeAsExcel(
|
||||
"hours_worked_report", headers, data, selectedTeam, weekNumber, currentYear);
|
||||
|
||||
StreamResource excelResource = new StreamResource("hours_worked_report.xlsx",
|
||||
() -> new ByteArrayInputStream(excelBytes));
|
||||
if (downloadLink == null) {
|
||||
downloadLink = new Anchor(excelResource, "Descargar Reporte en Excel");
|
||||
downloadLink.getElement().setAttribute("download", true);
|
||||
add(downloadLink);
|
||||
} else {
|
||||
downloadLink.setHref(excelResource);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error al generar el reporte de horas trabajadas en Excel.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
}
|
||||
}
|
||||
|
||||
private int getWeekOfYear(final LocalDate date) {
|
||||
return date.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,426 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.service.VacationService;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.html.Div;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("RequestEmployee")
|
||||
@Route(value = "/requests", layout = MainLayout.class)
|
||||
public class RequestEmployeeView extends Div implements HasUrlParameter<String> {
|
||||
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private final VacationService vacationService;
|
||||
private final PagingGrid<TimeOffRequest> requestGrid = new PagingGrid<>(TimeOffRequest.class);
|
||||
private List<TimeOffRequest> requests = Collections.emptyList();
|
||||
private ComboBox<TimeOffRequestType> categoryFilter;
|
||||
private ComboBox<TimeOffRequestStatus> stateFilter;
|
||||
private UUID employeeId;
|
||||
private TimeOffRequest request;
|
||||
|
||||
public RequestEmployeeView(final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final VacationService vacationService) {
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
this.vacationService = vacationService;
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
requestService.updateRequestStatuses();
|
||||
setupFilters();
|
||||
setupGrid();
|
||||
add(requestGrid, createActionButtons(), createSummaryLayout());
|
||||
refreshRequestGrid(null, null);
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
categoryFilter = createCategoryFilter();
|
||||
stateFilter = createStateFilter();
|
||||
add(categoryFilter, stateFilter);
|
||||
}
|
||||
|
||||
private ComboBox<TimeOffRequestType> createCategoryFilter() {
|
||||
categoryFilter = new ComboBox<>("Categoría");
|
||||
categoryFilter.setItems(TimeOffRequestType.values());
|
||||
categoryFilter.setValue(TimeOffRequestType.values()[0]);
|
||||
categoryFilter.addValueChangeListener(event -> refreshRequestGrid(event.getValue(), stateFilter.getValue()));
|
||||
return categoryFilter;
|
||||
}
|
||||
|
||||
private ComboBox<TimeOffRequestStatus> createStateFilter() {
|
||||
stateFilter = new ComboBox<>("Estado de la solicitud");
|
||||
stateFilter.setItems(TimeOffRequestStatus.values());
|
||||
stateFilter.setValue(TimeOffRequestStatus.values()[0]);
|
||||
stateFilter.addValueChangeListener(event -> refreshRequestGrid(categoryFilter.getValue(), event.getValue()));
|
||||
return stateFilter;
|
||||
}
|
||||
|
||||
private void setupGrid() {
|
||||
requestGrid.setColumns(
|
||||
"category",
|
||||
"state",
|
||||
"startDate",
|
||||
"endDate",
|
||||
"daysToBeTake");
|
||||
|
||||
requestGrid.getColumnByKey("category").setHeader("Categoría");
|
||||
requestGrid.getColumnByKey("state").setHeader("Estado");
|
||||
requestGrid.getColumnByKey("startDate").setHeader("Fecha de Inicio");
|
||||
requestGrid.getColumnByKey("endDate").setHeader("Fecha de Fin");
|
||||
requestGrid.getColumnByKey("daysToBeTake").setHeader("Días a Tomar");
|
||||
|
||||
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
requestGrid.setPageSize(5);
|
||||
requestGrid.asSingleSelect().addValueChangeListener(event -> {
|
||||
TimeOffRequest selectedRequest = event.getValue();
|
||||
if (selectedRequest != null) {
|
||||
request = selectedRequest;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getStandardExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.MATERNIDAD,
|
||||
TimeOffRequestType.PATERNIDAD,
|
||||
TimeOffRequestType.MATRIMONIO,
|
||||
TimeOffRequestType.DUELO_1ER_GRADO,
|
||||
TimeOffRequestType.DUELO_2ER_GRADO,
|
||||
TimeOffRequestType.DIA_DEL_PADRE,
|
||||
TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getMaleSpecificExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
|
||||
);
|
||||
}
|
||||
|
||||
private VerticalLayout createSummaryLayout() {
|
||||
Employee employee = employeeService.getEmployee(employeeId);
|
||||
boolean isMale = employee.getGender() == Employee.Gender.MALE;
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
|
||||
List<Vacation> vacations = vacationService.findVacations();
|
||||
|
||||
double healthLicence = 2;
|
||||
List<TimeOffRequest> healthRequests = requestService
|
||||
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD);
|
||||
if (healthRequests != null && !healthRequests.isEmpty()) {
|
||||
healthLicence = healthRequests.getLast().getDaysBalance();
|
||||
}
|
||||
|
||||
double totalFixedAndMovableHolidays = calculateHolidayDays(vacations);
|
||||
double totalPersonalDays = calculatePersonalDays(vacations, isMale);
|
||||
List<Double> vacationDays = calculateVacationDays(employee);
|
||||
|
||||
double utilizedVacationCurrentDays = vacationDays.get(1);
|
||||
List<TimeOffRequest> vacationCurrentRequests = requestService
|
||||
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ACTUAL);
|
||||
if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) {
|
||||
utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance();
|
||||
}
|
||||
double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays);
|
||||
|
||||
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 utilizedPersonalDays = calculatePersonalDaysUtilized(isMale, currentYear);
|
||||
|
||||
double remainingHolidayDays = totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays;
|
||||
double remainingPersonalDays = (totalPersonalDays - utilizedPersonalDays) + healthLicence;
|
||||
double remainingVacationDays = totalVacationCurrentDays + totalVacationPreviousDays;
|
||||
|
||||
double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays;
|
||||
|
||||
return new VerticalLayout(
|
||||
new Span("Total feriados fijos y movibles: " + remainingHolidayDays),
|
||||
new Span("Total días libres personales: " + remainingPersonalDays),
|
||||
new Span("Total vacaciones pendientes de uso: " + remainingVacationDays),
|
||||
new Span("TOTAL GENERAL DE DÍAS DISPONIBLES: " + totalAvailableDays)
|
||||
);
|
||||
}
|
||||
|
||||
private double calculateHolidayDays(final List<Vacation> vacations) {
|
||||
return vacations.stream()
|
||||
.filter(req -> req.getType() != Vacation.Type.OTHER)
|
||||
.mapToDouble(Vacation::getDuration)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private double calculatePersonalDays(final List<Vacation> vacations, final boolean isMale) {
|
||||
return vacations.stream()
|
||||
.filter(req -> req.getType() == Vacation.Type.OTHER)
|
||||
.filter(req -> !getStandardExclusions().contains(req.getCategory()))
|
||||
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
|
||||
.filter(req -> !req.getCategory().name().startsWith("VACACION"))
|
||||
.filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.mapToDouble(Vacation::getDuration)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private List<Double> calculateVacationDays(final Employee employee) {
|
||||
List<Double> vacationDays = new ArrayList<>();
|
||||
|
||||
if (employee.getDateOfEntry() != null) {
|
||||
LocalDate entryDate = employee.getDateOfEntry();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue()
|
||||
|| (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth()
|
||||
<= today.getDayOfMonth());
|
||||
|
||||
LocalDate previousVacationYearDate;
|
||||
LocalDate currentVacationYearDate;
|
||||
|
||||
if (hasAnniversaryPassed) {
|
||||
previousVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 1,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
currentVacationYearDate = LocalDate.of(
|
||||
today.getYear(),
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
} else {
|
||||
previousVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 2,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
currentVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 1,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
}
|
||||
|
||||
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate));
|
||||
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate));
|
||||
} else {
|
||||
vacationDays.add(0.0);
|
||||
vacationDays.add(0.0);
|
||||
}
|
||||
return vacationDays;
|
||||
}
|
||||
|
||||
private double calculateHolidayUtilizedDays(final int year) {
|
||||
return requests.stream()
|
||||
.filter(this::verificationIsHoliday)
|
||||
.filter(this::verificationIsHoliday)
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.APROBADO)
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.EN_USO)
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO)
|
||||
.filter(req -> getStartDateYear(req) == year)
|
||||
.mapToDouble(TimeOffRequest::getDaysToBeTake)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private double calculatePersonalDaysUtilized(final boolean isMale, final int year) {
|
||||
return requests.stream()
|
||||
.filter(req -> !verificationIsHoliday(req))
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.APROBADO)
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.EN_USO)
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO)
|
||||
.filter(req -> !getStandardExclusions().contains(req.getCategory()))
|
||||
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
|
||||
.filter(req -> !req.getCategory().name().startsWith("VACACION"))
|
||||
.filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.filter(req -> getStartDateYear(req) == year)
|
||||
.mapToDouble(TimeOffRequest::getDaysToBeTake)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private int getStartDateYear(final TimeOffRequest request) {
|
||||
if (request.getStartDate() != null) {
|
||||
return request.getStartDate().getYear();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) {
|
||||
int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0;
|
||||
if (yearsOfService > 10) {
|
||||
return 30;
|
||||
}
|
||||
if (yearsOfService > 5) {
|
||||
return 20;
|
||||
}
|
||||
if (yearsOfService > 1) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Boolean verificationIsHoliday(final TimeOffRequest request) {
|
||||
Vacation vacation = vacationService.findVacationByCategory(request.getCategory());
|
||||
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) {
|
||||
navigateToRequestView(request, "edit");
|
||||
}
|
||||
|
||||
private void navigateToViewRequest(final TimeOffRequest request) {
|
||||
navigateToRequestView(request, "view");
|
||||
}
|
||||
|
||||
private void navigateToRequestView(final TimeOffRequest request, final String action) {
|
||||
getUI().ifPresent(ui -> ui.navigate(RequestView.class, request.getId().toString() + "/" + action));
|
||||
}
|
||||
|
||||
private void refreshRequestGrid(final TimeOffRequestType category, final TimeOffRequestStatus state) {
|
||||
requestGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
int start = (int) (page * requestGrid.getPageSize());
|
||||
return fetchFilteredTimeOffRequests(start, pageSize, category, state);
|
||||
});
|
||||
requestGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<TimeOffRequest> fetchFilteredTimeOffRequests(final int start,
|
||||
final int pageSize,
|
||||
final TimeOffRequestType category,
|
||||
final TimeOffRequestStatus state) {
|
||||
|
||||
requests = requestService.findRequestsByEmployeeId(employeeId);
|
||||
generateRequests();
|
||||
if (category != null && !"TODOS".equals(category.name())) {
|
||||
requests = requests.stream()
|
||||
.filter(req -> req.getCategory().equals(category))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
if (state != null && !"TODOS".equals(state.name())) {
|
||||
requests = requests.stream()
|
||||
.filter(req -> req.getState().equals(state))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
int end = Math.min(start + pageSize, requests.size());
|
||||
return requests.subList(start, end);
|
||||
}
|
||||
|
||||
public void generateRequests() {
|
||||
boolean isMale = isEmployeeMale();
|
||||
|
||||
for (TimeOffRequestType type : TimeOffRequestType.values()) {
|
||||
if (shouldIncludeRequest(type) && isValidRequestType(type, isMale)) {
|
||||
TimeOffRequest request = createRequest(type);
|
||||
if (isVacationExpired(request)) {
|
||||
request.setState(TimeOffRequestStatus.VENCIDO);
|
||||
} else {
|
||||
request.setState(TimeOffRequestStatus.PENDIENTE);
|
||||
}
|
||||
requests.add(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEmployeeMale() {
|
||||
return employeeService.getEmployee(employeeId).getGender() == Employee.Gender.MALE;
|
||||
}
|
||||
|
||||
private boolean isValidRequestType(final TimeOffRequestType type, final boolean isMale) {
|
||||
return !getStandardExclusions().contains(type)
|
||||
&& !(isMale && getMaleSpecificExclusions().contains(type))
|
||||
&& type != TimeOffRequestType.TODOS;
|
||||
}
|
||||
|
||||
private TimeOffRequest createRequest(final TimeOffRequestType type) {
|
||||
TimeOffRequest request = new TimeOffRequest();
|
||||
request.setCategory(type);
|
||||
return request;
|
||||
}
|
||||
|
||||
private boolean isVacationExpired(final TimeOffRequest request) {
|
||||
Vacation vacation = vacationService.findVacationByCategory(request.getCategory());
|
||||
|
||||
if (vacation != null && vacation.getMonthOfYear() != null && vacation.getDayOfMonth() != null) {
|
||||
int vacationMonth = vacation.getMonthOfYear();
|
||||
int vacationDay = vacation.getDayOfMonth();
|
||||
int currentMonth = LocalDate.now().getMonthValue();
|
||||
int currentDay = LocalDate.now().getDayOfMonth();
|
||||
|
||||
return vacationMonth < currentMonth || (vacationMonth == currentMonth && vacationDay < currentDay);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldIncludeRequest(final TimeOffRequestType type) {
|
||||
List<TimeOffRequest> existingRequest = requestService.findByEmployeeAndCategory(employeeId, type);
|
||||
return existingRequest.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent event, final String parameter) {
|
||||
employeeId = UUID.fromString(parameter);
|
||||
Employee employee = employeeService.getEmployee(employeeId);
|
||||
requests = requestService.findRequestsByEmployeeId(employeeId);
|
||||
setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName());
|
||||
requestGrid.setItems(requests);
|
||||
initializeView();
|
||||
}
|
||||
|
||||
private void setViewTitle(final String employeeName, final String employeeTeam) {
|
||||
addComponentAsFirst(new H3("Nombre del empleado: " + employeeName));
|
||||
addComponentAtIndex(1, new H3("Equipo: " + employeeTeam));
|
||||
}
|
||||
}
|
@ -0,0 +1,511 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.service.VacationService;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.data.binder.Binder;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Request")
|
||||
@Route(value = "/requests/new", layout = MainLayout.class)
|
||||
public class RequestRegisterView extends VerticalLayout {
|
||||
|
||||
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
|
||||
private final ComboBox<TimeOffRequestType> categoryComboBox = new ComboBox<>("Categoría");
|
||||
private final NumberField availableDaysField = new NumberField("Días disponibles");
|
||||
private final DatePicker startDatePicker = new DatePicker("Fecha de inicio");
|
||||
private final DatePicker endDatePicker = new DatePicker("Fecha final");
|
||||
private final NumberField daysToBeTakenField = new NumberField("Días a tomar");
|
||||
private final NumberField balanceDaysField = new NumberField("Días de saldo");
|
||||
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private final VacationService vacationService;
|
||||
|
||||
private final Binder<TimeOffRequest> binder;
|
||||
private Vacation vacation;
|
||||
private Employee employee;
|
||||
private LocalDate endDate;
|
||||
|
||||
private Button saveButton;
|
||||
private Button closeButton;
|
||||
|
||||
public RequestRegisterView(final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final VacationService vacationService) {
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
this.vacationService = vacationService;
|
||||
this.binder = new Binder<>(TimeOffRequest.class);
|
||||
initializeView();
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
requestService.updateRequestStatuses();
|
||||
configureFormFields();
|
||||
configureButtons();
|
||||
configureBinder();
|
||||
setupFormLayout();
|
||||
configureInitialFieldStates();
|
||||
}
|
||||
|
||||
private void configureInitialFieldStates() {
|
||||
categoryComboBox.setEnabled(false);
|
||||
startDatePicker.setEnabled(false);
|
||||
endDatePicker.setEnabled(false);
|
||||
availableDaysField.setReadOnly(true);
|
||||
daysToBeTakenField.setReadOnly(true);
|
||||
balanceDaysField.setReadOnly(true);
|
||||
}
|
||||
|
||||
private void configureFormFields() {
|
||||
employeeComboBox.setItems(employeeService.findAllEmployees());
|
||||
employeeComboBox.setItemLabelGenerator(emp -> emp.getFirstName() + " " + emp.getLastName());
|
||||
employeeComboBox.addValueChangeListener(event -> {
|
||||
employee = event.getValue();
|
||||
handleEmployeeSelection(event.getValue());
|
||||
});
|
||||
categoryComboBox.addValueChangeListener(event -> {
|
||||
onCategoryChange(event.getValue());
|
||||
handleCategorySelection(event.getValue());
|
||||
});
|
||||
startDatePicker.addValueChangeListener(event -> updateDatePickerMinValues());
|
||||
endDatePicker.addValueChangeListener(event -> calculateDays());
|
||||
}
|
||||
|
||||
private void configureBinder() {
|
||||
binder.forField(employeeComboBox)
|
||||
.bind(TimeOffRequest::getEmployee, TimeOffRequest::setEmployee);
|
||||
binder.forField(categoryComboBox)
|
||||
.bind(TimeOffRequest::getCategory, TimeOffRequest::setCategory);
|
||||
binder.forField(availableDaysField)
|
||||
.bind(TimeOffRequest::getAvailableDays, TimeOffRequest::setAvailableDays);
|
||||
binder.forField(startDatePicker)
|
||||
.bind(TimeOffRequest::getStartDate, TimeOffRequest::setStartDate);
|
||||
binder.forField(endDatePicker)
|
||||
.bind(TimeOffRequest::getEndDate, TimeOffRequest::setEndDate);
|
||||
binder.forField(daysToBeTakenField)
|
||||
.bind(TimeOffRequest::getDaysToBeTake, TimeOffRequest::setDaysToBeTake);
|
||||
binder.forField(balanceDaysField)
|
||||
.bind(TimeOffRequest::getDaysBalance, TimeOffRequest::setDaysBalance);
|
||||
binder.setBean(new TimeOffRequest());
|
||||
}
|
||||
|
||||
private void handleEmployeeSelection(final Employee selectedEmployee) {
|
||||
if (selectedEmployee != null) {
|
||||
categoryComboBox.clear();
|
||||
availableDaysField.clear();
|
||||
startDatePicker.clear();
|
||||
endDatePicker.clear();
|
||||
daysToBeTakenField.clear();
|
||||
balanceDaysField.clear();
|
||||
categoryComboBox.setEnabled(true);
|
||||
startDatePicker.setEnabled(false);
|
||||
endDatePicker.setEnabled(false);
|
||||
filterCategories(selectedEmployee);
|
||||
}
|
||||
}
|
||||
|
||||
private void filterCategories(final Employee employee) {
|
||||
categoryComboBox.clear();
|
||||
List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
List<TimeOffRequestType> allCategories = Arrays.asList(TimeOffRequestType.values());
|
||||
List<TimeOffRequestType> availableCategories = allCategories.stream()
|
||||
.filter(category -> isCategoryAvailable(employeeRequests, category))
|
||||
.filter(category -> isCategoryAllowedByGender(category, employee.getGender()))
|
||||
.filter(category -> category != TimeOffRequestType.TODOS)
|
||||
.filter(category -> shouldIncludeVacationGestionActual(employeeRequests, category))
|
||||
.filter(category -> shouldIncludeVacationGestionAnterior(employeeRequests, category))
|
||||
.toList();
|
||||
|
||||
categoryComboBox.setItems(availableCategories);
|
||||
}
|
||||
|
||||
|
||||
private boolean shouldIncludeVacationGestionActual(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category != TimeOffRequestType.VACACION_GESTION_ACTUAL) {
|
||||
return true;
|
||||
}
|
||||
return employeeRequests.stream()
|
||||
.anyMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
&& request.getDaysBalance() == 0
|
||||
&& request.getState() == TimeOffRequestStatus.TOMADO);
|
||||
}
|
||||
|
||||
private boolean shouldIncludeVacationGestionAnterior(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category != TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
return true;
|
||||
}
|
||||
return employeeRequests.stream()
|
||||
.noneMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
&& request.getDaysBalance() == 0);
|
||||
}
|
||||
|
||||
private void onCategoryChange(final TimeOffRequestType selectedCategory) {
|
||||
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
startDatePicker.setEnabled(true);
|
||||
endDatePicker.setEnabled(true);
|
||||
} else {
|
||||
startDatePicker.setEnabled(true);
|
||||
endDatePicker.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCategoryAvailable(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
List<TimeOffRequest> requestsByCategory = employeeRequests.stream()
|
||||
.filter(request -> request.getCategory() == category)
|
||||
.toList();
|
||||
|
||||
if (requestsByCategory.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TimeOffRequest latestRequest = requestsByCategory.stream()
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.orElse(null);
|
||||
|
||||
boolean isSpecialCategory = category == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| category == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| category == TimeOffRequestType.VACACION_GESTION_ANTERIOR;
|
||||
|
||||
if (isSpecialCategory) {
|
||||
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getDaysBalance() > 0)
|
||||
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO
|
||||
|| (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getDaysBalance() == 0
|
||||
&& latestRequest.getExpiration().isBefore(LocalDate.now()));
|
||||
} else {
|
||||
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getExpiration().isBefore(LocalDate.now()))
|
||||
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCategoryAllowedByGender(final TimeOffRequestType category, final Employee.Gender gender) {
|
||||
if (gender == Employee.Gender.MALE) {
|
||||
return category != TimeOffRequestType.MATERNIDAD
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL;
|
||||
} else {
|
||||
return category != TimeOffRequestType.DIA_DEL_PADRE
|
||||
&& category != TimeOffRequestType.PATERNIDAD;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCategorySelection(final TimeOffRequestType selectedCategory) {
|
||||
if (selectedCategory != null) {
|
||||
updateAvailableDays(selectedCategory);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAvailableDays(final TimeOffRequestType selectedCategory) {
|
||||
vacation = vacationService.findVacationByCategory(selectedCategory);
|
||||
UUID employeeId = employeeComboBox.getValue().getId();
|
||||
List<TimeOffRequest> requests = requestService.findByEmployeeAndCategory(employeeId, selectedCategory);
|
||||
|
||||
if (vacation != null) {
|
||||
TimeOffRequest requestWithBalance = requests.stream()
|
||||
.filter(request -> request.getDaysBalance() > 0
|
||||
&& request.getState() != TimeOffRequestStatus.VENCIDO
|
||||
&& request.getState() != TimeOffRequestStatus.RECHAZADO)
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.orElse(null);
|
||||
|
||||
if (requestWithBalance != null) {
|
||||
if (requestWithBalance.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& requestWithBalance.getDaysBalance() > 0) {
|
||||
availableDaysField.setValue(requestWithBalance.getDaysBalance());
|
||||
} else {
|
||||
availableDaysField.setValue(vacation.getDuration());
|
||||
}
|
||||
} else if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
LocalDate dateOfEntry = employeeComboBox.getValue().getDateOfEntry();
|
||||
LocalDate currentDate = LocalDate.now();
|
||||
long yearsOfService = ChronoUnit.YEARS.between(dateOfEntry, currentDate);
|
||||
|
||||
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
yearsOfService -= 1;
|
||||
}
|
||||
|
||||
if (yearsOfService > 10) {
|
||||
availableDaysField.setValue(30.0);
|
||||
} else if (yearsOfService > 5) {
|
||||
availableDaysField.setValue(20.0);
|
||||
} else if (yearsOfService > 1) {
|
||||
availableDaysField.setValue(15.0);
|
||||
} else {
|
||||
availableDaysField.setValue(0.0);
|
||||
}
|
||||
} else {
|
||||
availableDaysField.setValue(vacation.getDuration());
|
||||
}
|
||||
setDatePickerLimits(vacation);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDatePickerLimits(final Vacation vacation) {
|
||||
LocalDate startDate;
|
||||
endDate = null;
|
||||
|
||||
UUID employeeId = employee.getId();
|
||||
List<TimeOffRequest> previousRequests
|
||||
= requestService.findByEmployeeAndCategory(employeeId, vacation.getCategory());
|
||||
|
||||
int startYear = calculateStartYear(previousRequests);
|
||||
|
||||
startDate = determineStartDate(vacation, startYear);
|
||||
|
||||
if (startDate.isBefore(LocalDate.now())) {
|
||||
startDate = determineStartDate(vacation, startYear + 1);
|
||||
}
|
||||
|
||||
if (startDate != null) {
|
||||
if (vacation.getExpiration() != null) {
|
||||
endDate = startDate.plusDays(vacation.getExpiration().intValue() - 1);
|
||||
} else {
|
||||
endDate = LocalDate.of(startDate.getYear(), 12, 31);
|
||||
}
|
||||
} else {
|
||||
startDate = LocalDate.now();
|
||||
}
|
||||
|
||||
setPickerValues(vacation, startDate);
|
||||
setPickerLimits(startDate, endDate);
|
||||
}
|
||||
|
||||
private int calculateStartYear(final List<TimeOffRequest> previousRequests) {
|
||||
if (previousRequests.isEmpty()) {
|
||||
return LocalDate.now().getYear();
|
||||
}
|
||||
|
||||
int lastRequestYear = previousRequests.stream()
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.map(request -> request.getStartDate().getYear())
|
||||
.orElse(LocalDate.now().getYear());
|
||||
|
||||
if (previousRequests.getLast().getState() != TimeOffRequestStatus.RECHAZADO) {
|
||||
lastRequestYear = lastRequestYear + 1;
|
||||
}
|
||||
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
return Math.max(lastRequestYear, currentYear);
|
||||
}
|
||||
|
||||
private LocalDate determineStartDate(final Vacation vacation, final int startYear) {
|
||||
if (vacation.getCategory() == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() != null) {
|
||||
return LocalDate.of(startYear, employee.getBirthday().getMonth(), employee.getBirthday().getDayOfMonth());
|
||||
}
|
||||
|
||||
if (vacation.getMonthOfYear() != null && vacation.getDayOfMonth() != null) {
|
||||
return LocalDate.of(startYear, vacation.getMonthOfYear().intValue(), vacation.getDayOfMonth().intValue());
|
||||
}
|
||||
|
||||
if (vacation.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD) {
|
||||
return LocalDate.now();
|
||||
}
|
||||
|
||||
return LocalDate.now();
|
||||
}
|
||||
|
||||
private void setPickerValues(final Vacation vacation, final LocalDate startDate) {
|
||||
startDatePicker.setValue(startDate);
|
||||
|
||||
if ((vacation.getDuration() != null && vacation.getDuration() == 0.5)
|
||||
|| vacation.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| vacation.getCategory() == TimeOffRequestType.CUMPLEAÑOS) {
|
||||
|
||||
endDatePicker.setValue(startDate);
|
||||
} else {
|
||||
int durationDays = (vacation.getDuration() != null ? vacation.getDuration().intValue() - 1 : 0);
|
||||
endDatePicker.setValue(startDate.plusDays(durationDays));
|
||||
}
|
||||
}
|
||||
|
||||
private void setPickerLimits(final LocalDate startDate, final LocalDate endDate) {
|
||||
startDatePicker.setMin(startDate);
|
||||
startDatePicker.setMax(endDate);
|
||||
endDatePicker.setMin(startDate);
|
||||
endDatePicker.setMax(endDate);
|
||||
}
|
||||
|
||||
private void updateDatePickerMinValues() {
|
||||
LocalDate startDate = startDatePicker.getValue();
|
||||
if (availableDaysField.getValue() != null) {
|
||||
if (availableDaysField.getValue() == 0.5) {
|
||||
endDatePicker.setValue(startDate.plusDays(0));
|
||||
} else {
|
||||
endDatePicker.setValue(startDate.plusDays(availableDaysField.getValue().intValue() - 1));
|
||||
}
|
||||
calculateDays();
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateDays() {
|
||||
LocalDate startDate = startDatePicker.getValue();
|
||||
LocalDate endDate = endDatePicker.getValue();
|
||||
Double availableDays = availableDaysField.getValue();
|
||||
|
||||
if (areDatesValid(startDate, endDate)) {
|
||||
double daysToBeTaken = calculateDaysBetween(startDate, endDate);
|
||||
setDaysToBeTakenField(daysToBeTaken);
|
||||
|
||||
double balanceDays = calculateBalanceDays(availableDays, daysToBeTakenField.getValue());
|
||||
balanceDaysField.setValue(balanceDays);
|
||||
|
||||
if (balanceDays < 0.0) {
|
||||
clearFields();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areDatesValid(final LocalDate startDate, final LocalDate endDate) {
|
||||
return startDate != null && endDate != null;
|
||||
}
|
||||
|
||||
private double calculateDaysBetween(final LocalDate startDate, final LocalDate endDate) {
|
||||
return java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1;
|
||||
}
|
||||
|
||||
private void setDaysToBeTakenField(final double daysToBeTaken) {
|
||||
if (vacation.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| vacation.getCategory() == TimeOffRequestType.CUMPLEAÑOS
|
||||
|| vacation.getCategory() == TimeOffRequestType.DIA_DEL_PADRE
|
||||
|| vacation.getCategory() == TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
|| vacation.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL
|
||||
|| vacation.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL) {
|
||||
daysToBeTakenField.setValue(0.5);
|
||||
} else {
|
||||
daysToBeTakenField.setValue(daysToBeTaken);
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateBalanceDays(final double availableDays, final double daysToBeTaken) {
|
||||
return availableDays - daysToBeTaken;
|
||||
}
|
||||
|
||||
private void clearFields() {
|
||||
daysToBeTakenField.clear();
|
||||
balanceDaysField.clear();
|
||||
endDatePicker.clear();
|
||||
}
|
||||
|
||||
private void configureButtons() {
|
||||
saveButton = new Button("Guardar", event -> saveRequest());
|
||||
closeButton = new Button("Salir", event -> closeForm());
|
||||
}
|
||||
|
||||
private void setupFormLayout() {
|
||||
add(
|
||||
new H3("Añadir solicitud de vacaciones"),
|
||||
employeeComboBox,
|
||||
categoryComboBox,
|
||||
availableDaysField,
|
||||
startDatePicker,
|
||||
endDatePicker,
|
||||
daysToBeTakenField,
|
||||
balanceDaysField,
|
||||
new HorizontalLayout(saveButton, closeButton)
|
||||
);
|
||||
}
|
||||
|
||||
private void saveRequest() {
|
||||
if (!binder.validate().isOk()) {
|
||||
Notification.show("Rellene correctamente todos los campos obligatorios.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateForm()) {
|
||||
Notification.show("Por favor, complete los campos antes de guardar");
|
||||
return;
|
||||
}
|
||||
|
||||
TimeOffRequest request = prepareRequest();
|
||||
|
||||
if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL) {
|
||||
handleVacationRequest(request);
|
||||
} else {
|
||||
handleExistingRequests(request);
|
||||
}
|
||||
|
||||
requestService.saveTimeOffRequest(request);
|
||||
Notification.show("Solicitud guardada correctamente.");
|
||||
closeForm();
|
||||
}
|
||||
|
||||
private TimeOffRequest prepareRequest() {
|
||||
TimeOffRequest request = binder.getBean();
|
||||
request.setStartDate(startDatePicker.getValue());
|
||||
request.setAvailableDays(availableDaysField.getValue());
|
||||
request.setExpiration(endDate != null ? endDate : endDatePicker.getValue());
|
||||
request.setState(TimeOffRequestStatus.SOLICITADO);
|
||||
return request;
|
||||
}
|
||||
|
||||
private void handleExistingRequests(final TimeOffRequest request) {
|
||||
List<TimeOffRequest> existingRequests =
|
||||
requestService.findByEmployeeAndCategory(employee.getId(), request.getCategory());
|
||||
|
||||
int maxRequests = request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
&& !request.getCategory().name().startsWith("VACACION")
|
||||
&& request.getCategory() != TimeOffRequestType.CUMPLEAÑOS
|
||||
? 2 : 1;
|
||||
|
||||
if (existingRequests.size() >= maxRequests) {
|
||||
existingRequests.stream()
|
||||
.min(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.ifPresent(oldestRequest -> requestService.deleteTimeOffRequest(oldestRequest.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleVacationRequest(final TimeOffRequest request) {
|
||||
List<TimeOffRequest> existingRequests = requestService.findByEmployeeAndCategory(
|
||||
employee.getId(),
|
||||
TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
);
|
||||
if (!existingRequests.isEmpty()) {
|
||||
TimeOffRequest existingRequest = existingRequests.getFirst();
|
||||
existingRequest.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR);
|
||||
requestService.saveTimeOffRequest(existingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateForm() {
|
||||
return employeeComboBox.getValue() != null
|
||||
&& categoryComboBox.getValue() != null
|
||||
&& startDatePicker.getValue() != null
|
||||
&& endDatePicker.getValue() != null;
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate(RequestsListView.class));
|
||||
}
|
||||
}
|
160
src/main/java/com/primefactorsolutions/views/RequestView.java
Normal file
160
src/main/java/com/primefactorsolutions/views/RequestView.java
Normal file
@ -0,0 +1,160 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Request")
|
||||
@Route(value = "/requests/:requestId?/:action?", layout = MainLayout.class)
|
||||
public class RequestView extends BeanValidationForm<TimeOffRequest> implements HasUrlParameter<String> {
|
||||
|
||||
private final ComboBox<TimeOffRequestStatus> state = new ComboBox<>("Estado de la solicitud");
|
||||
private final DatePicker expiration = new DatePicker("Vencimiento");
|
||||
private final DatePicker startDate = new DatePicker("Fecha de inicio");
|
||||
private final DatePicker endDate = new DatePicker("Fecha de fin");
|
||||
private final NumberField availableDays = new NumberField("Días disponibles");
|
||||
private final NumberField daysToBeTake = new NumberField("Días a tomar");
|
||||
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private TimeOffRequest request;
|
||||
private Employee employee;
|
||||
|
||||
private Button saveButton;
|
||||
|
||||
public RequestView(final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
super(TimeOffRequest.class);
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
state.setItems(List.of(TimeOffRequestStatus.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
final String requestIdString = params.get("requestId").orElse(null);
|
||||
|
||||
if ("new".equals(action)) {
|
||||
setEntityWithEnabledSave(new TimeOffRequest());
|
||||
} else {
|
||||
assert requestIdString != null;
|
||||
UUID requestId = UUID.fromString(requestIdString);
|
||||
request = requestService.findTimeOffRequest(requestId);
|
||||
UUID employeeId = request.getEmployee().getId();
|
||||
employee = employeeService.getEmployee(employeeId);
|
||||
setEntity(request);
|
||||
configureViewOrEditAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
createEmployeeHeader(),
|
||||
createTeamHeader(),
|
||||
createCategoryHeader(),
|
||||
state,
|
||||
expiration,
|
||||
startDate,
|
||||
endDate,
|
||||
availableDays,
|
||||
daysToBeTake,
|
||||
createCloseButton()
|
||||
);
|
||||
}
|
||||
|
||||
protected Button createSaveButton() {
|
||||
saveButton = new Button("Guardar");
|
||||
saveButton.addClickListener(event -> saveRequest());
|
||||
return saveButton;
|
||||
}
|
||||
|
||||
protected Button createCloseButton() {
|
||||
Button closeButton = new Button("Salir");
|
||||
closeButton.addClickListener(event -> closeForm());
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
private void setFieldsReadOnly(final boolean option) {
|
||||
state.setReadOnly(option);
|
||||
expiration.setReadOnly(option);
|
||||
startDate.setReadOnly(option);
|
||||
endDate.setReadOnly(option);
|
||||
availableDays.setReadOnly(option);
|
||||
daysToBeTake.setReadOnly(option);
|
||||
}
|
||||
|
||||
private void saveRequest() {
|
||||
if (isFormValid()) {
|
||||
TimeOffRequest request = getEntity();
|
||||
setRequestFieldValues(request);
|
||||
requestService.saveTimeOffRequest(request);
|
||||
Notification.show("Solicitud guardada correctamente.");
|
||||
closeForm();
|
||||
}
|
||||
}
|
||||
|
||||
private void setRequestFieldValues(final TimeOffRequest request) {
|
||||
request.setState(state.getValue());
|
||||
request.setExpiration(expiration.getValue());
|
||||
request.setStartDate(startDate.getValue());
|
||||
request.setEndDate(endDate.getValue());
|
||||
request.setAvailableDays(availableDays.getValue());
|
||||
request.setDaysToBeTake(daysToBeTake.getValue());
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate("requests/" + employee.getId().toString()));
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return !state.isEmpty()
|
||||
&& expiration.getValue() != null
|
||||
&& startDate.getValue() != null
|
||||
&& endDate.getValue() != null
|
||||
&& availableDays.getValue() != null
|
||||
&& daysToBeTake.getValue() != null;
|
||||
}
|
||||
|
||||
private void configureViewOrEditAction(final String action) {
|
||||
if ("edit".equals(action) && !request.getId().toString().isEmpty()) {
|
||||
setFieldsReadOnly(false);
|
||||
} else if ("view".equals(action) && !request.getId().toString().isEmpty()) {
|
||||
setFieldsReadOnly(true);
|
||||
saveButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private H3 createEmployeeHeader() {
|
||||
return new H3("Empleado: " + employee.getFirstName() + " " + employee.getLastName());
|
||||
}
|
||||
|
||||
private H3 createTeamHeader() {
|
||||
return new H3("Equipo: " + employee.getTeam().getName());
|
||||
}
|
||||
|
||||
private H3 createCategoryHeader() {
|
||||
return new H3("Categoría: " + request.getCategory());
|
||||
}
|
||||
}
|
@ -0,0 +1,414 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.service.VacationService;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Requests")
|
||||
@Route(value = "/requests", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class RequestsListView extends Main {
|
||||
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final VacationService vacationService;
|
||||
private final PagingGrid<Employee> requestGrid = new PagingGrid<>();
|
||||
|
||||
private List<Employee> employees = Collections.emptyList();
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<Team> teamFilter;
|
||||
private ComboBox<TimeOffRequestType> categoryFilter;
|
||||
private ComboBox<Status> stateFilter;
|
||||
private UUID selectedEmployeeId;
|
||||
|
||||
|
||||
public RequestsListView(final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService,
|
||||
final VacationService vacationService) {
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
this.vacationService = vacationService;
|
||||
this.employees = employeeService.findAllEmployees();
|
||||
initializeView();
|
||||
refreshGeneralRequestGrid(null, null, null);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
requestService.updateRequestStatuses();
|
||||
setupFilters();
|
||||
setupRequestGrid();
|
||||
add(requestGrid);
|
||||
add(createActionButtons());
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
add(createEmployeeFilter());
|
||||
add(createTeamFilter());
|
||||
add(createStateFilter());
|
||||
}
|
||||
|
||||
private void setupRequestGrid() {
|
||||
requestGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
|
||||
requestGrid.addColumn(this::getTeamName).setHeader("Equipo");
|
||||
requestGrid.addColumn(this::getEmployeeStatus).setHeader("Estado del empleado");
|
||||
requestGrid.addColumn(this::getGeneralTotal).setHeader("Total general");
|
||||
|
||||
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
requestGrid.setPageSize(5);
|
||||
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,
|
||||
final Team team,
|
||||
final Status state) {
|
||||
requestGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
int start = (int) (page * requestGrid.getPageSize());
|
||||
return fetchFilteredEmployees(start, pageSize, employee, team, state);
|
||||
});
|
||||
requestGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<Employee> fetchFilteredEmployees(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final Status state) {
|
||||
List<Employee> filteredEmployees = employeeService.findAllEmployees();
|
||||
|
||||
if (employee != null && !"TODOS".equals(employee.getFirstName())) {
|
||||
filteredEmployees = filteredEmployees.stream()
|
||||
.filter(emp -> emp.getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null && !"TODOS".equals(team.getName())) {
|
||||
filteredEmployees = filteredEmployees.stream()
|
||||
.filter(emp -> emp.getTeam() != null && emp.getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (state != null && state != Status.TODOS) {
|
||||
filteredEmployees = filteredEmployees.stream()
|
||||
.filter(emp -> {
|
||||
Optional<TimeOffRequest> request = requestService
|
||||
.findByEmployeeAndState(emp.getId(), TimeOffRequestStatus.EN_USO);
|
||||
return state == Status.EN_DESCANSO ? request.isPresent() : request.isEmpty();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredEmployees.size());
|
||||
return filteredEmployees.subList(start, end);
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final Employee employee) {
|
||||
return "TODOS".equals(employee.getFirstName())
|
||||
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private String getTeamName(final Employee employee) {
|
||||
Team team = employee.getTeam();
|
||||
return team != null ? team.getName() : "Sin asignar";
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return "TODOS".equals(team.getName()) ? "TODOS" : team.getName();
|
||||
}
|
||||
|
||||
private String getEmployeeStatus(final Employee employee) {
|
||||
Optional<TimeOffRequest> activeRequest = requestService
|
||||
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
|
||||
return activeRequest.isPresent() ? "EN_DESCANSO" : "ACTIVO";
|
||||
}
|
||||
|
||||
private String getGeneralTotal(final Employee employee) {
|
||||
List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
List<Vacation> vacations = vacationService.findVacations();
|
||||
|
||||
List<Double> vacationDays = calculateVacationDays(employee);
|
||||
|
||||
double utilizedVacationCurrentDays = vacationDays.get(1);
|
||||
List<TimeOffRequest> vacationCurrentRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.VACACION_GESTION_ACTUAL);
|
||||
if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) {
|
||||
utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance();
|
||||
}
|
||||
double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays);
|
||||
|
||||
double utilizedVacationPreviousDays = vacationDays.get(0);
|
||||
List<TimeOffRequest> vacationPreviousRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.VACACION_GESTION_ANTERIOR);
|
||||
if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) {
|
||||
utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance();
|
||||
}
|
||||
double totalVacationPreviousDays = vacationDays.getFirst()
|
||||
- (vacationDays.getFirst() - utilizedVacationPreviousDays);
|
||||
|
||||
|
||||
double totalUtilized = calculateTotalUtilized(employeeRequests);
|
||||
double totalVacations = totalVacationCurrentDays + totalVacationPreviousDays;
|
||||
double totalAvailable = calculateTotalAvailable(vacations, employeeRequests, employee);
|
||||
|
||||
double generalTotal = totalAvailable + totalVacations - totalUtilized;
|
||||
return String.valueOf(generalTotal);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getExcludedCategories() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.MATERNIDAD,
|
||||
TimeOffRequestType.PATERNIDAD,
|
||||
TimeOffRequestType.MATRIMONIO,
|
||||
TimeOffRequestType.DUELO_1ER_GRADO,
|
||||
TimeOffRequestType.DUELO_2ER_GRADO,
|
||||
TimeOffRequestType.DIA_DEL_PADRE,
|
||||
TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getGenderSpecificExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
|
||||
);
|
||||
}
|
||||
|
||||
private double calculateTotalUtilized(final List<TimeOffRequest> employeeRequests) {
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
return employeeRequests.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(request -> request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ACTUAL)
|
||||
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR)
|
||||
.filter(request -> request.getStartDate() != null && (
|
||||
request.getStartDate().getYear() == currentYear
|
||||
|| (request.getCategory().name().startsWith("VACACION")
|
||||
&& request.getStartDate().getYear() == currentYear - 1)
|
||||
))
|
||||
.mapToDouble(request -> request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0.0)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private List<Double> calculateVacationDays(final Employee employee) {
|
||||
List<Double> vacationDays = new ArrayList<>();
|
||||
|
||||
if (employee.getDateOfEntry() != null) {
|
||||
LocalDate entryDate = employee.getDateOfEntry();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue()
|
||||
|| (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth()
|
||||
<= today.getDayOfMonth());
|
||||
|
||||
LocalDate previousVacationYearDate;
|
||||
LocalDate currentVacationYearDate;
|
||||
|
||||
if (hasAnniversaryPassed) {
|
||||
previousVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 1,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
currentVacationYearDate = LocalDate.of(
|
||||
today.getYear(),
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
} else {
|
||||
previousVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 2,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
currentVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 1,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
}
|
||||
|
||||
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate));
|
||||
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate));
|
||||
} else {
|
||||
vacationDays.add(0.0);
|
||||
vacationDays.add(0.0);
|
||||
}
|
||||
return vacationDays;
|
||||
}
|
||||
|
||||
private double calculateTotalAvailable(final List<Vacation> vacations, final List<TimeOffRequest> employeeRequests,
|
||||
final Employee employee) {
|
||||
Set<TimeOffRequestType> excludedCategories = getExcludedCategories();
|
||||
Set<TimeOffRequestType> genderSpecificExclusions = getGenderSpecificExclusions();
|
||||
Set<TimeOffRequestType> employeeRequestCategories = employeeRequests.stream()
|
||||
.map(TimeOffRequest::getCategory)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
double healthLicence = 2;
|
||||
List<TimeOffRequest> healthRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.PERMISOS_DE_SALUD);
|
||||
if (healthRequests != null && !healthRequests.isEmpty()) {
|
||||
healthLicence = healthRequests.getLast().getDaysBalance();
|
||||
}
|
||||
|
||||
double totalAvailable = vacations.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(vacation -> vacation.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.filter(vacation -> shouldIncludeVacation(
|
||||
vacation,
|
||||
excludedCategories,
|
||||
genderSpecificExclusions,
|
||||
employee, employeeRequestCategories
|
||||
))
|
||||
.mapToDouble(vacation -> vacation.getDuration() != null ? vacation.getDuration() : 0.0)
|
||||
.sum();
|
||||
|
||||
return totalAvailable + healthLicence;
|
||||
}
|
||||
|
||||
private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) {
|
||||
int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0;
|
||||
if (yearsOfService > 10) {
|
||||
return 30;
|
||||
}
|
||||
if (yearsOfService > 5) {
|
||||
return 20;
|
||||
}
|
||||
if (yearsOfService > 1) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean shouldIncludeVacation(final Vacation vacation,
|
||||
final Set<TimeOffRequestType> excludedCategories,
|
||||
final Set<TimeOffRequestType> genderSpecificExclusions,
|
||||
final Employee employee,
|
||||
final Set<TimeOffRequestType> employeeRequestCategories) {
|
||||
if (excludedCategories.contains(vacation.getCategory())
|
||||
&& !employeeRequestCategories.contains(vacation.getCategory())) {
|
||||
return false;
|
||||
}
|
||||
if (!isFemale(employee) && genderSpecificExclusions.contains(vacation.getCategory())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isFemale(final Employee employee) {
|
||||
return employee.getGender() == Employee.Gender.FEMALE;
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
employees.addFirst(createAllEmployeesOption());
|
||||
employeeFilter.setItems(employees);
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestGrid(
|
||||
event.getValue(),
|
||||
teamFilter.getValue(),
|
||||
stateFilter.getValue()
|
||||
)
|
||||
);
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teams.addFirst(createAllTeamsOption());
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
teamFilter.setValue(teams.getFirst());
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestGrid(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue(),
|
||||
stateFilter.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Status> createStateFilter() {
|
||||
stateFilter = new ComboBox<>("Estado del empleado");
|
||||
stateFilter.setItems(Status.values());
|
||||
stateFilter.setValue(Status.values()[0]);
|
||||
stateFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestGrid(
|
||||
employeeFilter.getValue(),
|
||||
teamFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
return stateFilter;
|
||||
}
|
||||
|
||||
private enum Status {
|
||||
TODOS,
|
||||
EN_DESCANSO,
|
||||
ACTIVO
|
||||
}
|
||||
|
||||
private Employee createAllEmployeesOption() {
|
||||
Employee allEmployeesOption = new Employee();
|
||||
allEmployeesOption.setFirstName("TODOS");
|
||||
return allEmployeesOption;
|
||||
}
|
||||
|
||||
private Team createAllTeamsOption() {
|
||||
Team allTeamsOption = new Team();
|
||||
allTeamsOption.setName("TODOS");
|
||||
return allTeamsOption;
|
||||
}
|
||||
|
||||
private void navigateToMainView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(MainView.class));
|
||||
}
|
||||
|
||||
private void navigateToTimeOffRequestView(final UUID idEmployee) {
|
||||
getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString()));
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ package com.primefactorsolutions.views;
|
||||
import com.hilerio.ace.AceEditor;
|
||||
import com.hilerio.ace.AceMode;
|
||||
import com.hilerio.ace.AceTheme;
|
||||
import com.primefactorsolutions.model.Exam;
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import com.primefactorsolutions.model.Submission;
|
||||
import com.primefactorsolutions.service.ExamService;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.primefactorsolutions.service.CompilerService;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
@ -20,9 +20,9 @@ import com.vaadin.flow.component.menubar.MenuBarVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.*;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@ -37,17 +37,18 @@ import java.util.stream.Collectors;
|
||||
@PageTitle("Evaluacion")
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@Route(value = "/submissions", layout = MainLayout.class)
|
||||
@Route(value = "/submission", layout = MainLayout.class)
|
||||
@AnonymousAllowed
|
||||
@Slf4j
|
||||
public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
private final CompilerService compilerService;
|
||||
private final ExamService examService;
|
||||
private final AssessmentService assessmentService;
|
||||
|
||||
private AceEditor questionEditor = null;
|
||||
private AceEditor result = null;
|
||||
private Dialog dialog = null;
|
||||
private Exam exam = null;
|
||||
private Assessment assessment = null;
|
||||
private Submission currSubmission = null;
|
||||
private MenuItem prev = null;
|
||||
private MenuItem next = null;
|
||||
@ -56,9 +57,9 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
private Section editorSection = null;
|
||||
private H3 questionTitle = null;
|
||||
|
||||
public SubmissionView(final CompilerService compilerService, final ExamService examService) {
|
||||
public SubmissionView(final CompilerService compilerService, final AssessmentService assessmentService) {
|
||||
this.compilerService = compilerService;
|
||||
this.examService = examService;
|
||||
this.assessmentService = assessmentService;
|
||||
|
||||
addClassNames(Display.FLEX, Flex.GROW, Height.FULL);
|
||||
|
||||
@ -130,13 +131,13 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
prev = navMenuBar.addItem("Anterior pregunta",
|
||||
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
log.info(">>> prev");
|
||||
this.currSubmission = this.examService.getPrevSubmission(exam.getId(),
|
||||
this.currSubmission = this.assessmentService.getPrevSubmission(assessment.getId(),
|
||||
this.currSubmission);
|
||||
updateUI();
|
||||
});
|
||||
next = navMenuBar.addItem("Siguiente pregunta",
|
||||
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
goToNext();
|
||||
});
|
||||
|
||||
@ -185,7 +186,7 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
private void goToNext() {
|
||||
log.info(">>> next");
|
||||
Submission found = this.examService.getNextSubmission(exam.getId(),
|
||||
Submission found = this.assessmentService.getNextSubmission(assessment.getId(),
|
||||
this.currSubmission.getId(), false);
|
||||
|
||||
if (found != null) {
|
||||
@ -208,25 +209,25 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if (exam == null || !exam.isStarted()) {
|
||||
if (assessment == null || !assessment.isStarted()) {
|
||||
editorSection.setVisible(false);
|
||||
sidebar.setVisible(false);
|
||||
} else {
|
||||
if (currSubmission != null) {
|
||||
questionEditor.setValue(this.currSubmission.getText());
|
||||
questionEditor.setValue(this.currSubmission.getResponse());
|
||||
questionTitle.setText(this.currSubmission.getQuestion().getTitle());
|
||||
}
|
||||
|
||||
editorSection.setVisible(true);
|
||||
sidebar.setVisible(true);
|
||||
|
||||
prev.setEnabled(currSubmission != null && !exam.isFirst(currSubmission));
|
||||
next.setEnabled(currSubmission == null || !exam.isLast(currSubmission));
|
||||
prev.setEnabled(currSubmission != null && !assessment.isFirst(currSubmission));
|
||||
next.setEnabled(currSubmission == null || !assessment.isLast(currSubmission));
|
||||
|
||||
if (dl.getChildren().collect(Collectors.toList()).isEmpty()) {
|
||||
dl.add(
|
||||
createItem("Candidato:", exam.getCandidate().getEmail()),
|
||||
createItem("Hora de inicio:", Optional.ofNullable(exam.getStartingTime())
|
||||
createItem("Candidato:", assessment.getCandidate().getEmail()),
|
||||
createItem("Hora de inicio:", Optional.ofNullable(assessment.getStartingTime())
|
||||
.map(t -> ZonedDateTime.ofInstant(t,
|
||||
ZoneId.of("GMT-4")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
|
||||
.orElse("N/A"))
|
||||
@ -258,14 +259,14 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
this.exam = this.examService.getExam(UUID.fromString(s));
|
||||
this.assessment = this.assessmentService.getAssessment(UUID.fromString(s));
|
||||
|
||||
if (this.exam == null) {
|
||||
if (this.assessment == null) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
this.currSubmission = this.exam.isStarted()
|
||||
? this.examService.getNextSubmission(exam.getId(), null, false)
|
||||
this.currSubmission = this.assessment.isStarted()
|
||||
? this.assessmentService.getNextSubmission(assessment.getId(), null, false)
|
||||
: null;
|
||||
|
||||
updateUI();
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Timeoff")
|
||||
@Route(value = "/timeoffs/me", layout = MainLayout.class)
|
||||
public class TimeoffView extends Main {
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Timesheets")
|
||||
@Route(value = "/timesheets", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class TimesheestReportView extends Main {
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Timesheet")
|
||||
@Route(value = "/timesheets/me", layout = MainLayout.class)
|
||||
public class TimesheetView extends Main {
|
||||
}
|
||||
|
@ -1,135 +0,0 @@
|
||||
package com.primefactorsolutions.views.admin;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.TimeOffService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
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.ButtonVariant;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
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.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Registro de Vacaciones")
|
||||
@Route(value = "/time-off/list", layout = MainLayout.class)
|
||||
public class TimeOffListView extends BaseView {
|
||||
|
||||
private final TimeOffService timeOffService;
|
||||
private final VGrid<TimeOff> timeOffGrid = new VGrid<>();
|
||||
private ComboBox<Integer> yearFilter;
|
||||
|
||||
public TimeOffListView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffService timeOffService) {
|
||||
super(authenticationContext);
|
||||
this.timeOffService = timeOffService;
|
||||
|
||||
initializeView();
|
||||
}
|
||||
|
||||
private void refreshGridListHoursWorked(final Integer year) {
|
||||
final List<TimeOff> timeOffs = timeOffService.findTimeOffs(year);
|
||||
timeOffGrid.setDataProvider(new ListDataProvider<>(timeOffs));
|
||||
timeOffGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
getCurrentPageLayout().add(createAddTimeOff());
|
||||
setupFilters();
|
||||
setupListHoursWorkedGrid();
|
||||
getCurrentPageLayout().add(timeOffGrid);
|
||||
refreshGridListHoursWorked(yearFilter.getValue());
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createYearFilter());
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private ComboBox<Integer> createYearFilter() {
|
||||
yearFilter = new ComboBox<>("Year");
|
||||
final int nowYear = LocalDate.now().getYear();
|
||||
final List<Integer> years = IntStream.range(0, 2).mapToObj(y -> nowYear - y).toList();
|
||||
|
||||
yearFilter.setItems(years);
|
||||
yearFilter.setValue(years.getFirst());
|
||||
yearFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(event.getValue())
|
||||
);
|
||||
|
||||
return yearFilter;
|
||||
}
|
||||
|
||||
private void setupListHoursWorkedGrid() {
|
||||
timeOffGrid.addColumn(e -> e.getCategory().name())
|
||||
.setHeader("Categoria");
|
||||
timeOffGrid.addColumn(e -> e.getType().name())
|
||||
.setHeader("Tipo");
|
||||
timeOffGrid.addColumn(TimeOff::getDate)
|
||||
.setHeader("Fecha");
|
||||
timeOffGrid.addColumn(TimeOff::getDuration).setHeader("Duracion");
|
||||
timeOffGrid.addColumn(TimeOff::getExpiration).setHeader("Expiracion");
|
||||
timeOffGrid.addComponentColumn((ValueProvider<TimeOff, Component>) timeOff -> {
|
||||
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 -> {
|
||||
navigateToTimeOffView(timeOff.getId(), "view");
|
||||
});
|
||||
final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Editar");
|
||||
editItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
navigateToTimeOffView(timeOff.getId(), "edit");
|
||||
});
|
||||
return menuBar;
|
||||
});
|
||||
}
|
||||
|
||||
private void navigateToTimeOffView(final UUID idRecord, final String action) {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffView.class, idRecord.toString() + "/" + action));
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
|
||||
final Button button = new Button(label);
|
||||
|
||||
if (isPrimary) {
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
}
|
||||
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddTimeOff() {
|
||||
return createButton("Agregar Vacacion", this::navigateToTimeOff, true);
|
||||
}
|
||||
|
||||
private void navigateToTimeOff() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffView.class, "new"));
|
||||
}
|
||||
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
package com.primefactorsolutions.views.admin;
|
||||
|
||||
import com.primefactorsolutions.model.TimeOff;
|
||||
import com.primefactorsolutions.model.TimeOffRequestType;
|
||||
import com.primefactorsolutions.service.TimeOffService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.datepicker.VDatePicker;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Vacaciones")
|
||||
@Route(value = "/time-off/:hours-workedId?/:action?", layout = MainLayout.class)
|
||||
public class TimeOffView extends BaseEntityForm<TimeOff> implements HasUrlParameter<String> {
|
||||
private final ComboBox<TimeOffRequestType> category = new ComboBox<>("Categoria");
|
||||
private final ComboBox<TimeOff.Type> type = new ComboBox<>("Tipo");
|
||||
private final VDatePicker date = new VDatePicker("Fecha");
|
||||
private final NumberField duration = new NumberField("Duracion");
|
||||
private final NumberField expiration = new NumberField("Expiracion");
|
||||
|
||||
private final TimeOffService timeOffService;
|
||||
|
||||
public TimeOffView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffService timeOffService) {
|
||||
super(authenticationContext, TimeOff.class);
|
||||
this.timeOffService = timeOffService;
|
||||
|
||||
initializeDateField();
|
||||
category.setItems(TimeOffRequestType.values());
|
||||
type.setItems(TimeOff.Type.values());
|
||||
|
||||
this.setSavedHandler(this::saveTimeOff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
final String s = params.get("hours-workedId").orElse(null);
|
||||
|
||||
if ("new".equals(action) || s == null) {
|
||||
setEntityWithEnabledSave(new TimeOff());
|
||||
} else {
|
||||
final UUID timeOffId = UUID.fromString(s);
|
||||
final TimeOff timeOff = timeOffService.getTimeOff(timeOffId);
|
||||
|
||||
if ("edit".equals(action) && !s.isEmpty()) {
|
||||
setEntityWithEnabledSave(timeOff);
|
||||
} else if ("view".equals(action) && !s.isEmpty()) {
|
||||
setEntity(timeOff);
|
||||
duration.setReadOnly(true);
|
||||
expiration.setReadOnly(true);
|
||||
category.setReadOnly(true);
|
||||
type.setReadOnly(true);
|
||||
date.setReadOnly(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
category,
|
||||
type,
|
||||
date,
|
||||
duration,
|
||||
expiration
|
||||
);
|
||||
}
|
||||
|
||||
private void initializeDateField() {
|
||||
final LocalDate today = LocalDate.now();
|
||||
final YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth());
|
||||
final LocalDate startOfMonth = currentMonth.atDay(1);
|
||||
|
||||
date.setWidthFull();
|
||||
date.setMin(startOfMonth);
|
||||
date.setMax(today);
|
||||
date.setValue(today);
|
||||
}
|
||||
|
||||
private void saveTimeOff(final TimeOff timeOff) {
|
||||
if (isFormValid()) {
|
||||
timeOffService.saveTimeOff(timeOff);
|
||||
closeForm();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffListView.class));
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return date.getValue() != null;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.Candidate;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Candidates")
|
||||
@Route(value = "/candidates", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class CandidatesListView extends BaseView {
|
||||
|
||||
public CandidatesListView(final AuthenticationContext authenticationContext,
|
||||
final CandidateService candidateService) {
|
||||
super(authenticationContext);
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addCandidate = new Button("Add Candidate");
|
||||
addCandidate.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, "new"));
|
||||
});
|
||||
hl.add(addCandidate);
|
||||
|
||||
final VGrid<Candidate> grid = new VGrid<>(Candidate.class);
|
||||
grid.setColumns("email");
|
||||
grid.setAllRowsVisible(true);
|
||||
grid.addComponentColumn(candidate ->
|
||||
MenuBarUtils.menuBar(Map.of(Pair.of("Edit", VaadinIcon.PENCIL), menuItemClickEvent ->
|
||||
getUI().flatMap(ui -> ui.navigate(CandidateView.class, candidate.getId().toString())))));
|
||||
grid.setDataProvider(new ListDataProvider<>(candidateService.getCandidates()));
|
||||
|
||||
getCurrentPageLayout().add(hl, grid);
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.EvaluationService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.EntityComboBox;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.Text;
|
||||
import com.vaadin.flow.component.textfield.IntegerField;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.fields.ElementCollectionField;
|
||||
import org.vaadin.firitin.fields.EnumSelect;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Evaluations")
|
||||
@Route(value = "/evaluations", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class EvaluationView extends BaseEntityForm<Evaluation> implements HasUrlParameter<String> {
|
||||
private final CandidateService candidateService;
|
||||
private final EvaluationService evaluationService;
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
private final EntityComboBox<Candidate> candidate = new EntityComboBox<>("Candidate");
|
||||
private final EntityComboBox<Employee> interviewer = new EntityComboBox<>("Interviewer");
|
||||
private final EnumSelect<EmployeePosition> candidatePosition =
|
||||
new EnumSelect<>("Position", EmployeePosition.class);
|
||||
private final IntegerField points = new IntegerField("Points");
|
||||
private final Text skillLabel = new Text("Skills");
|
||||
private final ElementCollectionField<SkillEvaluation> skillEvaluations =
|
||||
new ElementCollectionField<>(SkillEvaluation.class);
|
||||
|
||||
public EvaluationView(final AuthenticationContext authenticationContext,
|
||||
final CandidateService candidateService,
|
||||
final EmployeeService employeeService,
|
||||
final EvaluationService evaluationService) {
|
||||
super(authenticationContext, Evaluation.class);
|
||||
this.employeeService = employeeService;
|
||||
this.evaluationService = evaluationService;
|
||||
this.candidateService = candidateService;
|
||||
|
||||
this.candidate.setWidthFull();
|
||||
this.interviewer.setWidthFull();
|
||||
this.points.setWidthFull();
|
||||
this.skillEvaluations.setWidthFull();
|
||||
this.candidatePosition.setWidthFull();
|
||||
this.candidate.setItems(this.candidateService.getCandidates());
|
||||
this.interviewer.setItems(this.employeeService.getEmployees());
|
||||
|
||||
setSavedHandler(evaluation -> goTo(EvaluationsListView.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
|
||||
var evaluation = evaluationService.getEvaluation(UUID.fromString(s));
|
||||
setEntityWithEnabledSave(evaluation);
|
||||
} else {
|
||||
setEntityWithEnabledSave(new Evaluation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
candidate,
|
||||
points,
|
||||
candidatePosition,
|
||||
skillLabel,
|
||||
skillEvaluations,
|
||||
interviewer
|
||||
);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.Evaluation;
|
||||
import com.primefactorsolutions.service.EvaluationService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Evaluations")
|
||||
@Route(value = "/evaluations", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class EvaluationsListView extends BaseView {
|
||||
|
||||
public EvaluationsListView(final AuthenticationContext authenticationContext,
|
||||
final EvaluationService evaluationService) {
|
||||
super(authenticationContext);
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addEvaluation = new Button("Add Evaluation");
|
||||
addEvaluation.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
addEvaluation.addClickListener(buttonClickEvent -> {
|
||||
this.getUI().flatMap(ui -> ui.navigate(EvaluationView.class, "new"));
|
||||
});
|
||||
hl.add(addEvaluation);
|
||||
|
||||
final VGrid<Evaluation> grid = new VGrid<>(Evaluation.class);
|
||||
grid.setColumns("candidate.email");
|
||||
grid.setAllRowsVisible(true);
|
||||
grid.addComponentColumn(evaluation ->
|
||||
MenuBarUtils.menuBar(Map.of(Pair.of("Edit", VaadinIcon.PENCIL), menuItemClickEvent ->
|
||||
getUI().flatMap(ui -> ui.navigate(EvaluationView.class, evaluation.getId().toString())))));
|
||||
grid.setDataProvider(new ListDataProvider<>(evaluationService.getEvaluations()));
|
||||
|
||||
getCurrentPageLayout().add(hl, grid);
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.Exam;
|
||||
import com.primefactorsolutions.service.ExamService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.SubmissionView;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.addon.stefan.clipboard.ClientsideClipboard;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Exams")
|
||||
@Route(value = "/exams", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class ExamsListView extends BaseView {
|
||||
|
||||
public ExamsListView(final AuthenticationContext authenticationContext,
|
||||
final ExamService examService) {
|
||||
super(authenticationContext);
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addExam = new Button("Add Exam");
|
||||
addExam.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
addExam.addClickListener(buttonClickEvent ->
|
||||
getUI().flatMap(ui -> ui.navigate(ExamView.class, "new")));
|
||||
hl.add(addExam);
|
||||
|
||||
final VGrid<Exam> grid = new VGrid<>(Exam.class);
|
||||
grid.setColumns("id", "candidate.email");
|
||||
final Grid.Column<Exam> statusColumn = grid.addColumn(exam ->
|
||||
exam.getExamEvents().isEmpty()
|
||||
? "N/A"
|
||||
: exam.getExamEvents().getLast().getStatus().name());
|
||||
statusColumn.setHeader("Status");
|
||||
|
||||
grid.addComponentColumn(exam -> MenuBarUtils.menuBar(
|
||||
Pair.of("View", __ ->
|
||||
getUI().flatMap(ui -> ui.navigate(SubmissionView.class, exam.getId().toString()))),
|
||||
Pair.of("Copy", __ ->
|
||||
ClientsideClipboard.writeToClipboard(
|
||||
String.format("email: %s link: "
|
||||
+ "https://intra.primefactorsolutions.com/candidate-exam/%s",
|
||||
exam.getCandidate().getEmail(),
|
||||
exam.getId()))),
|
||||
Pair.of("Email", __ -> {
|
||||
ConfirmDialog dialog = new ConfirmDialog();
|
||||
dialog.setHeader("Send Link Email");
|
||||
dialog.setText(String.format("Enviar link por email al candidato %s?",
|
||||
exam.getCandidate().getEmail()));
|
||||
dialog.setCancelable(true);
|
||||
dialog.setConfirmText("Enviar");
|
||||
dialog.setConfirmButtonTheme("primary");
|
||||
dialog.addConfirmListener(confirmEvent -> {
|
||||
try {
|
||||
examService.sendEmail(exam);
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error sending email: " + e.getMessage(), 10_000,
|
||||
Notification.Position.TOP_CENTER);
|
||||
}
|
||||
});
|
||||
dialog.open();
|
||||
})
|
||||
));
|
||||
|
||||
grid.setDataProvider(new ListDataProvider<>(examService.getExams()));
|
||||
grid.setAllRowsVisible(true);
|
||||
|
||||
getCurrentPageLayout().add(hl, grid);
|
||||
}
|
||||
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.Question;
|
||||
import com.primefactorsolutions.service.QuestionService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Questions")
|
||||
@Route(value = "/questions", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class QuestionsListView extends BaseView {
|
||||
|
||||
public QuestionsListView(final AuthenticationContext authenticationContext, final QuestionService questionService) {
|
||||
super(authenticationContext);
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addQuestion = new Button("Add Question");
|
||||
addQuestion.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
|
||||
this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, "new")));
|
||||
hl.add(addQuestion);
|
||||
|
||||
final VGrid<Question> grid = new VGrid<>(Question.class);
|
||||
grid.setColumns("title", "description", "timeMinutes");
|
||||
grid.addComponentColumn(question ->
|
||||
MenuBarUtils.menuBar(Pair.of("Edit", __ ->
|
||||
getUI().flatMap(ui -> ui.navigate(QuestionView.class, question.getId().toString())))));
|
||||
grid.setDataProvider(new ListDataProvider<>(questionService.getQuestions()));
|
||||
grid.setAllRowsVisible(true);
|
||||
|
||||
getCurrentPageLayout().add(hl, grid);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.ReportService;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.html.Anchor;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.UUID;
|
||||
@PermitAll
|
||||
@PageTitle("Reporte excel")
|
||||
@Route("employee-report")
|
||||
public class EmployeeReportView extends VerticalLayout implements HasUrlParameter<String> {
|
||||
private final EmployeeService employeeService;
|
||||
private final ReportService reportService;
|
||||
|
||||
public EmployeeReportView(final EmployeeService employeeService, final ReportService reportService) {
|
||||
this.employeeService = employeeService;
|
||||
this.reportService = reportService;
|
||||
Button backButton = new Button("Volver al Reporte de Empleados", event ->
|
||||
UI.getCurrent().navigate(EmployeesListView.class));
|
||||
backButton.addClassName("back-button");
|
||||
add(backButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent event, @OptionalParameter final String employeeId) {
|
||||
if (employeeId != null) {
|
||||
UUID id = UUID.fromString(employeeId);
|
||||
Employee employee = employeeService.getEmployee(id);
|
||||
if (employee != null) {
|
||||
generateExcelReport(employee);
|
||||
} else {
|
||||
Notification.show("Empleado no encontrado", 3000, Notification.Position.MIDDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateExcelReport(final Employee employee) {
|
||||
try {
|
||||
byte[] excelContent = reportService.generateExcelReport(employee);
|
||||
StreamResource resource = new StreamResource(
|
||||
employee.getFirstName() + "_" + employee.getLastName() + "_report.xlsx",
|
||||
() -> new ByteArrayInputStream(excelContent)
|
||||
);
|
||||
Anchor downloadLink = new Anchor(resource, "Descargar Reporte Excel");
|
||||
downloadLink.getElement().setAttribute("download", true);
|
||||
add(downloadLink);
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error al generar el reporte Excel", 3000, Notification.Position.MIDDLE);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,206 +0,0 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.Constants;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.html.Anchor;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
import com.vaadin.flow.component.grid.GridSortOrder;
|
||||
import com.vaadin.flow.data.provider.SortDirection;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Employees")
|
||||
@Route(value = "/employees", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@Slf4j
|
||||
public class EmployeesListView extends BaseView {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
private final PagingGrid<Employee> employeePagingGrid = new PagingGrid<>(Employee.class);
|
||||
|
||||
public EmployeesListView(final AuthenticationContext authenticationContext, final EmployeeService employeeService) {
|
||||
super(authenticationContext);
|
||||
this.employeeService = employeeService;
|
||||
setupView();
|
||||
refreshGrid();
|
||||
}
|
||||
|
||||
private void setupView() {
|
||||
configureTable();
|
||||
final HorizontalLayout hl = new HorizontalLayout(createAddEmployeeButton(), createExportButton());
|
||||
getCurrentPageLayout().add(hl);
|
||||
getCurrentPageLayout().add(employeePagingGrid);
|
||||
}
|
||||
|
||||
private Button createExportButton() {
|
||||
final StreamResource excelResource = new StreamResource("employees.xlsx", this::generateExcel);
|
||||
final Anchor downloadLink = new Anchor(excelResource, "Export Employees");
|
||||
downloadLink.getElement().setAttribute("download", true); // Forzar descarga
|
||||
|
||||
return new Button("Exportar como Excel", e -> getCurrentPageLayout().add(downloadLink));
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generateExcel() {
|
||||
final List<Employee> employees = employeeService.findAllEmployees();
|
||||
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
Sheet sheet = workbook.createSheet("Employees");
|
||||
Row headerRow = sheet.createRow(0);
|
||||
String[] headers = {
|
||||
"ID", "Nombres", "Apellidos", "Status", "Genero", "Fecha de Nacimiento", "Edad",
|
||||
"Ciudad y Pais de Nacimiento", "Dirección de Residencia", "Departamento y Provincia de Residencia",
|
||||
"Marital Status",
|
||||
"Numero de Hijos", "CI", "Expedido en", "Teléfono", "E-mail",
|
||||
"Teléfono Laboral", "E-mail Laboral", "Codigo de Empleado", "Cargo",
|
||||
"Equipo", "Lead/Manager", "Fecha de Ingreso", "Fecha de Retiro", "Tipo de Contrato",
|
||||
"Otro Tipo de Contrato", "Antiguedad", "Salario Total"
|
||||
};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
style.setFont(font);
|
||||
cell.setCellStyle(style);
|
||||
}
|
||||
int rowIndex = 1;
|
||||
for (Employee employee : employees) {
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
row.createCell(0).setCellValue(employee.getId().toString());
|
||||
row.createCell(1).setCellValue(employee.getFirstName());
|
||||
row.createCell(2).setCellValue(employee.getLastName());
|
||||
row.createCell(3).setCellValue(employee.getStatus().toString());
|
||||
row.createCell(4).setCellValue(employee.getGender() != null ? employee.getGender().toString() : "");
|
||||
row.createCell(5).setCellValue(employee.getBirthday() != null ? employee.getBirthday()
|
||||
.toString() : "");
|
||||
row.createCell(6).setCellValue(employee.getAge() != null ? employee.getAge() : "");
|
||||
row.createCell(7).setCellValue(employee.getBirthCity() != null ? employee.getBirthCity() : "");
|
||||
row.createCell(8).setCellValue(employee.getResidenceAddress() != null ? employee
|
||||
.getResidenceAddress() : "");
|
||||
row.createCell(9).setCellValue(employee.getLocalAddress() != null ? employee.getLocalAddress() : "");
|
||||
row.createCell(10).setCellValue(employee.getMaritalStatus() != null ? employee.getMaritalStatus()
|
||||
.toString() : "");
|
||||
row.createCell(11).setCellValue(employee.getNumberOfChildren() != null ? employee
|
||||
.getNumberOfChildren() : "");
|
||||
row.createCell(12).setCellValue(employee.getCi() != null ? employee.getCi() : "");
|
||||
row.createCell(13).setCellValue(employee.getIssuedIn() != null ? employee.getIssuedIn() : "");
|
||||
row.createCell(14).setCellValue(employee.getPhoneNumber() != null ? employee.getPhoneNumber() : "");
|
||||
row.createCell(15).setCellValue(employee.getPersonalEmail() != null ? employee
|
||||
.getPersonalEmail() : "");
|
||||
row.createCell(16).setCellValue(employee.getPhoneNumberProfessional() != null ? employee
|
||||
.getPhoneNumberProfessional() : "");
|
||||
row.createCell(17).setCellValue(employee.getProfessionalEmail() != null ? employee
|
||||
.getProfessionalEmail() : "");
|
||||
row.createCell(18).setCellValue(employee.getCod() != null ? employee.getCod() : "");
|
||||
row.createCell(19).setCellValue(employee.getPosition() != null ? employee.getPosition() : "");
|
||||
row.createCell(20).setCellValue(employee.getTeam() != null ? employee.getTeam().getName() : "");
|
||||
row.createCell(21).setCellValue(employee.getLeadManager() != null ? employee.getLeadManager() : "");
|
||||
row.createCell(22).setCellValue(employee.getDateOfEntry() != null ? employee.getDateOfEntry()
|
||||
.toString() : "");
|
||||
row.createCell(23).setCellValue(employee.getDateOfExit() != null ? employee.getDateOfExit()
|
||||
.toString() : "");
|
||||
row.createCell(24).setCellValue(employee.getContractType() != null ? employee.getContractType()
|
||||
.toString() : "");
|
||||
row.createCell(25).setCellValue(employee.getCustomContractType() != null
|
||||
? employee.getCustomContractType()
|
||||
: "");
|
||||
row.createCell(26).setCellValue(employee.getSeniority() != null ? employee.getSeniority() : "");
|
||||
row.createCell(27).setCellValue(employee.getSalaryTotal() != null
|
||||
? employee.getSalaryTotal().toString()
|
||||
: "");
|
||||
}
|
||||
workbook.write(out);
|
||||
return new ByteArrayInputStream(out.toByteArray());
|
||||
} catch (IOException e) {
|
||||
log.error("Error generating excel", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void configureTable() {
|
||||
employeePagingGrid.setColumns("username", "firstName", "lastName", "status");
|
||||
employeePagingGrid.addComponentColumn(employee -> MenuBarUtils.menuBar(
|
||||
Pair.of("View", __ -> navigateToEmployeeView(employee)),
|
||||
Pair.of("Edit", __ -> navigateToEditView(employee))));
|
||||
setupPagingGrid();
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
|
||||
final Button button = new Button(label);
|
||||
|
||||
if (isPrimary) {
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
}
|
||||
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddEmployeeButton() {
|
||||
return createButton("Add Employee", this::navigateToAddEmployeeView, true);
|
||||
}
|
||||
|
||||
private void navigateToEditView(final Employee employee) {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/edit"));
|
||||
}
|
||||
|
||||
private void navigateToEmployeeView(final Employee employee) {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/view"));
|
||||
}
|
||||
|
||||
private void navigateToAddEmployeeView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, "new"));
|
||||
}
|
||||
|
||||
private void setupPagingGrid() {
|
||||
employeePagingGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
employeePagingGrid.setPageSize(Constants.PAGE_SIZE);
|
||||
}
|
||||
|
||||
private void refreshGrid() {
|
||||
employeePagingGrid.setPagingDataProvider((page, pageSize) -> fetchEmployees((int) page, pageSize));
|
||||
}
|
||||
|
||||
private List<Employee> fetchEmployees(final int page, final int pageSize) {
|
||||
int start = page * pageSize;
|
||||
if (hasSortOrder()) {
|
||||
return fetchSortedEmployees(start, pageSize);
|
||||
}
|
||||
return employeeService.findEmployees(start, pageSize);
|
||||
}
|
||||
|
||||
private boolean hasSortOrder() {
|
||||
return !employeePagingGrid.getSortOrder().isEmpty();
|
||||
}
|
||||
|
||||
private List<Employee> fetchSortedEmployees(final int start, final int pageSize) {
|
||||
final GridSortOrder<Employee> sortOrder = employeePagingGrid.getSortOrder().getFirst();
|
||||
|
||||
return employeeService.findEmployees(start, pageSize,
|
||||
sortOrder.getSorted().getKey(),
|
||||
sortOrder.getDirection() == SortDirection.ASCENDING);
|
||||
}
|
||||
}
|
@ -1,525 +0,0 @@
|
||||
package com.primefactorsolutions.views.timeoff;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.service.TimeOffService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.primefactorsolutions.views.util.ComponentUtils.withFullWidth;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Request")
|
||||
@Route(value = "/requests/:requestId?/:action?", layout = MainLayout.class)
|
||||
public class TimeOffRequestView extends BaseEntityForm<TimeOffRequest> implements HasUrlParameter<String> {
|
||||
|
||||
private final ComboBox<TimeOffRequestType> category = withFullWidth(new ComboBox<>("Categoría"));
|
||||
private final ComboBox<TimeOffRequestStatus> state = withFullWidth(new ComboBox<>("Estado de la solicitud"));
|
||||
private final DatePicker expiration = withFullWidth(new DatePicker("Vencimiento"));
|
||||
private final DatePicker startDate = withFullWidth(new DatePicker("Fecha de inicio"));
|
||||
private final DatePicker endDate = withFullWidth(new DatePicker("Fecha de fin"));
|
||||
private final NumberField daysToBeTake = withFullWidth(new NumberField("Días a tomar"));
|
||||
private final NumberField daysBalance = withFullWidth(new NumberField("Días disponibles"));
|
||||
|
||||
private final TimeOffService timeOffService;
|
||||
private final TimeOffRequestService timeOffRequestService;
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
private Employee employee;
|
||||
|
||||
public TimeOffRequestView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffRequestService timeOffRequestService,
|
||||
final TimeOffService timeOffService,
|
||||
final EmployeeService employeeService) {
|
||||
super(authenticationContext, TimeOffRequest.class);
|
||||
this.timeOffService = timeOffService;
|
||||
this.timeOffRequestService = timeOffRequestService;
|
||||
this.employeeService = employeeService;
|
||||
state.setItems(List.of(TimeOffRequestStatus.values()));
|
||||
|
||||
this.setSavedHandler(this::saveRequest);
|
||||
|
||||
initView();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
category.addValueChangeListener(event -> {
|
||||
onCategoryChange(event.getValue());
|
||||
handleCategorySelection(event.getValue());
|
||||
});
|
||||
startDate.addValueChangeListener(event -> {
|
||||
LocalDate selectedDate = event.getValue();
|
||||
if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6
|
||||
|| selectedDate.getDayOfWeek().getValue() == 7)) {
|
||||
startDate.setValue(selectedDate.minusDays(1));
|
||||
}
|
||||
updateDatePickerMinValues();
|
||||
});
|
||||
endDate.addValueChangeListener(event -> {
|
||||
if (endDate.getValue() != null) {
|
||||
endDate.setMin(endDate.getValue());
|
||||
}
|
||||
final LocalDate selectedDate = event.getValue();
|
||||
|
||||
if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6
|
||||
|| selectedDate.getDayOfWeek().getValue() == 7)) {
|
||||
endDate.setValue(selectedDate.minusDays(1));
|
||||
}
|
||||
calculateDays();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
final String requestIdString = params.get("requestId").orElse(null);
|
||||
final UUID employeeId = getEmployeeId().get();
|
||||
employee = employeeService.getEmployee(employeeId);
|
||||
filterCategories(employee);
|
||||
|
||||
if ("new".equals(action)) {
|
||||
setEntityWithEnabledSave(new TimeOffRequest());
|
||||
} else {
|
||||
assert requestIdString != null;
|
||||
UUID requestId = UUID.fromString(requestIdString);
|
||||
TimeOffRequest timeOffRequest = timeOffRequestService.findTimeOffRequest(requestId);
|
||||
|
||||
if ("edit".equals(action)) {
|
||||
setEntityWithEnabledSave(timeOffRequest);
|
||||
setFieldsReadOnly(false);
|
||||
} else if ("view".equals(action)) {
|
||||
setEntity(timeOffRequest);
|
||||
setFieldsReadOnly(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
category,
|
||||
state,
|
||||
expiration,
|
||||
startDate,
|
||||
endDate,
|
||||
daysBalance,
|
||||
daysToBeTake
|
||||
);
|
||||
}
|
||||
|
||||
private void filterCategories(final Employee employee) {
|
||||
category.clear();
|
||||
List<TimeOffRequest> employeeRequests = timeOffRequestService.findRequestsByEmployeeId(employee.getId());
|
||||
List<TimeOffRequestType> allCategories = Arrays.asList(TimeOffRequestType.values());
|
||||
List<TimeOffRequestType> availableCategories = allCategories.stream()
|
||||
.filter(category -> isCategoryAvailable(employeeRequests, category))
|
||||
.filter(category -> isCategoryAllowedByGender(category, employee.getGender()))
|
||||
.filter(category -> shouldIncludeVacationGestionActual(employeeRequests, category))
|
||||
.filter(category -> shouldIncludeVacationGestionAnterior(employeeRequests, category))
|
||||
.toList();
|
||||
|
||||
category.setItems(availableCategories);
|
||||
}
|
||||
|
||||
private boolean shouldIncludeVacationGestionActual(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category != TimeOffRequestType.VACACION_GESTION_ACTUAL) {
|
||||
return true;
|
||||
}
|
||||
return employeeRequests.stream()
|
||||
.anyMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
&& request.getDaysBalance() == 0
|
||||
&& request.getState() == TimeOffRequestStatus.TOMADO);
|
||||
}
|
||||
|
||||
private boolean shouldIncludeVacationGestionAnterior(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category != TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
return true;
|
||||
}
|
||||
return employeeRequests.stream()
|
||||
.noneMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
&& request.getDaysBalance() == 0);
|
||||
}
|
||||
|
||||
private void onCategoryChange(final TimeOffRequestType selectedCategory) {
|
||||
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
startDate.setReadOnly(false);
|
||||
endDate.setReadOnly(false);
|
||||
} else {
|
||||
startDate.setReadOnly(false);
|
||||
endDate.setReadOnly(true);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCategoryAvailable(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<TimeOffRequest> requestsByCategory = employeeRequests.stream()
|
||||
.filter(request -> request.getCategory() == category)
|
||||
.toList();
|
||||
|
||||
if (requestsByCategory.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TimeOffRequest latestRequest = requestsByCategory.stream()
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.orElse(null);
|
||||
|
||||
boolean isSpecialCategory = category == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| category == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| category == TimeOffRequestType.VACACION_GESTION_ANTERIOR;
|
||||
|
||||
if (isSpecialCategory) {
|
||||
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getDaysBalance() > 0)
|
||||
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO
|
||||
|| (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getDaysBalance() == 0
|
||||
&& latestRequest.getExpiration().isBefore(LocalDate.now()));
|
||||
} else {
|
||||
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getExpiration().isBefore(LocalDate.now()))
|
||||
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCategoryAllowedByGender(final TimeOffRequestType category, final Employee.Gender gender) {
|
||||
if (gender == Employee.Gender.MALE) {
|
||||
return category != TimeOffRequestType.MATERNIDAD
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL;
|
||||
} else {
|
||||
return category != TimeOffRequestType.DIA_DEL_PADRE
|
||||
&& category != TimeOffRequestType.PATERNIDAD;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCategorySelection(final TimeOffRequestType selectedCategory) {
|
||||
if (selectedCategory != null) {
|
||||
updateAvailableDays(selectedCategory);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAvailableDays(final TimeOffRequestType selectedCategory) {
|
||||
final TimeOff timeoff = timeOffService.findVacationByCategory(selectedCategory);
|
||||
final List<TimeOffRequest> requests =
|
||||
timeOffRequestService.findByEmployeeAndCategory(employee.getId(), selectedCategory);
|
||||
|
||||
final TimeOffRequest requestWithBalance = requests.stream()
|
||||
.filter(request -> request.getDaysBalance() > 0
|
||||
&& request.getState() != TimeOffRequestStatus.VENCIDO
|
||||
&& request.getState() != TimeOffRequestStatus.RECHAZADO)
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.orElse(null);
|
||||
|
||||
if (requestWithBalance != null) {
|
||||
if (requestWithBalance.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& requestWithBalance.getDaysBalance() > 0) {
|
||||
daysBalance.setValue(requestWithBalance.getDaysBalance());
|
||||
} else {
|
||||
daysBalance.setValue(timeoff.getDuration());
|
||||
}
|
||||
} else if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
final LocalDate dateOfEntry = employee.getDateOfEntry();
|
||||
final LocalDate currentDate = LocalDate.now();
|
||||
long yearsOfService = ChronoUnit.YEARS.between(dateOfEntry, currentDate);
|
||||
|
||||
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
yearsOfService -= 1;
|
||||
}
|
||||
|
||||
if (yearsOfService > 10) {
|
||||
daysBalance.setValue(30.0);
|
||||
} else if (yearsOfService > 5) {
|
||||
daysBalance.setValue(20.0);
|
||||
} else if (yearsOfService > 1) {
|
||||
daysBalance.setValue(15.0);
|
||||
} else {
|
||||
daysBalance.setValue(0.0);
|
||||
}
|
||||
} else {
|
||||
daysBalance.setValue(timeoff.getDuration());
|
||||
}
|
||||
|
||||
setDatePickerLimits(timeoff);
|
||||
}
|
||||
|
||||
private void setDatePickerLimits(final TimeOff timeoff) {
|
||||
LocalDate startDateValue;
|
||||
LocalDate endDateValue = null;
|
||||
|
||||
final UUID employeeId = employee.getId();
|
||||
final List<TimeOffRequest> previousRequests
|
||||
= timeOffRequestService.findByEmployeeAndCategory(employeeId, timeoff.getCategory());
|
||||
|
||||
final int startYear = calculateStartYear(previousRequests);
|
||||
|
||||
startDateValue = determineStartDate(timeoff, startYear);
|
||||
|
||||
if (startDateValue.isBefore(LocalDate.now())) {
|
||||
startDateValue = determineStartDate(timeoff, startYear + 1);
|
||||
}
|
||||
|
||||
if (startDateValue != null) {
|
||||
if (timeoff.getExpiration() != null) {
|
||||
endDateValue = startDateValue.plusDays(timeoff.getExpiration().intValue() - 1);
|
||||
} else {
|
||||
endDateValue = LocalDate.of(startDateValue.getYear(), 12, 31);
|
||||
}
|
||||
} else {
|
||||
startDateValue = LocalDate.now();
|
||||
}
|
||||
|
||||
setPickerValues(timeoff, startDateValue);
|
||||
setPickerLimits(startDateValue, endDateValue);
|
||||
}
|
||||
|
||||
private int calculateStartYear(final List<TimeOffRequest> previousRequests) {
|
||||
if (previousRequests.isEmpty()) {
|
||||
return LocalDate.now().getYear();
|
||||
}
|
||||
|
||||
int lastRequestYear = previousRequests.stream()
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.map(request -> request.getStartDate().getYear())
|
||||
.orElse(LocalDate.now().getYear());
|
||||
|
||||
if (previousRequests.getLast().getState() != TimeOffRequestStatus.RECHAZADO) {
|
||||
lastRequestYear = lastRequestYear + 1;
|
||||
}
|
||||
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
return Math.max(lastRequestYear, currentYear);
|
||||
}
|
||||
|
||||
private LocalDate determineStartDate(final TimeOff timeoff, final int startYear) {
|
||||
if (timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() != null) {
|
||||
return LocalDate.of(startYear, employee.getBirthday().getMonth(), employee.getBirthday().getDayOfMonth());
|
||||
}
|
||||
|
||||
if (timeoff.getDate() != null) {
|
||||
return LocalDate.of(startYear, timeoff.getDate().getMonthValue(), timeoff.getDate().getDayOfMonth());
|
||||
}
|
||||
|
||||
return LocalDate.now();
|
||||
}
|
||||
|
||||
private void setPickerValues(final TimeOff timeoff, final LocalDate startDateValue) {
|
||||
startDate.setValue(startDateValue);
|
||||
|
||||
if ((timeoff.getDuration() != null && timeoff.getDuration() == 0.5)
|
||||
|| timeoff.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS) {
|
||||
|
||||
endDate.setValue(startDateValue);
|
||||
} else {
|
||||
int durationDays = (timeoff.getDuration() != null ? timeoff.getDuration().intValue() - 1 : 0);
|
||||
endDate.setValue(startDateValue.plusDays(durationDays));
|
||||
}
|
||||
}
|
||||
|
||||
private void setPickerLimits(final LocalDate startDateValue, final LocalDate endDateValue) {
|
||||
startDate.setMin(startDateValue);
|
||||
startDate.setMax(endDateValue);
|
||||
endDate.setMin(startDateValue);
|
||||
endDate.setMax(endDateValue);
|
||||
}
|
||||
|
||||
private void updateDatePickerMinValues() {
|
||||
LocalDate startDateValue = startDate.getValue();
|
||||
if (daysBalance.getValue() != null) {
|
||||
if (daysBalance.getValue() == 0.5) {
|
||||
endDate.setValue(startDateValue.plusDays(0));
|
||||
} else {
|
||||
endDate.setValue(startDateValue.plusDays(daysBalance.getValue().intValue() - 1));
|
||||
}
|
||||
|
||||
calculateDays();
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateDays() {
|
||||
LocalDate startDateValue = startDate.getValue();
|
||||
LocalDate endDateValue = endDate.getValue();
|
||||
Double availableDaysValue = daysBalance.getValue();
|
||||
|
||||
if (areDatesValid(startDateValue, endDateValue)) {
|
||||
long workDays = countWorkDaysBetween(startDateValue, endDateValue);
|
||||
|
||||
daysToBeTake.setValue((double) workDays);
|
||||
daysBalance.setValue(daysBalance.getValue() - workDays);
|
||||
|
||||
double daysToBeTaken = calculateDaysBetween(startDateValue, endDateValue);
|
||||
setDaysToBeTakenField(daysToBeTaken);
|
||||
|
||||
double balanceDays = calculateBalanceDays(availableDaysValue, daysToBeTake.getValue());
|
||||
daysBalance.setValue(balanceDays);
|
||||
|
||||
if (balanceDays < 0.0) {
|
||||
clearFields();
|
||||
}
|
||||
|
||||
if (daysToBeTake.getValue() > 10
|
||||
&& (category.getValue() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
|| category.getValue() == TimeOffRequestType.VACACION_GESTION_ACTUAL)) {
|
||||
clearFields();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearFields() {
|
||||
daysToBeTake.clear();
|
||||
daysBalance.clear();
|
||||
endDate.clear();
|
||||
}
|
||||
|
||||
private boolean areDatesValid(final LocalDate startDate, final LocalDate endDate) {
|
||||
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) {
|
||||
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) {
|
||||
final TimeOffRequest timeOffRequest = getEntity();
|
||||
|
||||
if (timeOffRequest.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.CUMPLEAÑOS
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.DIA_DEL_PADRE
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL) {
|
||||
daysToBeTake.setValue(0.5);
|
||||
} else {
|
||||
daysToBeTake.setValue(daysToBeTaken);
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateBalanceDays(final double availableDays, final double daysToBeTaken) {
|
||||
return availableDays - daysToBeTaken;
|
||||
}
|
||||
|
||||
private void setFieldsReadOnly(final boolean option) {
|
||||
state.setReadOnly(option);
|
||||
expiration.setReadOnly(option);
|
||||
startDate.setReadOnly(option);
|
||||
endDate.setReadOnly(option);
|
||||
daysBalance.setReadOnly(option);
|
||||
daysToBeTake.setReadOnly(option);
|
||||
}
|
||||
|
||||
private void saveRequest(final TimeOffRequest request) {
|
||||
if (!isFormValid()) {
|
||||
Notification.show("Por favor, complete los campos antes de guardar");
|
||||
return;
|
||||
}
|
||||
|
||||
prepareRequest(request);
|
||||
|
||||
if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL) {
|
||||
handleVacationRequest(request);
|
||||
} else {
|
||||
handleExistingRequests(request);
|
||||
}
|
||||
|
||||
long differentDays = ChronoUnit.DAYS.between(LocalDate.now(), request.getStartDate());
|
||||
if (differentDays >= -15 && differentDays <= 90) {
|
||||
timeOffRequestService.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 void prepareRequest(final TimeOffRequest request) {
|
||||
request.setStartDate(startDate.getValue());
|
||||
request.setAvailableDays(daysBalance.getValue());
|
||||
request.setExpiration(endDate.getValue());
|
||||
request.setState(TimeOffRequestStatus.SOLICITADO);
|
||||
}
|
||||
|
||||
private void handleExistingRequests(final TimeOffRequest request) {
|
||||
final List<TimeOffRequest> existingRequests =
|
||||
timeOffRequestService.findByEmployeeAndCategory(employee.getId(), request.getCategory());
|
||||
|
||||
int maxRequests = request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
&& !request.getCategory().name().startsWith("VACACION")
|
||||
&& request.getCategory() != TimeOffRequestType.CUMPLEAÑOS
|
||||
? 2 : 1;
|
||||
|
||||
if (existingRequests.size() >= maxRequests) {
|
||||
existingRequests.stream()
|
||||
.min(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.ifPresent(oldestRequest -> timeOffRequestService.deleteTimeOffRequest(oldestRequest.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleVacationRequest(final TimeOffRequest request) {
|
||||
final List<TimeOffRequest> existingRequests = timeOffRequestService.findByEmployeeAndCategory(
|
||||
employee.getId(),
|
||||
TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
);
|
||||
if (!existingRequests.isEmpty()) {
|
||||
TimeOffRequest existingRequest = existingRequests.getFirst();
|
||||
existingRequest.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR);
|
||||
timeOffRequestService.saveTimeOffRequest(existingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffRequestsListView.class));
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return !state.isEmpty()
|
||||
&& expiration.getValue() != null
|
||||
&& startDate.getValue() != null
|
||||
&& endDate.getValue() != null
|
||||
&& daysBalance.getValue() != null
|
||||
&& daysToBeTake.getValue() != null;
|
||||
}
|
||||
}
|
@ -1,343 +0,0 @@
|
||||
package com.primefactorsolutions.views.timeoff;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
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.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
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.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
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.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
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.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Time-Off Requests")
|
||||
@Route(value = "/time-off/requests", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class TimeOffRequestsListView extends BaseView {
|
||||
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final PagingGrid<TimeOffRequest> requestsGrid = new PagingGrid<>();
|
||||
private final ComboBox<Employee> employeeFilter = new ComboBox<>("Empleado");
|
||||
private final ComboBox<Team> teamFilter = new ComboBox<>("Equipo");
|
||||
private final ComboBox<TimeOffRequestType> categoryFilter = new ComboBox<>("Categoría");
|
||||
private final ComboBox<TimeOffRequestStatus> stateFilter = new ComboBox<>("Estado");
|
||||
|
||||
public TimeOffRequestsListView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext);
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
initializeView();
|
||||
refreshGeneralRequestsGrid(employeeFilter.getValue(), teamFilter.getValue(), categoryFilter.getValue(),
|
||||
stateFilter.getValue());
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
final Button newRequestButton = new Button("Crear nueva peticion", event -> navigateToAddNew());
|
||||
newRequestButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout(newRequestButton);
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final Button downloadReportButton = new Button("Descargar reporte de rechazos", event -> downloadReport());
|
||||
hl.add(downloadReportButton);
|
||||
}
|
||||
|
||||
getCurrentPageLayout().add(hl);
|
||||
setupFilters();
|
||||
setupRequestsGrid();
|
||||
}
|
||||
|
||||
private void navigateToAddNew() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffRequestView.class, "new"));
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createEmployeeFilter());
|
||||
hl.add(createTeamFilter());
|
||||
hl.add(createCategoryFilter());
|
||||
hl.add(createStateFilter());
|
||||
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private void setupRequestsGrid() {
|
||||
requestsGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
|
||||
requestsGrid.addColumn(this::getTeamName).setHeader("Equipo");
|
||||
requestsGrid.addColumn(this::getCategory).setHeader("Categoría");
|
||||
requestsGrid.addColumn(this::getDates).setHeader("Dias");
|
||||
requestsGrid.addColumn(this::getState).setHeader("Estado");
|
||||
requestsGrid.addColumn(this::getUpdated).setHeader("Fecha Actualizacion");
|
||||
requestsGrid.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;
|
||||
});
|
||||
|
||||
requestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
requestsGrid.setPageSize(PAGE_SIZE);
|
||||
|
||||
getCurrentPageLayout().add(requestsGrid);
|
||||
}
|
||||
|
||||
private void actionForRequest(final UUID selectedRequestId, final TimeOffRequestStatus status) {
|
||||
TimeOffRequest request = requestService.findTimeOffRequest(selectedRequestId);
|
||||
request.setState(status);
|
||||
requestService.saveTimeOffRequest(request);
|
||||
refreshGeneralRequestsGrid(null, null, null, null);
|
||||
}
|
||||
|
||||
private void refreshGeneralRequestsGrid(final Employee employee,
|
||||
final Team team,
|
||||
final TimeOffRequestType category,
|
||||
final TimeOffRequestStatus timeOffRequestStatus) {
|
||||
requestsGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
int start = (int) (page * requestsGrid.getPageSize());
|
||||
return fetchFilteredRequests(start, pageSize, employee, team, category, timeOffRequestStatus);
|
||||
});
|
||||
requestsGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<TimeOffRequest> fetchFilteredRequests(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final TimeOffRequestType category,
|
||||
final TimeOffRequestStatus timeOffRequestStatus) {
|
||||
List<TimeOffRequest> filteredRequests = requestService.findAllTimeOffRequests();
|
||||
|
||||
if (employee != null) {
|
||||
filteredRequests = filteredRequests.stream()
|
||||
.filter(emp -> emp.getEmployee().getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null) {
|
||||
filteredRequests = filteredRequests.stream()
|
||||
.filter(emp -> emp.getEmployee().getTeam() != null
|
||||
&& emp.getEmployee().getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (category != null) {
|
||||
filteredRequests = filteredRequests.stream()
|
||||
.filter(emp -> emp.getCategory().equals(category))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (timeOffRequestStatus != null) {
|
||||
filteredRequests = filteredRequests.stream()
|
||||
.filter(emp -> emp.getState().equals(timeOffRequestStatus))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredRequests.size());
|
||||
return filteredRequests.subList(start, end);
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final TimeOffRequest request) {
|
||||
Employee employee = request.getEmployee();
|
||||
return getEmployeeFullNameLabel(employee);
|
||||
}
|
||||
|
||||
private String getEmployeeFullNameLabel(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private String getTeamName(final TimeOffRequest request) {
|
||||
Team team = request.getEmployee().getTeam();
|
||||
return team != null ? team.getName() : "Sin asignar";
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return team.getName();
|
||||
}
|
||||
|
||||
private String getCategory(final TimeOffRequest request) {
|
||||
return String.valueOf(request.getCategory());
|
||||
}
|
||||
|
||||
private String getDates(final TimeOffRequest request) {
|
||||
return String.format("de %s a %s", request.getStartDate(), request.getEndDate());
|
||||
}
|
||||
|
||||
private String getState(final TimeOffRequest request) {
|
||||
return request.getState().name();
|
||||
}
|
||||
|
||||
private String getUpdated(final TimeOffRequest request) {
|
||||
return DateTimeFormatter.ofPattern("yyyy/dd/MM hh:mm")
|
||||
.format(request.getUpdated().atOffset(ZoneOffset.ofHours(-5)));
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final List<Employee> employees = employeeService.findAllEmployees();
|
||||
employeeFilter.setItems(employees);
|
||||
} else {
|
||||
final Employee employee = employeeService.getEmployee(getEmployeeId().get());
|
||||
employeeFilter.setItems(List.of(employee));
|
||||
employeeFilter.setValue(employee);
|
||||
employeeFilter.setReadOnly(true);
|
||||
}
|
||||
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullNameLabel);
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestsGrid(
|
||||
event.getValue(),
|
||||
teamFilter.getValue(),
|
||||
categoryFilter.getValue(),
|
||||
stateFilter.getValue()
|
||||
)
|
||||
);
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
final List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestsGrid(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue(),
|
||||
categoryFilter.getValue(),
|
||||
stateFilter.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private ComboBox<TimeOffRequestType> createCategoryFilter() {
|
||||
categoryFilter.setPlaceholder("Seleccionar ...");
|
||||
categoryFilter.setClearButtonVisible(true);
|
||||
categoryFilter.setItems(TimeOffRequestType.values());
|
||||
categoryFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestsGrid(
|
||||
employeeFilter.getValue(),
|
||||
teamFilter.getValue(),
|
||||
event.getValue(),
|
||||
stateFilter.getValue()
|
||||
)
|
||||
);
|
||||
return categoryFilter;
|
||||
}
|
||||
|
||||
private ComboBox<TimeOffRequestStatus> createStateFilter() {
|
||||
stateFilter.setPlaceholder("Seleccionar ...");
|
||||
stateFilter.setClearButtonVisible(true);
|
||||
stateFilter.setItems(TimeOffRequestStatus.values());
|
||||
stateFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestsGrid(
|
||||
employeeFilter.getValue(),
|
||||
teamFilter.getValue(),
|
||||
categoryFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
|
||||
return stateFilter;
|
||||
}
|
||||
|
||||
private void downloadReport() {
|
||||
StreamResource resource = generateGeneralVacationReport();
|
||||
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
|
||||
}
|
||||
|
||||
private StreamResource generateGeneralVacationReport() {
|
||||
List<TimeOffRequest> requests = requestService.findAllTimeOffRequests().stream()
|
||||
.filter(request -> request.getState() == TimeOffRequestStatus.RECHAZADO)
|
||||
.collect(Collectors.toList());
|
||||
ByteArrayInputStream excelStream = generateExcelReport(requests);
|
||||
return new StreamResource("reporte_de_solicitudes_rechazadas_" + LocalDate.now() + ".xlsx",
|
||||
() -> excelStream);
|
||||
}
|
||||
|
||||
private void openDocumentStream(final StreamResource resource, final UI ui) {
|
||||
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generateExcelReport(final List<TimeOffRequest> requests) {
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
Sheet sheet = workbook.createSheet("REPORTE_DE_SOLICITUDES_DE_VACACIONES_RECHAZADAS");
|
||||
Row headerRow = sheet.createRow(0);
|
||||
|
||||
String[] headers = {"Empleado", "Categoria", "Observaciones"};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
}
|
||||
|
||||
int rowIndex = 1;
|
||||
for (TimeOffRequest request : requests) {
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
row.createCell(0).setCellValue(getEmployeeFullName(request));
|
||||
row.createCell(1).setCellValue(getCategory(request));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,809 +0,0 @@
|
||||
package com.primefactorsolutions.views.timeoff;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.service.TimeOffService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
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.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
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.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
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.Period;
|
||||
import java.time.Year;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Requests")
|
||||
@Route(value = "/time-off/summary", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class TimeOffSummaryListView extends BaseView {
|
||||
|
||||
private static final Map<UUID, TimeOffSummary> SUMMARY_MAP = new ConcurrentHashMap<>();
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final TimeOffService timeOffService;
|
||||
private final PagingGrid<Employee> requestGrid = new PagingGrid<>();
|
||||
private final ComboBox<Employee> employeeFilter = new ComboBox<>("Empleado");;
|
||||
private final ComboBox<Team> teamFilter = new ComboBox<>("Equipo");
|
||||
|
||||
public TimeOffSummaryListView(
|
||||
final AuthenticationContext authenticationContext,
|
||||
final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService,
|
||||
final TimeOffService timeOffService) {
|
||||
super(authenticationContext);
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
this.timeOffService = timeOffService;
|
||||
initializeView();
|
||||
refreshGeneralRequestGrid(employeeFilter.getValue(), teamFilter.getValue());
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
requestService.updateRequestStatuses();
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final Button downloadButton = new Button("Descargar reporte", event -> downloadReport());
|
||||
getCurrentPageLayout().add(downloadButton);
|
||||
}
|
||||
|
||||
setupFilters();
|
||||
setupRequestGrid();
|
||||
getCurrentPageLayout().add(requestGrid);
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createEmployeeFilter());
|
||||
hl.add(createTeamFilter());
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private void setupRequestGrid() {
|
||||
requestGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
|
||||
requestGrid.addColumn(this::getTeamName).setHeader("Equipo");
|
||||
requestGrid.addColumn(this::getRemainingHolidays).setHeader("Remaining holiday");
|
||||
requestGrid.addColumn(this::getRemainingPersonal).setHeader("Remaining personal");
|
||||
requestGrid.addColumn(this::getRemainingVacation).setHeader("Remaining vacation");
|
||||
requestGrid.addColumn(this::getRemainingTotal).setHeader("Remaining total");
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
requestGrid.addComponentColumn(employee -> MenuBarUtils.menuBar(
|
||||
Pair.of("Download", __ -> downloadEmployeeReport(employee.getId()))));
|
||||
}
|
||||
|
||||
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
requestGrid.setPageSize(PAGE_SIZE);
|
||||
}
|
||||
|
||||
private Double getRemainingHolidays(final Employee employee) {
|
||||
final TimeOffSummary summary = SUMMARY_MAP.computeIfAbsent(employee.getId(), __ -> getTimeOffSummary(employee));
|
||||
|
||||
return summary.remainingHolidayDays;
|
||||
}
|
||||
|
||||
private Double getRemainingPersonal(final Employee employee) {
|
||||
final TimeOffSummary summary = SUMMARY_MAP.computeIfAbsent(employee.getId(), __ -> getTimeOffSummary(employee));
|
||||
|
||||
return summary.remainingPersonalDays;
|
||||
}
|
||||
|
||||
private Double getRemainingVacation(final Employee employee) {
|
||||
final TimeOffSummary summary = SUMMARY_MAP.computeIfAbsent(employee.getId(), __ -> getTimeOffSummary(employee));
|
||||
|
||||
return summary.remainingVacationDays;
|
||||
}
|
||||
|
||||
private Double getRemainingTotal(final Employee employee) {
|
||||
final TimeOffSummary summary = SUMMARY_MAP.computeIfAbsent(employee.getId(), __ -> getTimeOffSummary(employee));
|
||||
|
||||
return summary.getTotalRemaining();
|
||||
}
|
||||
|
||||
private void refreshGeneralRequestGrid(final Employee employee,
|
||||
final Team team) {
|
||||
requestGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
int start = (int) (page * requestGrid.getPageSize());
|
||||
return fetchFilteredEmployees(start, pageSize, employee, team);
|
||||
});
|
||||
requestGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<Employee> fetchFilteredEmployees(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team) {
|
||||
List<Employee> filteredEmployees = employeeService.findAllEmployees();
|
||||
|
||||
if (employee != null) {
|
||||
filteredEmployees = filteredEmployees.stream()
|
||||
.filter(emp -> emp.getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null) {
|
||||
filteredEmployees = filteredEmployees.stream()
|
||||
.filter(emp -> emp.getTeam() != null && emp.getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredEmployees.size());
|
||||
return filteredEmployees.subList(start, end);
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private String getTeamName(final Employee employee) {
|
||||
Team team = employee.getTeam();
|
||||
return team != null ? team.getName() : "Sin asignar";
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return team.getName();
|
||||
}
|
||||
|
||||
private String getEmployeeStatus(final Employee employee) {
|
||||
Optional<TimeOffRequest> activeRequest = requestService
|
||||
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
|
||||
return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES";
|
||||
}
|
||||
|
||||
private String getGeneralTotal(final Employee employee) {
|
||||
final List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
final List<TimeOff> timeOffs = timeOffService.findVacations();
|
||||
final List<Double> vacationDays = calculateVacationDays(employee);
|
||||
double utilizedVacationCurrentDays = vacationDays.get(1);
|
||||
|
||||
final List<TimeOffRequest> vacationCurrentRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.VACACION_GESTION_ACTUAL);
|
||||
|
||||
if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) {
|
||||
utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance();
|
||||
}
|
||||
double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays);
|
||||
|
||||
double utilizedVacationPreviousDays = vacationDays.get(0);
|
||||
final List<TimeOffRequest> vacationPreviousRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.VACACION_GESTION_ANTERIOR);
|
||||
|
||||
if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) {
|
||||
utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance();
|
||||
}
|
||||
|
||||
final double totalVacationPreviousDays = vacationDays.getFirst()
|
||||
- (vacationDays.getFirst() - utilizedVacationPreviousDays);
|
||||
|
||||
|
||||
final double totalUtilized = calculateTotalUtilized(employeeRequests);
|
||||
final double totalVacations = totalVacationCurrentDays + totalVacationPreviousDays;
|
||||
final double totalAvailable = calculateTotalAvailable(timeOffs, employeeRequests, employee);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getExcludedCategories() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.MATERNIDAD,
|
||||
TimeOffRequestType.PATERNIDAD,
|
||||
TimeOffRequestType.MATRIMONIO,
|
||||
TimeOffRequestType.DUELO_1ER_GRADO,
|
||||
TimeOffRequestType.DUELO_2ER_GRADO,
|
||||
TimeOffRequestType.DIA_DEL_PADRE,
|
||||
TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getGenderSpecificExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
|
||||
);
|
||||
}
|
||||
|
||||
private double calculateTotalUtilized(final List<TimeOffRequest> employeeRequests) {
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
return employeeRequests.stream()
|
||||
.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.VACACION_GESTION_ACTUAL)
|
||||
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR)
|
||||
.filter(request -> request.getStartDate() != null && (
|
||||
request.getStartDate().getYear() == currentYear
|
||||
|| (request.getCategory().name().startsWith("VACACION")
|
||||
&& request.getStartDate().getYear() == currentYear - 1)
|
||||
))
|
||||
.mapToDouble(request -> request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0.0)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private List<Double> calculateVacationDays(final Employee employee) {
|
||||
List<Double> vacationDays = new ArrayList<>();
|
||||
|
||||
if (employee.getDateOfEntry() != null) {
|
||||
LocalDate entryDate = employee.getDateOfEntry();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue()
|
||||
|| (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth()
|
||||
<= today.getDayOfMonth());
|
||||
|
||||
LocalDate previousVacationYearDate;
|
||||
LocalDate currentVacationYearDate;
|
||||
|
||||
if (hasAnniversaryPassed) {
|
||||
previousVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 1,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
currentVacationYearDate = LocalDate.of(
|
||||
today.getYear(),
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
} else {
|
||||
previousVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 2,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
currentVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 1,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
}
|
||||
|
||||
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate));
|
||||
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate));
|
||||
} else {
|
||||
vacationDays.add(0.0);
|
||||
vacationDays.add(0.0);
|
||||
}
|
||||
|
||||
return vacationDays;
|
||||
}
|
||||
|
||||
private double calculateTotalAvailable(final List<TimeOff> timeOffs, final List<TimeOffRequest> employeeRequests,
|
||||
final Employee employee) {
|
||||
Set<TimeOffRequestType> excludedCategories = getExcludedCategories();
|
||||
Set<TimeOffRequestType> genderSpecificExclusions = getGenderSpecificExclusions();
|
||||
Set<TimeOffRequestType> employeeRequestCategories = employeeRequests.stream()
|
||||
.map(TimeOffRequest::getCategory)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
double healthLicence = 2;
|
||||
List<TimeOffRequest> healthRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.PERMISOS_DE_SALUD);
|
||||
if (healthRequests != null && !healthRequests.isEmpty()) {
|
||||
healthLicence = healthRequests.getLast().getDaysBalance();
|
||||
}
|
||||
|
||||
double totalAvailable = timeOffs.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(vacation -> vacation.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.filter(vacation -> shouldIncludeVacation(
|
||||
vacation,
|
||||
excludedCategories,
|
||||
genderSpecificExclusions,
|
||||
employee, employeeRequestCategories
|
||||
))
|
||||
.mapToDouble(vacation -> vacation.getDuration() != null ? vacation.getDuration() : 0.0)
|
||||
.sum();
|
||||
|
||||
return totalAvailable + healthLicence;
|
||||
}
|
||||
|
||||
private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) {
|
||||
int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0;
|
||||
if (yearsOfService > 10) {
|
||||
return 30;
|
||||
}
|
||||
if (yearsOfService > 5) {
|
||||
return 20;
|
||||
}
|
||||
if (yearsOfService > 1) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean shouldIncludeVacation(final TimeOff timeoff,
|
||||
final Set<TimeOffRequestType> excludedCategories,
|
||||
final Set<TimeOffRequestType> genderSpecificExclusions,
|
||||
final Employee employee,
|
||||
final Set<TimeOffRequestType> employeeRequestCategories) {
|
||||
if (excludedCategories.contains(timeoff.getCategory())
|
||||
&& !employeeRequestCategories.contains(timeoff.getCategory())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isFemale(employee) || !genderSpecificExclusions.contains(timeoff.getCategory());
|
||||
}
|
||||
|
||||
private boolean isFemale(final Employee employee) {
|
||||
return employee.getGender() == Employee.Gender.FEMALE;
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
employeeFilter.setItems(employees);
|
||||
} else {
|
||||
final Employee employee = employeeService.getEmployee(getEmployeeId().get());
|
||||
employeeFilter.setItems(List.of(employee));
|
||||
employeeFilter.setValue(employee);
|
||||
employeeFilter.setReadOnly(true);
|
||||
}
|
||||
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestGrid(
|
||||
event.getValue(),
|
||||
teamFilter.getValue()
|
||||
)
|
||||
);
|
||||
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
final List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestGrid(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generateExcelReport(final List<Employee> employees) {
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
final Sheet sheet = workbook.createSheet("REPORTE_GENERAL_DE_VACACIONES");
|
||||
final 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 (final 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() {
|
||||
final List<Employee> employees = employeeService.findAllEmployees();
|
||||
final ByteArrayInputStream excelStream = generateExcelReport(employees);
|
||||
return new StreamResource("reporte_general_de_vacaciones_" + LocalDate.now() + ".xlsx",
|
||||
() -> excelStream);
|
||||
}
|
||||
|
||||
private void downloadReport() {
|
||||
final StreamResource resource = generateGeneralVacationReport();
|
||||
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
|
||||
}
|
||||
|
||||
private void openDocumentStream(final StreamResource resource, final UI ui) {
|
||||
final StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
}
|
||||
|
||||
private TimeOffSummary getTimeOffSummary(final Employee employee) {
|
||||
final boolean isMale = employee.getGender() == Employee.Gender.MALE;
|
||||
final int currentYear = LocalDate.now().getYear();
|
||||
final LocalDate currentDate = LocalDate.now();
|
||||
final List<TimeOff> timeOffs = timeOffService.findVacations();
|
||||
double healthLicence = getHealthLicence(employee.getId());
|
||||
|
||||
double totalFixedAndMovableHolidays = calculateHolidayDays(timeOffs);
|
||||
double totalPersonalDays = calculatePersonalDays(timeOffs, isMale);
|
||||
final List<Double> vacationDays = calculateVacationDays(employee);
|
||||
|
||||
double totalVacationCurrentDays = calculateUtilizedVacationDays(
|
||||
employee.getId(),
|
||||
vacationDays.get(1),
|
||||
TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
);
|
||||
double totalVacationPreviousDays = calculateUtilizedVacationDays(
|
||||
employee.getId(),
|
||||
vacationDays.get(0),
|
||||
TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
);
|
||||
|
||||
final List<TimeOffRequest> requests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(requests, currentYear);
|
||||
double utilizedPersonalDays = calculatePersonalDaysUtilized(requests, isMale, currentYear);
|
||||
|
||||
double remainingHolidayDays = calculateRemainingHolidayDays(
|
||||
totalFixedAndMovableHolidays,
|
||||
utilizedFixedAndMovableHolidays,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
double remainingPersonalDays = calculateRemainingPersonalDays(
|
||||
totalPersonalDays,
|
||||
utilizedPersonalDays,
|
||||
healthLicence,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
double remainingVacationDays = calculateRemainingVacationDays(
|
||||
totalVacationCurrentDays,
|
||||
totalVacationPreviousDays,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
|
||||
return new TimeOffSummary(remainingHolidayDays, remainingPersonalDays, remainingVacationDays);
|
||||
}
|
||||
|
||||
private record TimeOffSummary(double remainingHolidayDays, double remainingPersonalDays,
|
||||
double remainingVacationDays) {
|
||||
public double getTotalRemaining() {
|
||||
return remainingHolidayDays + remainingPersonalDays + remainingVacationDays;
|
||||
}
|
||||
}
|
||||
|
||||
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 UUID employeeId, 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<TimeOff> timeOffs) {
|
||||
return timeOffs.stream()
|
||||
.filter(req -> req.getType() != TimeOff.Type.OTHER)
|
||||
.mapToDouble(TimeOff::getDuration)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private double calculatePersonalDays(final List<TimeOff> timeOffs, final boolean isMale) {
|
||||
return timeOffs.stream()
|
||||
.filter(req -> req.getType() == TimeOff.Type.OTHER)
|
||||
.filter(req -> !getStandardExclusions().contains(req.getCategory()))
|
||||
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
|
||||
.filter(req -> !req.getCategory().name().startsWith("VACACION"))
|
||||
.filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.mapToDouble(TimeOff::getDuration)
|
||||
.sum();
|
||||
}
|
||||
|
||||
|
||||
private Set<TimeOffRequestType> getStandardExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.MATERNIDAD,
|
||||
TimeOffRequestType.PATERNIDAD,
|
||||
TimeOffRequestType.MATRIMONIO,
|
||||
TimeOffRequestType.DUELO_1ER_GRADO,
|
||||
TimeOffRequestType.DUELO_2ER_GRADO,
|
||||
TimeOffRequestType.DIA_DEL_PADRE,
|
||||
TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getMaleSpecificExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
|
||||
);
|
||||
}
|
||||
|
||||
private double calculateHolidayUtilizedDays(final List<TimeOffRequest> requests, final int year) {
|
||||
return requests.stream()
|
||||
.filter(this::verificationIsHoliday)
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO
|
||||
|| req.getState() == TimeOffRequestStatus.APROBADO)
|
||||
.filter(req -> getStartDateYear(req) == year)
|
||||
.mapToDouble(TimeOffRequest::getDaysToBeTake)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private double calculatePersonalDaysUtilized(final List<TimeOffRequest> requests, final boolean isMale,
|
||||
final int year) {
|
||||
return requests.stream()
|
||||
.filter(req -> !verificationIsHoliday(req))
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO
|
||||
|| req.getState() == TimeOffRequestStatus.APROBADO)
|
||||
.filter(req -> !getStandardExclusions().contains(req.getCategory()))
|
||||
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
|
||||
.filter(req -> !req.getCategory().name().startsWith("VACACION"))
|
||||
.filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.filter(req -> getStartDateYear(req) == year)
|
||||
.mapToDouble(TimeOffRequest::getDaysToBeTake)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private int getStartDateYear(final TimeOffRequest request) {
|
||||
if (request.getStartDate() != null) {
|
||||
return request.getStartDate().getYear();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Boolean verificationIsHoliday(final TimeOffRequest request) {
|
||||
TimeOff timeoff = timeOffService.findVacationByCategory(request.getCategory());
|
||||
return timeoff.getType() != TimeOff.Type.OTHER;
|
||||
}
|
||||
|
||||
private String getDateString(final LocalDate date) {
|
||||
return (date != null) ? date.toString() : "";
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generatePdfReport(final UUID employeeId) {
|
||||
final List<TimeOffRequest> requests = requestService.findRequestsByEmployeeId(employeeId);
|
||||
final Employee employee = employeeService.getEmployee(employeeId);
|
||||
final TimeOffSummary result = getTimeOffSummary(employee);
|
||||
|
||||
try (PDDocument document = new PDDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
PDPage page = new PDPage();
|
||||
document.addPage(page);
|
||||
|
||||
List<TimeOffRequest> filteredRequests = requests.stream()
|
||||
.filter(request ->
|
||||
(request.getStartDate() == null || Year.from(request.getStartDate()).equals(Year.now()))
|
||||
|| request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR)
|
||||
.toList();
|
||||
|
||||
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 : filteredRequests) {
|
||||
String startDate = getDateString(request.getStartDate());
|
||||
String endDate = getDateString(request.getEndDate());
|
||||
|
||||
String[] rowData = {
|
||||
request.getCategory().name(),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
currentY -= 30;
|
||||
|
||||
if (currentY < 80) {
|
||||
contentStream.close();
|
||||
page = new PDPage();
|
||||
document.addPage(page);
|
||||
contentStream = new PDPageContentStream(document, page);
|
||||
currentY = 750;
|
||||
}
|
||||
|
||||
contentStream.setFont(PDType1Font.TIMES_ROMAN, 14);
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(50, currentY - 20);
|
||||
contentStream.showText("Total feriados fijos y movibles: " + result.remainingHolidayDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("Total días libres personales: " + result.remainingPersonalDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("Total vacaciones pendientes de uso: " + result.remainingVacationDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("TOTAL GENERAL DE DÍAS DISPONIBLES: "
|
||||
+ (result.remainingHolidayDays + result.remainingPersonalDays + result.remainingVacationDays));
|
||||
contentStream.endText();
|
||||
|
||||
} 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(final UUID employeeId) {
|
||||
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(employeeId);
|
||||
|
||||
return new StreamResource(fileName, () -> pdfStream)
|
||||
.setContentType("application/pdf")
|
||||
.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
}
|
||||
|
||||
private void downloadEmployeeReport(final UUID employeeId) {
|
||||
StreamResource resource = generateVacationReport(employeeId);
|
||||
getUI().ifPresent(ui -> openDocumentEmployeeStream(resource, ui));
|
||||
}
|
||||
|
||||
private void openDocumentEmployeeStream(final StreamResource resource, final UI ui) {
|
||||
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
}
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
package com.primefactorsolutions.views.timesheet;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TimesheetService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.datepicker.VDatePicker;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Horas Trabajadas")
|
||||
@Route(value = "/timesheet/:hours-workedId?/:action?", layout = MainLayout.class)
|
||||
public class TimesheetEntryView extends BaseEntityForm<TimesheetEntry> implements HasUrlParameter<String> {
|
||||
private final ComboBox<Team> team = new ComboBox<>("Equipo");
|
||||
private final ComboBox<Employee> employee = new ComboBox<>("Empleado");
|
||||
private final ComboBox<String> task = new ComboBox<>("Tarea");
|
||||
private final TextField details = new TextField("Detalle");
|
||||
private final VDatePicker date = new VDatePicker("Fecha");
|
||||
private final NumberField hours = new NumberField("Horas");
|
||||
|
||||
private final TimesheetService timesheetService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
|
||||
private TimesheetEntry timesheetEntry;
|
||||
|
||||
public TimesheetEntryView(final AuthenticationContext authenticationContext,
|
||||
final TimesheetService timesheetService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext, TimesheetEntry.class);
|
||||
this.timesheetService = timesheetService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
|
||||
task.setRequired(true);
|
||||
|
||||
initializeDateField();
|
||||
initializeTeamField();
|
||||
initializeEmployeeField();
|
||||
configureTasks();
|
||||
|
||||
this.setSavedHandler(this::saveHoursWorked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
final String s = params.get("hours-workedId").orElse(null);
|
||||
|
||||
if ("new".equals(action) || s == null) {
|
||||
setEntityWithEnabledSave(new TimesheetEntry());
|
||||
} else {
|
||||
final UUID hoursWorkedId = UUID.fromString(s);
|
||||
timesheetEntry = timesheetService.getTimesheetEntry(hoursWorkedId);
|
||||
|
||||
if ("edit".equals(action) && !s.isEmpty()) {
|
||||
setEntityWithEnabledSave(timesheetEntry);
|
||||
} else if ("view".equals(action) && !s.isEmpty()) {
|
||||
setEntity(timesheetEntry);
|
||||
setFormReadOnly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setFormReadOnly() {
|
||||
employee.setReadOnly(true);
|
||||
team.setReadOnly(true);
|
||||
task.setReadOnly(true);
|
||||
details.setReadOnly(true);
|
||||
date.setReadOnly(true);
|
||||
hours.setReadOnly(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
date,
|
||||
team,
|
||||
employee,
|
||||
task,
|
||||
details,
|
||||
hours
|
||||
);
|
||||
}
|
||||
|
||||
private void configureTasks() {
|
||||
task.setWidthFull();
|
||||
task.setItems(
|
||||
"Entrevistas",
|
||||
"Reuniones",
|
||||
"Colaboraciones",
|
||||
"Aprendizajes",
|
||||
"Proyectos PFS",
|
||||
"Consulta Medica",
|
||||
"Afiliación al Seguro",
|
||||
"Fallas Tecnicas",
|
||||
"Otros");
|
||||
task.setPlaceholder("Selecciona una tarea...");
|
||||
}
|
||||
|
||||
private void initializeTeamField() {
|
||||
team.setWidthFull();
|
||||
team.setItems(teamService.findAllTeams());
|
||||
team.setItemLabelGenerator(Team::getName);
|
||||
team.addValueChangeListener(event -> {
|
||||
if (event.getOldValue() != null) {
|
||||
final Team selectedTeam = event.getValue();
|
||||
updateEmployeeField(selectedTeam);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateEmployeeField(final Team selectedTeam) {
|
||||
if (selectedTeam != null) {
|
||||
final List<Employee> employeesInTeam = employeeService.findAllEmployees().stream()
|
||||
.filter(employee -> employee.getTeam() != null && employee.getTeam().equals(selectedTeam))
|
||||
.toList();
|
||||
employee.setItems(employeesInTeam);
|
||||
employee.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeEmployeeField() {
|
||||
final List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
employee.setWidthFull();
|
||||
employee.setItems(employees);
|
||||
employee.setItemLabelGenerator(TimesheetEntryView::getEmployeeFullName);
|
||||
employee.setRequired(true);
|
||||
}
|
||||
|
||||
private static String getEmployeeFullName(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private void initializeDateField() {
|
||||
final LocalDate today = LocalDate.now();
|
||||
final YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth());
|
||||
final LocalDate startOfMonth = currentMonth.atDay(1);
|
||||
|
||||
date.setWidthFull();
|
||||
date.setMin(startOfMonth);
|
||||
date.setMax(today);
|
||||
date.setValue(today);
|
||||
}
|
||||
|
||||
private void saveHoursWorked(final TimesheetEntry timesheetEntry) {
|
||||
if (isFormValid()) {
|
||||
timesheetService.save(timesheetEntry);
|
||||
closeForm();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimesheetListView.class));
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return date.getValue() != null
|
||||
&& team.getValue() != null
|
||||
&& employee.getValue() != null
|
||||
&& task.getValue() != null;
|
||||
}
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
package com.primefactorsolutions.views.timesheet;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.model.Week;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TimesheetService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
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.ButtonVariant;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
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.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Registro de Horas Trabajadas")
|
||||
@Route(value = "/timesheet/records", layout = MainLayout.class)
|
||||
public class TimesheetListView extends BaseView {
|
||||
|
||||
private final TimesheetService timesheetService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final PagingGrid<TimesheetEntry> timesheetPagingGrid = new PagingGrid<>();
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<Team> teamFilter;
|
||||
private ComboBox<Week> weekFilter;
|
||||
|
||||
public TimesheetListView(final AuthenticationContext authenticationContext,
|
||||
final TimesheetService timesheetService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext);
|
||||
this.timesheetService = timesheetService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
|
||||
initializeView();
|
||||
}
|
||||
|
||||
private void refreshGridListHoursWorked(final Employee employee,
|
||||
final Team team,
|
||||
final Week week) {
|
||||
timesheetPagingGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
final int start = (int) (page * timesheetPagingGrid.getPageSize());
|
||||
return fetchFilteredHoursWorked(start, pageSize, employee, team, week);
|
||||
});
|
||||
timesheetPagingGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<TimesheetEntry> fetchFilteredHoursWorked(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final Week week) {
|
||||
List<TimesheetEntry> filteredTimesheetEntry = timesheetService.findListHoursWorkedEmployee(week.from(),
|
||||
week.to());
|
||||
|
||||
if (employee != null) {
|
||||
filteredTimesheetEntry = filteredTimesheetEntry.stream()
|
||||
.filter(e -> e.getEmployee().getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null) {
|
||||
filteredTimesheetEntry = filteredTimesheetEntry.stream()
|
||||
.filter(e -> e.getEmployee().getTeam() != null
|
||||
&& e.getEmployee().getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredTimesheetEntry.size());
|
||||
|
||||
return filteredTimesheetEntry.subList(start, end);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
getCurrentPageLayout().add(createAddHoursWorked());
|
||||
setupFilters();
|
||||
setupListHoursWorkedGrid();
|
||||
getCurrentPageLayout().add(timesheetPagingGrid);
|
||||
refreshGridListHoursWorked(employeeFilter.getValue(), teamFilter.getValue(), weekFilter.getValue());
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createWeekFilter());
|
||||
hl.add(createEmployeeFilter());
|
||||
hl.add(createTeamFilter());
|
||||
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private void setupListHoursWorkedGrid() {
|
||||
timesheetPagingGrid.addColumn(e -> e.getDate() != null ? e.getDate().toString() : "")
|
||||
.setHeader("Fecha")
|
||||
.setSortable(true);
|
||||
timesheetPagingGrid.addColumn(e -> e.getEmployee().getFirstName() + " " + e.getEmployee().getLastName())
|
||||
.setHeader("Empleado");
|
||||
timesheetPagingGrid.addColumn(e -> e.getEmployee().getTeam() != null
|
||||
? e.getEmployee().getTeam().getName()
|
||||
: "Sin asignar")
|
||||
.setHeader("Equipo");
|
||||
timesheetPagingGrid.addColumn(e -> {
|
||||
String details = e.getDetails() != null ? e.getDetails() : "Sin Detalle";
|
||||
String task = e.getTask() != null ? e.getTask() : "";
|
||||
return !task.isEmpty() ? task : details;
|
||||
}).setHeader("Details");
|
||||
timesheetPagingGrid.addColumn(TimesheetEntry::getHours).setHeader("Horas").setSortable(true);
|
||||
|
||||
timesheetPagingGrid.addComponentColumn((ValueProvider<TimesheetEntry, Component>) entry -> {
|
||||
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 -> {
|
||||
navigateToTimesheetEntryView(entry.getId(), "view");
|
||||
});
|
||||
final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Editar");
|
||||
editItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
navigateToTimesheetEntryView(entry.getId(), "edit");
|
||||
});
|
||||
return menuBar;
|
||||
});
|
||||
|
||||
timesheetPagingGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
timesheetPagingGrid.setPageSize(PAGE_SIZE);
|
||||
}
|
||||
|
||||
private void navigateToTimesheetEntryView(final UUID idRecord, final String action) {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimesheetEntryView.class, idRecord.toString() + "/" + action));
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
|
||||
final Button button = new Button(label);
|
||||
|
||||
if (isPrimary) {
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
}
|
||||
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddHoursWorked() {
|
||||
return createButton("Agregar Actividad", this::navigateToHours, true);
|
||||
}
|
||||
|
||||
private void navigateToHours() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimesheetEntryView.class, "new"));
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
|
||||
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
|
||||
if (!isRoleAdmin()) {
|
||||
employeeFilter.setReadOnly(true);
|
||||
employees = employees.stream()
|
||||
.filter(e -> getEmployeeId().equals(Optional.ofNullable(e.getId())))
|
||||
.toList();
|
||||
}
|
||||
|
||||
employeeFilter.setItems(employees);
|
||||
|
||||
if (!isRoleAdmin()) {
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
}
|
||||
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(
|
||||
event.getValue(),
|
||||
teamFilter.getValue(),
|
||||
weekFilter.getValue()
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
final List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue(),
|
||||
weekFilter.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Week> createWeekFilter() {
|
||||
final List<Week> weeks = Week.getLastWeeks(4);
|
||||
weekFilter = new ComboBox<>("Week");
|
||||
weekFilter.setItems(weeks);
|
||||
weekFilter.setValue(weeks.getFirst());
|
||||
weekFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(
|
||||
employeeFilter.getValue(),
|
||||
teamFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
|
||||
return weekFilter;
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return team.getName();
|
||||
}
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
package com.primefactorsolutions.views.timesheet;
|
||||
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.model.Week;
|
||||
import com.primefactorsolutions.service.TimesheetService;
|
||||
import com.primefactorsolutions.service.ReportService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.html.Anchor;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.IsoFields;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Route(value = "/timesheet/report", layout = MainLayout.class)
|
||||
@PageTitle("Reporte de Horas Trabajadas")
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@Slf4j
|
||||
public class TimesheetReportView extends BaseView {
|
||||
|
||||
private final TimesheetService timesheetService;
|
||||
private final ReportService reportService;
|
||||
private final ComboBox<Team> teamComboBox = new ComboBox<>("Equipo");
|
||||
private final ComboBox<Week> weekComboBox = new ComboBox<>("Semana");
|
||||
private final Grid<Map<String, Object>> grid = new Grid<>();
|
||||
private Anchor downloadLink;
|
||||
private final int currentYear = LocalDate.now().getYear();
|
||||
|
||||
@Autowired
|
||||
public TimesheetReportView(final AuthenticationContext authenticationContext,
|
||||
final TimesheetService timesheetService,
|
||||
final ReportService reportService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext);
|
||||
this.timesheetService = timesheetService;
|
||||
this.reportService = reportService;
|
||||
|
||||
final List<Team> teams = teamService.findAllTeams();
|
||||
teamComboBox.setPlaceholder("Seleccionar ...");
|
||||
teamComboBox.setItems(teams);
|
||||
teamComboBox.setItemLabelGenerator(Team::getName);
|
||||
initializeWeekComboBox();
|
||||
|
||||
final Button reportButton = new Button("Generar Reporte de Horas Trabajadas",
|
||||
event -> generateHoursWorkedReport());
|
||||
getCurrentPageLayout().add(reportButton);
|
||||
final HorizontalLayout filtersLayout = new HorizontalLayout(teamComboBox, weekComboBox);
|
||||
getCurrentPageLayout().add(filtersLayout);
|
||||
|
||||
grid.addColumn(map -> map.get("Empleado")).setHeader("Empleado");
|
||||
grid.addColumn(map -> map.get("Horas Trabajadas")).setHeader("Horas Trabajadas");
|
||||
grid.addColumn(map -> map.get("Horas Pendientes")).setHeader("Horas Pendientes");
|
||||
grid.addColumn(map -> map.get("Observaciones")).setHeader("Observaciones");
|
||||
|
||||
getCurrentPageLayout().add(grid);
|
||||
}
|
||||
|
||||
private void initializeWeekComboBox() {
|
||||
weekComboBox.setItems(Week.getLastWeeks(8));
|
||||
weekComboBox.setPlaceholder("Seleccionar ...");
|
||||
}
|
||||
|
||||
private void generateHoursWorkedReport() {
|
||||
final Team selectedTeam = teamComboBox.getValue();
|
||||
final Week selectedWeek = weekComboBox.getValue();
|
||||
|
||||
if (selectedTeam == null || selectedWeek == null) {
|
||||
Notification.show("Por favor, selecciona un equipo y una semana para generar el reporte.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<TimesheetEntry> timesheetEntryList =
|
||||
timesheetService.findListHoursWorkedEmployee(selectedWeek.from(), selectedWeek.to()).stream()
|
||||
.filter(e -> e.getEmployee().getTeam().getId().equals(selectedTeam.getId()))
|
||||
.toList();
|
||||
|
||||
if (timesheetEntryList.isEmpty()) {
|
||||
Notification.show("No hay horas trabajadas disponibles para generar el reporte.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<Map<String, Object>> data = timesheetEntryList.stream()
|
||||
.map(e -> {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("ID", e.getId().toString());
|
||||
map.put("Employee ID", e.getEmployee().getId().toString());
|
||||
map.put("Empleado", e.getEmployee().getFirstName() + " "
|
||||
+ e.getEmployee().getLastName());
|
||||
map.put("Horas Trabajadas", e.getHours());
|
||||
map.put("Horas Pendientes", 40 - e.getHours());
|
||||
map.put("Observaciones", "");
|
||||
return map;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
grid.setItems(data);
|
||||
int weekNumber = weekComboBox.getValue().from().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
generateExcelDownloadLink(data, weekNumber);
|
||||
}
|
||||
|
||||
private void generateExcelDownloadLink(final List<Map<String, Object>> data, final int weekNumber) {
|
||||
try {
|
||||
final List<String> headers = List.of("Empleado", "Horas Trabajadas", "Horas Pendientes", "Observaciones");
|
||||
final String selectedTeam = teamComboBox.getValue().getName();
|
||||
final byte[] excelBytes = reportService.writeAsExcel(
|
||||
"hours_worked_report", headers, data, selectedTeam, weekNumber, currentYear);
|
||||
final StreamResource excelResource = new StreamResource("hours_worked_report.xlsx",
|
||||
() -> new ByteArrayInputStream(excelBytes));
|
||||
|
||||
if (downloadLink == null) {
|
||||
downloadLink = new Anchor(excelResource, "Descargar Reporte en Excel");
|
||||
downloadLink.getElement().setAttribute("download", true);
|
||||
getCurrentPageLayout().add(downloadLink);
|
||||
} else {
|
||||
downloadLink.setHref(excelResource);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error al generar el reporte de horas trabajadas en Excel.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
log.error("Error generating report", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package com.primefactorsolutions.views.util;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@UtilityClass
|
||||
public class AuthUtils {
|
||||
public static boolean isUser(final AuthenticationContext authenticationContext) {
|
||||
return authenticationContext.getAuthenticatedUser(UserDetails.class).isPresent();
|
||||
}
|
||||
|
||||
public static boolean isAdmin(final AuthenticationContext authenticationContext) {
|
||||
return authenticationContext.getAuthenticatedUser(UserDetails.class)
|
||||
.map(u ->
|
||||
u.getAuthorities().stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.anyMatch("ROLE_ADMIN"::equals)).orElse(false);
|
||||
}
|
||||
|
||||
public static Optional<UUID> getEmployeeId(final AuthenticationContext authenticationContext) {
|
||||
return authenticationContext.getAuthenticatedUser(UserDetails.class)
|
||||
.map(u -> u instanceof Employee
|
||||
? ((Employee) u).getId()
|
||||
: null);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package com.primefactorsolutions.views.util;
|
||||
|
||||
import com.vaadin.flow.component.HasSize;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class ComponentUtils {
|
||||
|
||||
public static <T extends HasSize> T withFullWidth(final T component) {
|
||||
component.setWidthFull();
|
||||
return component;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package com.primefactorsolutions.views.util;
|
||||
|
||||
import com.primefactorsolutions.model.HasLabel;
|
||||
import org.vaadin.firitin.components.combobox.VComboBox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityComboBox<T extends HasLabel> extends VComboBox<T> {
|
||||
public EntityComboBox(final String label) {
|
||||
super(label);
|
||||
this.setItemLabelGenerator(HasLabel::getLabel);
|
||||
}
|
||||
|
||||
public EntityComboBox(final String label, final List<T> items) {
|
||||
super(label, items);
|
||||
this.setItemLabelGenerator(HasLabel::getLabel);
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package com.primefactorsolutions.views.util;
|
||||
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
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 com.vaadin.flow.component.menubar.MenuBarVariant;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@UtilityClass
|
||||
public class MenuBarUtils {
|
||||
public static final Map<String, VaadinIcon> ICON_MAP = Map.of(
|
||||
"View", VaadinIcon.EYE,
|
||||
"Edit", VaadinIcon.PENCIL,
|
||||
"Copy", VaadinIcon.COPY,
|
||||
"Email", VaadinIcon.ENVELOPE,
|
||||
"Download", VaadinIcon.DOWNLOAD
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static MenuBar menuBar(final Map<Pair<String, VaadinIcon>,
|
||||
ComponentEventListener<ClickEvent<MenuItem>>> entries) {
|
||||
final MenuBar menuBar = new MenuBar();
|
||||
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||
|
||||
entries.forEach((key, value) -> {
|
||||
final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, key.getValue(), key.getKey());
|
||||
viewItem.addClickListener(value);
|
||||
});
|
||||
|
||||
return menuBar;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static MenuBar menuBar(final Pair<String, ComponentEventListener<ClickEvent<MenuItem>>>... entries) {
|
||||
final MenuBar menuBar = new MenuBar();
|
||||
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||
|
||||
for (Pair<String, ComponentEventListener<ClickEvent<MenuItem>>> entry: entries) {
|
||||
final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar,
|
||||
ICON_MAP.get(entry.getKey()), entry.getKey());
|
||||
viewItem.addClickListener(entry.getValue());
|
||||
}
|
||||
|
||||
return menuBar;
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ spring.mustache.check-template-location = false
|
||||
vaadin.launch-browser=true
|
||||
# To improve the performance during development.
|
||||
# For more information https://vaadin.com/docs/latest/integrations/spring/configuration#special-configuration-parameters
|
||||
vaadin.allowed-packages = com.vaadin,org.vaadin,com.primefactorsolutions,com.hilerio.ace,com.flowingcode.vaadin,org.vaadin.firitin,org.vaadin.addons.stefan,io.overcoded
|
||||
vaadin.allowed-packages = com.vaadin,org.vaadin,com.primefactorsolutions,com.hilerio.ace,com.flowingcode.vaadin,org.vaadin.firitin,org.vaadin.addons.stefan
|
||||
|
||||
spring.mail.host=smtp.primefactorsolutions.com
|
||||
spring.mail.username=${SMTP_USER}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user