Compare commits

..

No commits in common. "main" and "Listado-General-Vacaciones" have entirely different histories.

111 changed files with 3570 additions and 7324 deletions

1
.gitignore vendored
View File

@ -19,4 +19,3 @@ drivers/
# Error screenshots generated by TestBench for failed integration tests
error-screenshots/
webpack.generated.js
*.env

Binary file not shown.

2089
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,22 +6,22 @@
"@f0rce/ace-widget": "1.0.2",
"@polymer/polymer": "3.5.1",
"@vaadin-component-factory/vcf-pdf-viewer": "2.0.1",
"@vaadin/bundles": "24.5.1",
"@vaadin/bundles": "24.4.2",
"@vaadin/common-frontend": "0.0.19",
"@vaadin/polymer-legacy-adapter": "24.5.1",
"@vaadin/react-components": "24.5.1",
"@vaadin/react-components-pro": "24.5.1",
"@vaadin/router": "2.0.0",
"@vaadin/polymer-legacy-adapter": "24.4.2",
"@vaadin/react-components": "24.4.2",
"@vaadin/react-components-pro": "24.4.2",
"@vaadin/router": "1.7.5",
"@vaadin/vaadin-development-mode-detector": "2.0.7",
"@vaadin/vaadin-lumo-styles": "24.5.1",
"@vaadin/vaadin-material-styles": "24.5.1",
"@vaadin/vaadin-themable-mixin": "24.5.1",
"@vaadin/vaadin-usage-statistics": "2.1.3",
"@vaadin/vaadin-lumo-styles": "24.4.2",
"@vaadin/vaadin-material-styles": "24.4.2",
"@vaadin/vaadin-themable-mixin": "24.4.2",
"@vaadin/vaadin-usage-statistics": "2.1.2",
"construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3",
"lit": "3.1.4",
"print-js": "1.6.0",
"proj4": "2.12.1",
"proj4": "2.11.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.23.1"
@ -51,22 +51,22 @@
"@f0rce/ace-widget": "1.0.2",
"@polymer/polymer": "3.5.1",
"@vaadin-component-factory/vcf-pdf-viewer": "2.0.1",
"@vaadin/bundles": "24.5.1",
"@vaadin/bundles": "24.4.2",
"@vaadin/common-frontend": "0.0.19",
"@vaadin/polymer-legacy-adapter": "24.5.1",
"@vaadin/react-components": "24.5.1",
"@vaadin/react-components-pro": "24.5.1",
"@vaadin/router": "2.0.0",
"@vaadin/polymer-legacy-adapter": "24.4.2",
"@vaadin/react-components": "24.4.2",
"@vaadin/react-components-pro": "24.4.2",
"@vaadin/router": "1.7.5",
"@vaadin/vaadin-development-mode-detector": "2.0.7",
"@vaadin/vaadin-lumo-styles": "24.5.1",
"@vaadin/vaadin-material-styles": "24.5.1",
"@vaadin/vaadin-themable-mixin": "24.5.1",
"@vaadin/vaadin-usage-statistics": "2.1.3",
"@vaadin/vaadin-lumo-styles": "24.4.2",
"@vaadin/vaadin-material-styles": "24.4.2",
"@vaadin/vaadin-themable-mixin": "24.4.2",
"@vaadin/vaadin-usage-statistics": "2.1.2",
"construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3",
"lit": "3.1.4",
"print-js": "1.6.0",
"proj4": "2.12.1",
"proj4": "2.11.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.23.1"
@ -91,7 +91,7 @@
"workbox-core": "7.1.0",
"workbox-precaching": "7.1.0"
},
"hash": "1a0f17d48b329307b5862bc57499307d1b89f7d89260121c2b7189f76957c436"
"hash": "0962b593830d75a70657cde2e956e8c49b704d39c45bfd150cda9fdac99f1c6e"
},
"overrides": {
"@vaadin/bundles": "$@vaadin/bundles",

64
pom.xml
View File

@ -11,8 +11,7 @@
<properties>
<java.version>21</java.version>
<vaadin.version>24.5.1</vaadin.version>
<vaadin-maven-plugin.version>24.4.6</vaadin-maven-plugin.version>
<vaadin.version>24.4.6</vaadin.version>
</properties>
<parent>
@ -120,15 +119,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>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
@ -191,21 +181,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>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.flowingcode.addons</groupId>
<artifactId>simple-timer</artifactId>
@ -265,49 +240,16 @@
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>9.0.1</version>
</dependency>
</dependencies>
<build>
<defaultGoal>spring-boot:run</defaultGoal>
<plugins>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>9.0.1</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
<phase>initialize</phase>
</execution>
</executions>
<configuration>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<includeOnlyProperties>
<includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty>
<includeOnlyProperty>^git.commit.id.(abbrev|full)$</includeOnlyProperty>
</includeOnlyProperties>
<commitIdGenerationMode>full</commitIdGenerationMode>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
@ -337,7 +279,7 @@
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin-maven-plugin.version}</version>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>
@ -398,7 +340,7 @@
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin-maven-plugin.version}</version>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>

Binary file not shown.

Binary file not shown.

View File

@ -4,6 +4,7 @@ import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.theme.Theme;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* The entry point of the Spring Boot application.
*

View File

@ -1,19 +0,0 @@
package com.primefactorsolutions.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
@Configuration
public class PropertiesConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
final PropertySourcesPlaceholderConfigurer propsConfig = new PropertySourcesPlaceholderConfigurer();
propsConfig.setLocation(new ClassPathResource("git.properties"));
propsConfig.setIgnoreResourceNotFound(true);
propsConfig.setIgnoreUnresolvablePlaceholders(true);
return propsConfig;
}
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -1,6 +1,6 @@
package com.primefactorsolutions.model;
public enum ExamStatus {
public enum AssessmentStatus {
CREATED,
STARTED,
COMPLETED

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -1,4 +0,0 @@
package com.primefactorsolutions.model;
public record Certification(String title, Integer year) {
}

View File

@ -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
}

View File

@ -1,193 +1,147 @@
package com.primefactorsolutions.model;
package com.primefactorsolutions.model;
import com.google.common.collect.Lists;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
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 org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.time.LocalDate;
import java.util.Collection;
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 {
private String username;
private String firstName;
private String lastName;
private LocalDate birthday;
private String birthCity;
private String age;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity implements UserDetails, HasLabel {
private String residenceAddress;
private String localAddress;
private String phoneNumber;
private String personalEmail;
private String position;
@ManyToOne
@JoinColumn(name = "team_id", nullable = false)
private Team team;
private String emergencyCName;
private String emergencyCAddress;
private String emergencyCPhone;
private String emergencyCEmail;
private String numberOfChildren;
private String username;
@NotNull(message = "El nombre no puede estar vacío")
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre solo debe contener letras")
private String firstName;
@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;
private String age;
@Size(max = 50, message = "La dirección de residencia no debe exceder 50 caracteres")
private String residenceAddress;
@Size(max = 30, message = "La dirección local no debe exceder 100 caracteres")
@Pattern(regexp = "^[a-zA-Z -]+$", message = "La dirección local solo debe contener letras y guion")
private String localAddress;
@Pattern(regexp = "^[0-9]+$", message = "El número de teléfono debe contener solo números")
private String phoneNumber;
@Email(message = "El correo personal no tiene un formato válido")
private String personalEmail;
@Pattern(regexp = "^[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;
private String ci;
private String issuedIn;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El cargo solo debe contener letras")
private String position;
@ManyToOne
@JoinColumn(name = "team_id", nullable = false)
private Team team;
private String pTitle1;
private String pTitle2;
private String pTitle3;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre y apellido de contacto"
+ " de emergencia solo debe contener letras")
private String emergencyCName;
private String emergencyCAddress;
@Pattern(regexp = "^[0-9]+$", message = "El teléfono de contacto de emergencia "
+ " debe contener solo números")
private String emergencyCPhone;
@Email(message = "El correo de contacto de emergencia no tiene un formato válido")
private String emergencyCEmail;
private String numberOfChildren;
@Pattern(regexp = "^[a-zA-Z0-9]+$", message = "El CI debe contener solo letras y números")
private String ci;
private String issuedIn;
private String pStudy1;
private String pStudy2;
private String pStudy3;
@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 recognition;
private String achievements;
@Type(JsonType.class)
@Column(columnDefinition = "json")
@ColumnDefault("JSON_ARRAY()")
private List<Language> languages = Lists.newArrayList();
@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;
private String certification1;
private String certification2;
private String certification3;
private String certification4;
private LocalDate dateOfEntry;
private LocalDate dateOfExit;
private String recognition;
private String achievements;
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 = "^[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;
@Column(columnDefinition = "TEXT")
private String profileImage;
@Enumerated(EnumType.STRING)
private Status status;
private String language;
private String languageLevel;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Lists.newArrayList(new SimpleGrantedAuthority("ROLE_" + this.role.name()));
private String cod;
private String leadManager;
private String project;
private LocalDate dateOfEntry;
private LocalDate dateOfExit;
private String contractType;
private String seniority;
private String salary;
private String bankName;
private String accountNumber;
private String gpss;
private String sss;
private String beneficiaries;
@Column(columnDefinition = "TEXT")
private String profileImage;
@Enumerated(EnumType.STRING)
private Status status;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Lists.newArrayList();
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public enum Status {
ACTIVE,
INACTIVE
}
@Enumerated(EnumType.STRING)
private MaritalStatus maritalStatus;
public enum MaritalStatus {
SINGLE,
MARRIED,
WIDOWED,
DIVORCED
}
@Enumerated(EnumType.STRING)
private Gender gender;
public enum Gender {
MALE,
FEMALE
}
public Status getStatus() {
return status;
}
public void setStatus(final Status status) {
this.status = status;
}
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
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
}
public enum MaritalStatus {
SINGLE,
MARRIED,
WIDOWED,
DIVORCED
}
public enum ContractType {
CONTRATO_LABORAL,
CONTRATO_CIVIL_O_SERVICIOS,
CONTRATO_PLAZO_FIJO,
CONSULTORIA_INTERNA,
CONSULTORIA_EXTERNA,
CONTRATO_MIXTO,
OTROS
}
}

View File

@ -1,8 +0,0 @@
package com.primefactorsolutions.model;
public enum EmployeePosition {
JUNIOR_DEV,
JUNIOR_QA,
SENIOR_DEV,
SENIOR_QA
}

View File

@ -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;
}

View File

@ -1,5 +0,0 @@
package com.primefactorsolutions.model;
public interface HasLabel {
String getLabel();
}

View File

@ -1,4 +0,0 @@
package com.primefactorsolutions.model;
public record Language(String name, Proficiency proficiency) {
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -1,7 +0,0 @@
package com.primefactorsolutions.model;
public enum Proficiency {
BASIC,
ADVANCED,
FLUENT
}

View File

@ -1,4 +0,0 @@
package com.primefactorsolutions.model;
public record SkillEvaluation(SkillType skillType, SkillLevel level, String comments) {
}

View File

@ -1,8 +0,0 @@
package com.primefactorsolutions.model;
public enum SkillLevel {
NEEDS_IMPROVEMENT,
FAIR,
VERY_GOOD,
EXCELLENT
}

View File

@ -1,8 +0,0 @@
package com.primefactorsolutions.model;
public enum SkillType {
TECHNICAL_KNOWLEDGE,
ASSERTIVENESS,
COMPLETENESS_AND_CONCISENESS,
COMMUNICATION
}

View File

@ -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;
}

View File

@ -1,32 +0,0 @@
package com.primefactorsolutions.model;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import lombok.AllArgsConstructor;
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 {
@Enumerated(EnumType.STRING)
private TimeOffRequestType category;
private LocalDate date;
private Double duration;
private Double expiration;
@Enumerated(EnumType.STRING)
private Type type;
public enum Type {
FIXED,
MOVABLE,
OTHER
}
}

View File

@ -6,7 +6,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.util.Date;
@Data
@Entity
@ -20,11 +20,24 @@ public class TimeOffRequest extends BaseEntity {
@Enumerated(EnumType.STRING)
private TimeOffRequestType category;
@Enumerated(EnumType.STRING)
private TimeOffRequestStatus state;
private Status state;
private Double availableDays;
private LocalDate expiration;
private LocalDate startDate;
private LocalDate endDate;
private Date expiration;
private Date startDate;
private Date endDate;
private Double daysToBeTake;
private Double daysBalance;
public enum Status {
ALL,
TAKEN,
REQUESTED,
APPROVED,
IN_USE,
UNDER_REVIEW,
PENDING,
REJECTED,
COMPLETED,
CANCELLED,
EXPIRED,
}
}

View File

@ -1,13 +0,0 @@
package com.primefactorsolutions.model;
public enum TimeOffRequestStatus {
TODOS,
TOMADO,
APROBADO,
EN_USO,
PENDIENTE,
RECHAZADO,
VENCIDO,
SOLICITADO,
}

View File

@ -1,29 +1,10 @@
package com.primefactorsolutions.model;
public enum TimeOffRequestType {
AÑO_NUEVO,
LUNES_CARNAVAL,
MARTES_CARNAVAL,
VIERNES_SANTO,
DIA_DEL_TRABAJADOR,
DIA_DE_LA_INDEPENDENCIA,
NAVIDAD,
DIA_DEL_ESTADO_PLURINACIONAL,
CORPUS_CHRISTI,
AÑO_NUEVO_ANDINO,
ANIVERSARIO_DEPARTAMENTAL,
DIA_DE_TODOS_LOS_DIFUNTOS,
CUMPLEAÑOS,
MATERNIDAD,
PATERNIDAD,
MATRIMONIO,
DUELO_1ER_GRADO,
DUELO_2ER_GRADO,
DIA_DEL_PADRE,
DIA_DE_LA_MADRE,
DIA_DE_LA_MUJER_INTERNACIONAL,
DIA_DE_LA_MUJER_NACIONAL,
PERMISOS_DE_SALUD,
VACACION_GESTION_ACTUAL,
VACACION_GESTION_ANTERIOR,
ALL,
VACATION,
MATERNITY,
BIRTHDAY,
FIXED_HOLIDAY,
OTHER
}

View 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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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> {
}

View File

@ -3,14 +3,9 @@ package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
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);
List<Employee> findByTeamName(String teamName);
}
}

View File

@ -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> {
}

View File

@ -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);
}

View File

@ -1,19 +1,11 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.TimeOffRequest;
import com.primefactorsolutions.model.TimeOffRequestStatus;
import com.primefactorsolutions.model.TimeOffRequestType;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
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);
List<TimeOffRequest> findByState(TimeOffRequestStatus state);
void deleteByEmployeeIdAndCategory(UUID employeeId, TimeOffRequestType category);
}

View File

@ -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);
}

View File

@ -1,101 +0,0 @@
package com.primefactorsolutions.service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.primefactorsolutions.model.Employee;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@Service
@Slf4j
public class AccountService {
private final EmailService emailService;
private final EmployeeService employeeService;
private final String secret;
public AccountService(final EmailService emailService, final EmployeeService employeeService,
@Value("${application.jwtSecret}") final String secret) {
this.emailService = emailService;
this.employeeService = employeeService;
this.secret = secret;
}
public void sendResetPasswordEmail(final String personalEmail) {
final Employee employee = employeeService.getEmployeeByPersonalEmail(personalEmail);
if (employee == null) {
log.warn("Could not find employee for email {}", personalEmail);
return;
}
final String link = createResetPasswordLink(employee.getUsername());
final String content = "Visit this link to reset your password: " + link;
emailService.sendEmail(personalEmail, "PFS - Reset Password", content);
}
public void resetPassword(final String username, final String newPassword, final String token) {
DecodedJWT decodedJWT;
try {
Algorithm algorithm = Algorithm.HMAC512(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("pfs")
.build();
decodedJWT = verifier.verify(token);
final Instant expiry = decodedJWT.getExpiresAtAsInstant();
final String claim = decodedJWT.getClaim("username").asString();
if (expiry.isBefore(Instant.now())
|| !username.equals(claim)) {
log.warn("token invalid {} {} {}", username, claim, expiry);
return;
}
} catch (JWTVerificationException e) {
log.warn("error updating password", e);
return;
}
final Employee employee = employeeService.getDetachedEmployeeByUsername(username);
if (employee == null) {
log.warn("Could not find employee for username {}", username);
return;
}
if (StringUtils.isBlank(newPassword) || newPassword.length() < 8) {
throw new IllegalArgumentException("New password should be at least 8 chars long");
}
employeeService.updatePassword(employee, newPassword);
log.info("updated password for {}", username);
}
private String createResetPasswordLink(final String username) {
String token = "";
try {
Algorithm algorithm = Algorithm.HMAC512(secret);
token = JWT.create()
.withIssuer("pfs")
.withClaim("username", username)
.withExpiresAt(Instant.now().plus(1, ChronoUnit.HOURS))
.sign(algorithm);
} catch (JWTCreationException e) {
throw new RuntimeException(e);
}
return String.format("https://intra.primefactorsolutions.com/reset-password?username=%s&token=%s", username,
token);
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -1,32 +0,0 @@
package com.primefactorsolutions.service;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
@Slf4j
public class EmailService {
public static final String NO_REPLY_PRIMEFACTORSOLUTIONS_COM = "no-reply@primefactorsolutions.com";
private final JavaMailSender emailSender;
public void sendEmail(final String email, final String title, final String messageContent) {
try {
final SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(NO_REPLY_PRIMEFACTORSOLUTIONS_COM);
message.setBcc(NO_REPLY_PRIMEFACTORSOLUTIONS_COM);
message.setTo(email);
message.setSubject(title);
message.setText(messageContent);
emailSender.send(message);
log.info("Sent email to {}", email);
} catch (Exception e) {
log.error("Error sending email to {}", email, e);
throw e;
}
}
}

View File

@ -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;
@ -19,18 +18,12 @@ import java.util.Collections;
@Service
@AllArgsConstructor
public class EmployeeService {
private static final String USERPASSWORD = "userPassword";
private static final String OBJECTCLASS = "objectclass";
private static final String ORGANIZATIONAL_PERSON = "organizationalPerson";
private static final String INET_ORG_PERSON = "inetOrgPerson";
private static final String TOP = "top";
private static final String PERSON = "person";
public static final String BASE_DN = "dc=primefactorsolutions,dc=com";
private final EmployeeRepository employeeRepository;
private final LdapTemplate ldapTemplate;
private final EntityManager entityManager;
public static final String BASE_DN = "dc=primefactorsolutions,dc=com";
protected Name buildDn(final Employee employee) {
return LdapNameBuilder.newInstance(BASE_DN)
.add("ou", "users")
@ -38,10 +31,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);
@ -53,20 +42,9 @@ public class EmployeeService {
return null;
}
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();
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,16 +58,11 @@ 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);
}
public Employee getEmployeeByPersonalEmail(final String email) {
return employeeRepository.findByPersonalEmail(email).orElse(null);
}
public Employee createOrUpdate(final Employee employee) {
if (employee.getId() == null) {
final Name dn = buildDn(employee);
@ -101,41 +74,34 @@ public class EmployeeService {
}
public Employee getEmployee(final UUID id) {
final Optional<Employee> employee = employeeRepository.findById(id);
Optional<Employee> employee = employeeRepository.findById(id);
return employee.orElse(null);
}
private Attributes buildAttributes(final Employee employee) {
final Attributes attrs = new BasicAttributes();
final BasicAttribute ocattr = new BasicAttribute(OBJECTCLASS);
ocattr.add(TOP);
ocattr.add(PERSON);
ocattr.add(ORGANIZATIONAL_PERSON);
ocattr.add(INET_ORG_PERSON);
final BasicAttribute ocattr = new BasicAttribute("objectclass");
ocattr.add("top");
ocattr.add("person");
ocattr.add("organizationalPerson");
ocattr.add("inetOrgPerson");
attrs.put(ocattr);
attrs.put("cn", String.format("%s %s", employee.getFirstName(), employee.getLastName()));
attrs.put("sn", String.format("%s %s", employee.getFirstName(), employee.getLastName()));
attrs.put("uid", employee.getUsername());
attrs.put(USERPASSWORD, String.format("%s%s", employee.getUsername(), 123));
attrs.put("userpassword", String.format("%s%s", employee.getUsername(), 123));
return attrs;
}
public void updatePassword(final Employee employee, final String newPassword) {
final Attribute attr = new BasicAttribute(USERPASSWORD, newPassword);
public void updatePassword(final Employee employee) {
final Attribute attr = new BasicAttribute("userpassword", employee.getUsername() + "123");
final ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
ldapTemplate.modifyAttributes(buildDn(employee), new ModificationItem[] {item});
}
public List<Employee> findAllEmployees() {
List<Employee> employees = employeeRepository.findAll();
Collections.reverse(employees);
return employees;
}
public List<Employee> findEmployeesByTeam(final String teamName) {
return employeeRepository.findByTeamName(teamName);
return employeeRepository.findAll();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -2,7 +2,6 @@ package com.primefactorsolutions.service;
import com.openhtmltopdf.pdfboxout.PdfBoxRenderer;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import com.primefactorsolutions.model.Employee;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
@ -10,115 +9,15 @@ import freemarker.template.TemplateExceptionHandler;
import lombok.SneakyThrows;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@Service
public class ReportService {
public ReportService() {
}
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)
throws IOException {
return createExcelFile(reportName, headers, data, selectedTeam, weekNumber, currentYear);
}
private byte[] createExcelFile(final String reportName, final List<String> headers,
final List<Map<String, Object>> data, final String selectedTeam,
final int weekNumber, final int currentYear)
throws IOException {
try (Workbook workbook = new XSSFWorkbook();
ByteArrayOutputStream os = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet(reportName);
// Crear encabezados
// Crear una fila para el rótulo "Reporte por equipo"
Row titleRow = sheet.createRow(0); // Fila 0 para el rótulo
Cell titleCell = titleRow.createCell(0);
// Concatenar el nombre del equipo al rótulo
String titleText = "Informe: " + weekNumber + "/" + currentYear;
titleCell.setCellValue(titleText);
// Estilo del rótulo
CellStyle titleStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 14); // Tamaño de la fuente
titleStyle.setFont(titleFont);
titleCell.setCellStyle(titleStyle);
// Fusionar celdas para el rótulo
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, headers.size() - 1)); // Ajusta el rango de celdas
// Crear filas adicionales con la información solicitada
Row asuntoRow = sheet.createRow(1); // Fila 1: Asunto
asuntoRow.createCell(0).setCellValue("Asunto: Informe semanal de horas trabajadas");
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, headers.size() - 1));
Row semanaRow = sheet.createRow(2); // Fila 2: Semana
semanaRow.createCell(0).setCellValue("Semana: " + weekNumber); // Puedes insertar una fecha real aquí
sheet.addMergedRegion(new CellRangeAddress(2, 2, 0, headers.size() - 1));
Row horasCumplirRow = sheet.createRow(3); // Fila 3: Horas a cumplir
horasCumplirRow.createCell(0).setCellValue("Horas a cumplir: 40 horas"); // Puedes agregar las horas reales
sheet.addMergedRegion(new CellRangeAddress(3, 3, 0, headers.size() - 1));
Row teamLeadRow = sheet.createRow(4); // Fila 4: Team Lead
teamLeadRow.createCell(0).setCellValue("Team Lead: "); // Solo texto
sheet.addMergedRegion(new CellRangeAddress(4, 4, 0, headers.size() - 1));
// Crear encabezados (fila 5)
Row headerRow = sheet.createRow(5); // Los encabezados empiezan en la fila 5
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
for (int i = 0; i < headers.size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers.get(i));
cell.setCellStyle(headerStyle);
}
// Crear filas de datos (a partir de la fila 6)
for (int i = 0; i < data.size(); i++) {
Row dataRow = sheet.createRow(i + 6); // Los datos empiezan después de la fila de encabezados
Map<String, Object> rowData = data.get(i);
int cellIndex = 0;
for (String key : headers) {
Cell cell = dataRow.createCell(cellIndex++);
Object value = rowData.get(key);
if (value != null) {
if (value instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
}
} else {
cell.setCellValue(""); // Manejo de valores nulos
}
}
}
workbook.write(os);
return os.toByteArray();
} catch (IOException e) {
System.err.println("Error al generar el archivo Excel: " + e.getMessage());
throw e; // Propagar la excepción después de registrarla
}
}
@SneakyThrows
public byte[] writeAsPdf(final String reportName, final Object model) {
@ -170,81 +69,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);
}
}
}
}

View File

@ -5,7 +5,6 @@ import com.primefactorsolutions.repositories.TimeOffRequestRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import java.util.Optional;
@ -19,20 +18,12 @@ public class TimeOffRequestService {
timeOffRequestRepository.save(newTimeOffRequest);
}
public void deleteTimeOffRequestByEmployeeAndCategory(final UUID employeeId, final TimeOffRequestType category) {
timeOffRequestRepository.deleteByEmployeeIdAndCategory(employeeId, category);
}
public void saveAll(final List<TimeOffRequest> requests) {
timeOffRequestRepository.saveAll(requests);
}
public void deleteTimeOffRequest(final UUID id) {
timeOffRequestRepository.deleteById(id);
}
public List<TimeOffRequest> findAllTimeOffRequests() {
return timeOffRequestRepository.findByOrderByUpdatedDesc();
return timeOffRequestRepository.findAll();
}
public TimeOffRequest findTimeOffRequest(final UUID id) {
@ -40,49 +31,7 @@ public class TimeOffRequestService {
return timeOffRequest.orElse(null);
}
public List<TimeOffRequest> findRequestsByState(final TimeOffRequestStatus state) {
return timeOffRequestRepository.findByState(state);
}
public List<TimeOffRequest> findRequestsByEmployeeId(final UUID idEmployee) {
return timeOffRequestRepository.findByEmployeeId(idEmployee);
}
public Optional<TimeOffRequest> findByEmployeeAndState(final UUID employeeId, final TimeOffRequestStatus state) {
return timeOffRequestRepository.findByEmployeeIdAndState(employeeId, state);
}
public List<TimeOffRequest> findByEmployeeAndCategory(final UUID employeeId, final TimeOffRequestType category) {
return timeOffRequestRepository.findByEmployeeIdAndCategory(employeeId, category);
}
public void updateRequestStatuses() {
List<TimeOffRequest> requests = findAllTimeOffRequests();
LocalDate now = LocalDate.now();
LocalDate startOfYear = LocalDate.of(now.getYear(), 1, 1);
for (TimeOffRequest request : requests) {
if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL && now.isEqual(startOfYear)) {
deleteTimeOffRequestByEmployeeAndCategory(
request.getEmployee().getId(),
TimeOffRequestType.VACACION_GESTION_ANTERIOR
);
request.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR);
}
if (request.getState() == TimeOffRequestStatus.APROBADO
|| request.getState() == TimeOffRequestStatus.EN_USO) {
LocalDate startDate = request.getStartDate();
LocalDate endDate = request.getEndDate();
if (now.isAfter(endDate)) {
request.setState(TimeOffRequestStatus.TOMADO);
} else if (now.isEqual(startDate) || (now.isAfter(startDate) && now.isBefore(endDate))) {
request.setState(TimeOffRequestStatus.EN_USO);
}
}
}
saveAll(requests);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,4 @@
package com.primefactorsolutions.views;
public class CalendarView {
}

View File

@ -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);
});
}

View File

@ -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);
}
}

View File

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

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,487 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.ReportService;
import com.vaadin.componentfactory.pdfviewer.PdfViewer;
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.dialog.Dialog;
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.textfield.EmailField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
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 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.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Employee")
@Route(value = "/employees/:employeeId?/:action?", layout = MainLayout.class)
public class EmployeeView extends BeanValidationForm<Employee> implements HasUrlParameter<String> {
private final EmployeeService employeeService;
private final ReportService reportService;
// 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 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", 20, false);
private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false);
private final TextField localAddress = createTextField("Dep/Provincia de Residencia", 10, false);
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
private final TextField numberOfChildren = createTextField("Numero de Hijos", 3, false);
private final TextField ci = createTextField("CI", 30, false);
private final TextField issuedIn = createTextField("Expedido en ", 30, false);
private final TextField phoneNumber = createTextField("Teléfono", 8, false);
private final EmailField personalEmail = createEmailField("E-mail");
private final TextField cod = createTextField("Codigo de Empleado", 30, false);
private final TextField position = createTextField("Cargo", 30, false);
private final TextField team = createTextField("Equipo", 30, false);
private final TextField leadManager = createTextField("Lead/Manager", 30, false);
private final TextField project = createTextField("Proyecto", 30, false);
private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false);
private final TextField emergencyCAddress = createTextField("Dirección de Contacto", 50, false);
private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false);
private final EmailField emergencyCEmail = createEmailField("Email de Contacto");
private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload upload = new Upload(buffer);
private final Image profileImagePreview = new Image();
//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 TextField language = createTextField("Idioma", 30, false);
private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false);
//INFORMACION DE CONTRATACION
private final VDatePicker dateOfEntry = new VDatePicker("Fecha de Ingreso");
private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro");
private final TextField contractType = createTextField("Tipo de Contratación", 30, false);
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 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 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();
//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");
//TITULOS PARA INFORMACIÓN ADMINISTRATIVA
private final H2 infoAdm = new H2("Información Administrativa");
private final H3 infoCont = new H3("Información de Contratación");
private final H3 datBanc = new H3("Datos Bancados");
private final H3 datGest = new H3("Datos Gestora Pública y Seguro Social");
public EmployeeView(final EmployeeService employeeService, final ReportService reportService) {
super(Employee.class);
this.employeeService = employeeService;
this.reportService = reportService;
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
configureComponents();
addClassName("main-layout");
}
private void configureComponents() {
phoneNumber.setValueChangeMode(ValueChangeMode.EAGER);
phoneNumber.addValueChangeListener(e -> validatePhoneNumber(phoneNumber, e.getValue()));
emergencyCPhone.setValueChangeMode(ValueChangeMode.EAGER);
emergencyCPhone.addValueChangeListener(e -> validatePhoneNumber(emergencyCPhone, e.getValue()));
configureUpload();
saveButton.setVisible(true);
editButton.setVisible(true);
reportButton.setVisible(true);
birthday.addValueChangeListener(event -> calculateAge());
reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
var employee = getEntity();
byte[] pdfContent = reportService.writeAsPdf("ficha", employee);
var resource = new StreamResource("ficha.pdf", () -> new ByteArrayInputStream(pdfContent));
pdfViewer.setSrc(resource);
dialog.open();
});
initDialog();
}
private void calculateAge() {
if (birthday.getValue() != null) {
int currentYear = java.time.LocalDate.now().getYear();
int birthYear = birthday.getValue().getYear();
int ages = currentYear - birthYear;
age.setValue(String.valueOf(ages));
System.out.println(age);
}
}
private void configureUpload() {
upload.setAcceptedFileTypes("image/jpeg", "image/png");
upload.setMaxFileSize(1024 * 1024);
upload.addSucceededListener(event -> {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
buffer.getInputStream().transferTo(outputStream);
byte[] imageBytes = outputStream.toByteArray();
String base64Image = Base64.getEncoder().encodeToString(imageBytes);
getEntity().setProfileImage(base64Image);
profileImagePreview.setSrc("data:image/jpeg;base64," + base64Image);
profileImagePreview.setMaxWidth("150px");
profileImagePreview.setMaxHeight("150px");
} catch (IOException e) {
Notification.show("Error al subir la imagen.");
}
});
}
private void validatePhoneNumber(final TextField textField, final String value) {
if (!value.matches("\\d*")) {
textField.setErrorMessage(PHONE_NUMBER_ERROR_MESSAGE);
}
}
private void initDialog() {
pdfViewer.setSizeFull();
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");
dialogLayout.getStyle().set("display", "flex");
dialogLayout.getStyle().set("flex-direction", "column");
dialogLayout.setPadding(false);
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);
}
private ComboBox<Employee.MaritalStatus> createMaritalStatusComboBox() {
ComboBox<Employee.MaritalStatus> comboBox = new ComboBox<>("Estado Civil");
comboBox.setItems(Employee.MaritalStatus.values());
comboBox.setItemLabelGenerator(Employee.MaritalStatus::name);
return comboBox;
}
private ComboBox<Employee.Status> createStatusComboBox() {
ComboBox<Employee.Status> comboBox = new ComboBox<>("Estado");
comboBox.setItems(Employee.Status.values());
comboBox.setItemLabelGenerator(Employee.Status::name);
comboBox.setRequiredIndicatorVisible(true); // Indicador de campo requerido
return comboBox;
}
private VerticalLayout createContentLayout() {
VerticalLayout contentLayout = new VerticalLayout();
contentLayout.setWidth("100%");
return contentLayout;
}
private TextField createTextField(final String label, final int maxLength, final boolean required) {
TextField textField = new TextField(label);
textField.setWidthFull();
textField.setMaxLength(maxLength);
textField.setRequired(required);
return textField;
}
private EmailField createEmailField(final String label) {
EmailField emailField = new EmailField(label);
emailField.setWidthFull();
emailField.setMaxLength(30);
return emailField;
}
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());
comboBox.setItemLabelGenerator(Employee.Gender::name);
comboBox.setRequiredIndicatorVisible(true);
return comboBox;
}
private boolean validateForm() {
return !firstName.isEmpty() && !lastName.isEmpty() && status.getValue() != null;
}
private void saveEmployee() {
if (validateForm()) {
Employee employee = getEntity();
employee.setStatus(status.getValue());
employee.setAge(age.getValue());
employeeService.createOrUpdate(employee);
Notification.show(NOTIFICATION_SAVE_SUCCESS);
getUI().ifPresent(ui -> ui.navigate(EmployeesListView.class));
} else {
Notification.show(NOTIFICATION_VALIDATE_ERROR, 3000, Notification.Position.MIDDLE);
}
}
private void enableEditMode() {
setFieldsEditable();
saveButton.setVisible(true);
editButton.setVisible(false);
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
final String s = params.get("employeeId").orElse(null);
if ("new".equals(action)) {
setEntityWithEnabledSave(new Employee());
saveButton.setVisible(true);
editButton.setVisible(false);
setFieldsEditable();
} else {
UUID employeeId = UUID.fromString(s);
var employee = employeeService.getEmployee(employeeId);
setEntityWithEnabledSave(employee);
if ("edit".equals(action) && !s.isEmpty()) {
saveButton.setVisible(true);
editButton.setVisible(false);
status.setValue(employee.getStatus());
setFieldsEditable();
} else if ("view".equals(action) && !s.isEmpty()) {
setFieldsReadOnly();
saveButton.setVisible(false);
editButton.setVisible(true);
setFieldsReadOnly();
displayProfileImage(employee);
}
}
}
private void displayProfileImage(final Employee employee) {
if (employee.getProfileImage() != null && !employee.getProfileImage().isEmpty()) {
profileImagePreview.setSrc("data:image/jpeg;base64," + employee.getProfileImage());
profileImagePreview.setVisible(true);
profileImagePreview.setMaxWidth("150px");
profileImagePreview.setMaxHeight("150px");
upload.setVisible(false);
} else {
profileImagePreview.setVisible(false);
upload.setVisible(true);
}
}
private void setFieldsReadOnly() {
username.setReadOnly(false);
firstName.setReadOnly(true);
lastName.setReadOnly(true);
status.setReadOnly(true);
birthday.setReadOnly(true);
birthCity.setReadOnly(true);
residenceAddress.setReadOnly(true);
localAddress.setReadOnly(true);
maritalStatus.setReadOnly(true);
numberOfChildren.setReadOnly(true);
phoneNumber.setReadOnly(true);
personalEmail.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);
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);
language.setReadOnly(true);
languageLevel.setReadOnly(true);
cod.setReadOnly(true);
leadManager.setReadOnly(true);
project.setReadOnly(true);
dateOfEntry.setReadOnly(true);
dateOfExit.setReadOnly(true);
contractType.setReadOnly(true);
seniority.setReadOnly(true);
salary.setReadOnly(true);
bankName.setReadOnly(true);
accountNumber.setReadOnly(true);
gpss.setReadOnly(true);
sss.setReadOnly(true);
beneficiaries.setReadOnly(true);
}
private void setFieldsEditable() {
username.setReadOnly(false);
firstName.setReadOnly(false);
lastName.setReadOnly(false);
status.setReadOnly(false);
birthday.setReadOnly(false);
birthCity.setReadOnly(false);
residenceAddress.setReadOnly(false);
localAddress.setReadOnly(false);
maritalStatus.setReadOnly(false);
numberOfChildren.setReadOnly(false);
phoneNumber.setReadOnly(false);
personalEmail.setReadOnly(false);
position.setReadOnly(false);
team.setReadOnly(false);
emergencyCName.setReadOnly(false);
emergencyCAddress.setReadOnly(false);
emergencyCPhone.setReadOnly(false);
emergencyCEmail.setReadOnly(false);
upload.setVisible(false);
age.setReadOnly(false);
gender.setReadOnly(false);
status.setReadOnly(false);
ci.setReadOnly(false);
issuedIn.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);
language.setReadOnly(false);
languageLevel.setReadOnly(false);
cod.setReadOnly(false);
leadManager.setReadOnly(false);
project.setReadOnly(false);
dateOfEntry.setReadOnly(false);
dateOfExit.setReadOnly(false);
contractType.setReadOnly(false);
seniority.setReadOnly(false);
salary.setReadOnly(false);
bankName.setReadOnly(false);
accountNumber.setReadOnly(false);
gpss.setReadOnly(false);
sss.setReadOnly(false);
beneficiaries.setReadOnly(false);
}
@Override
protected List<Component> getFormComponents() {
return List.of(
username,
infoPer,
infoGenr,
upload, profileImagePreview,
firstName, lastName,
gender, status,
birthday, age,
birthCity, residenceAddress, localAddress,
maritalStatus, ci, issuedIn, numberOfChildren,
phoneNumber, personalEmail,
cod, position, team, leadManager, project,
contEmerg, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail,
infProf,
titulos, pTitle1, pTitle2, pTitle3, pStudy1, pStudy2, pStudy3,
certif, certification1, certification2, certification3, certification4,
logros, recognition, achievements,
idioma, language, languageLevel,
infoAdm,
infoCont, dateOfEntry, dateOfExit, contractType, seniority, salary,
datBanc, bankName, accountNumber,
datGest, gpss, sss, beneficiaries,
saveButton, editButton, reportButton, dialog
);
}
}

View File

@ -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);
}
}

View File

@ -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,24 @@ 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 -> {
log.info(">>> prev");
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 +253,10 @@ 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());
log.info(">>> start");
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 +318,8 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
}
private void goToNext() {
Submission found = this.examService.getNextSubmission(exam.getId(),
log.info(">>> next");
Submission found = this.assessmentService.getNextSubmission(assessment.getId(),
this.currSubmission.getId());
if (found == null) {
@ -340,44 +330,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 +410,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 +437,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();

View File

@ -0,0 +1,197 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.html.H2;
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 java.time.LocalDate;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Hours Worked")
@Route(value = "/hours-worked/me", layout = MainLayout.class)
public class HoursWorkedView extends VerticalLayout {
public HoursWorkedView() {
H2 title = new H2("Registro de Horas Trabajadas");
DatePicker datePicker = new DatePicker("Selecciona una fecha");
datePicker.setValue(LocalDate.now());
ComboBox<String> equipoDropdown = new ComboBox<>("Equipo");
equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3"); // Ejemplo de datos
TextField empleadoSearch = new TextField("Empleado (Search)");
HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, empleadoSearch);
Grid<Actividad> grid = new Grid<>(Actividad.class, false);
grid.addColumn(Actividad::getNombre).setHeader("Actividad");
grid.addColumn(Actividad::getLunes).setHeader("Lunes");
grid.addColumn(Actividad::getMartes).setHeader("Martes");
grid.addColumn(Actividad::getMiercoles).setHeader("Miércoles");
grid.addColumn(Actividad::getJueves).setHeader("Jueves");
grid.addColumn(Actividad::getViernes).setHeader("Viernes");
grid.addColumn(Actividad::getSabado).setHeader("Sábado");
grid.addColumn(Actividad::getDomingo).setHeader("Domingo");
grid.setItems(
new Actividad.Builder()
.nombre("Actividad 1")
.lunes(3)
.martes(3)
.miercoles(3)
.jueves(3)
.viernes(3)
.sabado(1)
.domingo(2)
.build(),
new Actividad.Builder()
.nombre("Actividad 2")
.lunes(2)
.martes(2)
.miercoles(2)
.jueves(2)
.viernes(2)
.sabado(0)
.domingo(1)
.build(),
new Actividad.Builder()
.nombre("Meeting 1")
.lunes(0)
.martes(0.5)
.miercoles(0.5)
.jueves(0)
.viernes(0)
.sabado(0.5)
.domingo(0)
.build()
);
Button actualizarButton = new Button("Actualizar");
Button guardarButton = new Button("Guardar");
Button cerrarButton = new Button("Cerrar");
HorizontalLayout buttonsLayout = new HorizontalLayout(actualizarButton, guardarButton, cerrarButton);
add(title, datePicker, filtersLayout, grid, buttonsLayout);
}
public static final class Actividad {
private final String nombre;
private final double lunes;
private final double martes;
private final double miercoles;
private final double jueves;
private final double viernes;
private final double sabado;
private final double domingo;
private Actividad(final Builder builder) {
this.nombre = builder.nombre;
this.lunes = builder.lunes;
this.martes = builder.martes;
this.miercoles = builder.miercoles;
this.jueves = builder.jueves;
this.viernes = builder.viernes;
this.sabado = builder.sabado;
this.domingo = builder.domingo;
}
public static class Builder {
private String nombre;
private double lunes;
private double martes;
private double miercoles;
private double jueves;
private double viernes;
private double sabado;
private double domingo;
public Builder nombre(final String nombre) {
this.nombre = nombre;
return this;
}
public Builder lunes(final double lunes) {
this.lunes = lunes;
return this;
}
public Builder martes(final double martes) {
this.martes = martes;
return this;
}
public Builder miercoles(final double miercoles) {
this.miercoles = miercoles;
return this;
}
public Builder jueves(final double jueves) {
this.jueves = jueves;
return this;
}
public Builder viernes(final double viernes) {
this.viernes = viernes;
return this;
}
public Builder sabado(final double sabado) {
this.sabado = sabado;
return this;
}
public Builder domingo(final double domingo) {
this.domingo = domingo;
return this;
}
public Actividad build() {
return new Actividad(this);
}
}
public String getNombre() {
return nombre;
}
public double getLunes() {
return lunes;
}
public double getMartes() {
return martes;
}
public double getMiercoles() {
return miercoles;
}
public double getJueves() {
return jueves;
}
public double getViernes() {
return viernes;
}
public double getSabado() {
return sabado;
}
public double getDomingo() {
return domingo;
}
}
}

View File

@ -1,54 +0,0 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
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
public class InitAccountView extends VerticalLayout implements BeforeEnterObserver {
public InitAccountView() {
setSizeFull();
setAlignItems(Alignment.CENTER);
setJustifyContentMode(JustifyContentMode.CENTER);
final VerticalLayout vl = new VerticalLayout();
vl.setJustifyContentMode(JustifyContentMode.CENTER);
vl.setWidth("400px");
final PasswordField password = new PasswordField("Password");
final PasswordField confirmPassword = new PasswordField("Confirm Password");
final FormLayout formLayout = new FormLayout(password, confirmPassword);
formLayout.setColspan(password, 3);
formLayout.setColspan(confirmPassword, 3);
final Button primaryButton = new Button("Submit");
primaryButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
final Button secondaryButton = new Button("Cancel");
HorizontalLayout hl = new HorizontalLayout(secondaryButton, primaryButton);
vl.add(new H3("Set Account Password"));
vl.add(formLayout);
vl.add(hl);
add(vl);
}
@Override
public void beforeEnter(final BeforeEnterEvent beforeEnterEvent) {
}
}

View File

@ -1,8 +1,6 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.login.LoginForm;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.BeforeEnterEvent;
@ -10,9 +8,6 @@ import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import com.vaadin.flow.theme.lumo.LumoUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@Route("login")
@PageTitle("PFS Intra")
@ -21,22 +16,16 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver {
private final LoginForm login = new LoginForm();
public LoginView(@Autowired @Value("${git.commit.id.abbrev}") final String commitId) {
public LoginView() {
addClassName("login-view");
setSizeFull();
setAlignItems(Alignment.CENTER);
setJustifyContentMode(JustifyContentMode.CENTER);
login.setAction("login");
login.setForgotPasswordButtonVisible(false);
add(new H1("PFS Intra"));
add(login);
add(new Anchor("/password-recovery", "Reset password?"));
final Span version = new Span(String.format("v.%s", commitId));
version.addClassName(LumoUtility.FontSize.XSMALL);
add(version);
}
@Override

View File

@ -1,41 +1,22 @@
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;
import com.vaadin.flow.component.applayout.DrawerToggle;
import com.vaadin.flow.component.avatar.Avatar;
import com.vaadin.flow.component.contextmenu.HasMenuItems;
import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.contextmenu.SubMenu;
import com.vaadin.flow.component.html.*;
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 com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Footer;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Header;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.component.shared.Tooltip;
import com.vaadin.flow.component.sidenav.SideNav;
import com.vaadin.flow.component.sidenav.SideNavItem;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.spring.security.AuthenticationContext;
import com.vaadin.flow.theme.lumo.LumoUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.vaadin.lineawesome.LineAwesomeIcon;
@ -49,158 +30,108 @@ public class MainLayout extends AppLayout {
private H1 viewTitle;
public MainLayout(final AuthenticationContext authContext,
@Autowired @Value("${git.commit.id.abbrev}") final String commitId) {
public MainLayout(final AuthenticationContext authContext) {
this.authContext = authContext;
setPrimarySection(Section.DRAWER);
addDrawerContent(commitId);
addDrawerContent();
addHeaderContent();
}
private void addHeaderContent() {
final DrawerToggle toggle = new DrawerToggle();
DrawerToggle toggle = new DrawerToggle();
toggle.setAriaLabel("Menu toggle");
viewTitle = new H1();
viewTitle.addClassNames(LumoUtility.FontSize.LARGE, LumoUtility.Margin.NONE);
final HorizontalLayout header = authContext.getAuthenticatedUser(UserDetails.class)
.map(user -> {
String employeeId = "N/A";
HorizontalLayout
header =
authContext.getAuthenticatedUser(UserDetails.class)
.map(user -> {
final Button logout = new Button("Logout", click -> this.authContext.logout());
final Span loggedUser = new Span("Welcome " + user.getUsername());
String employeeId = "N/A";
if (user instanceof Employee) {
final UUID uuid = ((Employee) user).getId();
if (user instanceof Employee) {
final UUID uuid = ((Employee) user).getId();
if (uuid != null) {
employeeId = uuid.toString();
}
}
if (uuid != null) {
employeeId = uuid.toString();
}
}
final Avatar loggedUser = new Avatar(user.getUsername());
loggedUser.getStyle().set("display", "block");
loggedUser.getElement().setAttribute("tabindex", "-1");
final Tooltip tooltip = Tooltip.forComponent(loggedUser)
.withText("Employee id: " + employeeId)
.withPosition(Tooltip.TooltipPosition.TOP_START);
final MenuBar menuBar = new MenuBar();
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
final MenuItem actions = createIconItem(menuBar, loggedUser, null, employeeId);
final SubMenu actionsSubMenu = actions.getSubMenu();
final MenuItem signOutMenuItem = createIconItem(actionsSubMenu,
createIcon(VaadinIcon.EXIT, true), "Sign-out", null);
signOutMenuItem.addClickListener(c -> this.authContext.logout());
final HorizontalLayout hl = new HorizontalLayout(loggedUser, logout);
hl.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
final HorizontalLayout hl = new HorizontalLayout(menuBar);
hl.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
return hl;
}).orElseGet(HorizontalLayout::new);
return hl;
}).orElseGet(HorizontalLayout::new);
header.setAlignItems(FlexComponent.Alignment.STRETCH);
header.setWidthFull();
addToNavbar(true, toggle, viewTitle, header);
}
private MenuItem createIconItem(final HasMenuItems menu, final Component component,
final String label, final String ariaLabel) {
final MenuItem item = menu.addItem(component, e -> {
});
if (ariaLabel != null) {
item.setAriaLabel(ariaLabel);
}
if (label != null) {
item.add(new Text(label));
}
return item;
}
private Icon createIcon(final VaadinIcon iconName, final boolean isChild) {
final Icon icon = new Icon(iconName);
if (isChild) {
icon.getStyle().set("width", "var(--lumo-icon-size-s)");
icon.getStyle().set("height", "var(--lumo-icon-size-s)");
icon.getStyle().set("marginRight", "var(--lumo-space-s)");
}
return icon;
}
private void addDrawerContent(final String commitId) {
final Span appName = new Span("pfs-intra");
private void addDrawerContent() {
Span appName = new Span("pfs-intra");
appName.addClassNames(LumoUtility.FontWeight.SEMIBOLD, LumoUtility.FontSize.LARGE);
final Header header = new Header(appName);
final Scroller scroller = new Scroller(createNavigation());
addToDrawer(header, scroller, createFooter(commitId));
Header header = new Header(appName);
Scroller scroller = new Scroller(createNavigation());
addToDrawer(header, scroller, createFooter());
}
private SideNav createNavigation() {
final SideNav nav = new SideNav();
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", MainView.class,
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", MainView.class,
LineAwesomeIcon.SUPERSCRIPT_SOLID.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.SUN.create()));
SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class,
LineAwesomeIcon.HOURGLASS_START_SOLID.create());
timesheet.addItem(new SideNavItem("Hours Worked", HoursWorkedView.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;
}
private Footer createFooter(final String commitId) {
return new Footer(new Text(String.format("v.%s", commitId)));
private Footer createFooter() {
Footer layout = new Footer();
return layout;
}
@Override

View File

@ -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;
@ -11,8 +10,7 @@ import jakarta.annotation.security.PermitAll;
@Route(value = "", layout = MainLayout.class)
@PermitAll
public class MainView extends Main {
public MainView() {
add(new VerticalLayout(new Text("Welcome to PFS!")));
add(new Text("welcome"));
}
}

View File

@ -1,67 +0,0 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.service.AccountService;
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.formlayout.FormLayout;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
@Route("password-recovery")
@PageTitle("PFS Intra")
@AnonymousAllowed
public class PasswordRecoveryView extends VerticalLayout implements BeforeEnterObserver {
public PasswordRecoveryView(final AccountService accountService) {
setSizeFull();
setAlignItems(Alignment.CENTER);
setJustifyContentMode(JustifyContentMode.CENTER);
final VerticalLayout vl = new VerticalLayout();
vl.setJustifyContentMode(JustifyContentMode.CENTER);
vl.setWidth("400px");
final EmailField personalEmail = new EmailField("Personal Email");
personalEmail.setRequired(true);
final EmailField confirmPersonalEmail = new EmailField("Confirm Personal Email");
confirmPersonalEmail.setRequired(true);
final FormLayout formLayout = new FormLayout(personalEmail, confirmPersonalEmail);
formLayout.setColspan(personalEmail, 3);
formLayout.setColspan(confirmPersonalEmail, 3);
final Button primaryButton = new Button("Submit");
primaryButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
primaryButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
if (personalEmail.getValue().equals(confirmPersonalEmail.getValue())) {
accountService.sendResetPasswordEmail(personalEmail.getValue());
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
});
final Button secondaryButton = new Button("Cancel");
final HorizontalLayout hl = new HorizontalLayout(secondaryButton, primaryButton);
secondaryButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
getUI().ifPresent(ui -> ui.navigate(MainView.class)));
vl.add(new H3("PFS - Password Recovery"));
vl.add(formLayout);
vl.add(hl);
add(vl);
}
@Override
public void beforeEnter(final BeforeEnterEvent beforeEnterEvent) {
}
}

View File

@ -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 {
}

View File

@ -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);
});
}

View File

@ -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);
}
}

View File

@ -0,0 +1,156 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.TimeOffRequest;
import com.primefactorsolutions.model.TimeOffRequestType;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TimeOffRequestService;
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.Div;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Span;
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 java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Request")
@Route(value = "/requests", layout = MainLayout.class)
public class RequestView extends Div implements HasUrlParameter<String> {
private final TimeOffRequestService requestService;
private final EmployeeService employeeService;
private final Grid<TimeOffRequest> requestGrid = new Grid<>(TimeOffRequest.class);
private List<TimeOffRequest> requests = Collections.emptyList();
private ComboBox<TimeOffRequestType> categoryFilter;
private ComboBox<TimeOffRequest.Status> stateFilter;
private UUID employeeId;
public RequestView(final TimeOffRequestService requestService, final EmployeeService employeeService) {
this.requestService = requestService;
this.employeeService = employeeService;
initializeView();
}
private void initializeView() {
setupFilters();
setupGrid();
add(requestGrid, createSummaryLayout(), createActionButtons());
refreshRequestGrid(null, null);
}
private void setupFilters() {
categoryFilter = createCategoryFilter();
stateFilter = createStateFilter();
add(categoryFilter, stateFilter);
}
private ComboBox<TimeOffRequestType> createCategoryFilter() {
categoryFilter = new ComboBox<>("Category");
categoryFilter.setItems(TimeOffRequestType.values());
categoryFilter.setValue(TimeOffRequestType.values()[0]);
categoryFilter.addValueChangeListener(event -> refreshRequestGrid(event.getValue(), stateFilter.getValue()));
return categoryFilter;
}
private ComboBox<TimeOffRequest.Status> createStateFilter() {
stateFilter = new ComboBox<>("State");
stateFilter.setItems(TimeOffRequest.Status.values());
stateFilter.setValue(TimeOffRequest.Status.values()[0]);
stateFilter.addValueChangeListener(event -> refreshRequestGrid(categoryFilter.getValue(), event.getValue()));
return stateFilter;
}
private void setupGrid() {
requestGrid.setColumns(
"category",
"state",
"availableDays",
"expiration",
"startDate",
"endDate",
"daysToBeTake",
"daysBalance"
);
requestGrid.setAllRowsVisible(true);
}
private VerticalLayout createSummaryLayout() {
int totalVacations = 15;
int totalTimeOff = 2;
int totalAvailableDays = totalVacations + totalTimeOff;
return new VerticalLayout(
new Span("TOTAL HOLIDAYS: " + totalVacations),
new Span("TOTAL TIME OFF: " + totalTimeOff),
new Span("TOTAL AVAILABLE DAYS: " + totalAvailableDays)
);
}
private HorizontalLayout createActionButtons() {
Button viewButton = new Button("View");
Button editButton = new Button("Edit");
Button saveButton = new Button("Save");
Button closeButton = new Button("Close", event -> navigateToRequestsListView());
return new HorizontalLayout(viewButton, editButton, saveButton, closeButton);
}
private void navigateToRequestsListView() {
getUI().ifPresent(ui -> ui.navigate(RequestsListView.class));
}
private void refreshRequestGrid(final TimeOffRequestType category, final TimeOffRequest.Status state) {
List<TimeOffRequest> filteredRequests = allFiltersAreNull(category, state)
? requestService.findRequestsByEmployeeId(employeeId)
: fetchFilteredTimeOffRequests(category, state);
requestGrid.setItems(filteredRequests);
}
private boolean allFiltersAreNull(final TimeOffRequestType category, final TimeOffRequest.Status state) {
return category == null && state == null;
}
private List<TimeOffRequest> fetchFilteredTimeOffRequests(final TimeOffRequestType category,
final TimeOffRequest.Status state) {
requests = requestService.findRequestsByEmployeeId(employeeId);
if (category != null && !"ALL".equals(category.name())) {
requests = requests.stream()
.filter(req -> req.getCategory().equals(category))
.collect(Collectors.toList());
}
if (state != null && !"ALL".equals(state.name())) {
requests = requests.stream()
.filter(req -> req.getState().equals(state))
.collect(Collectors.toList());
}
return requests;
}
@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);
}
private void setViewTitle(final String employeeName, final String employeeTeam) {
addComponentAsFirst(new H3("Name: " + employeeName));
addComponentAtIndex(1, new H3("Team: " + employeeTeam));
}
}

View File

@ -0,0 +1,273 @@
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.html.Span;
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.Collections;
import java.util.List;
import java.util.UUID;
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 PagingGrid<TimeOffRequest> requestGrid = new PagingGrid<>(TimeOffRequest.class);
private List<TimeOffRequest> requests = Collections.emptyList();
private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter;
private ComboBox<TimeOffRequestType> categoryFilter;
private ComboBox<TimeOffRequest.Status> stateFilter;
private UUID selectedEmployeeId;
public RequestsListView(final TimeOffRequestService requestService,
final EmployeeService employeeService,
final TeamService teamService) {
this.requestService = requestService;
this.employeeService = employeeService;
this.teamService = teamService;
this.requests = requestService.findAllTimeOffRequests();
initializeView();
refreshRequestGrid(null, null, null, null);
}
private void initializeView() {
setupFilters();
setupRequestGrid();
add(requestGrid);
add(createActionButtons());
}
private void setupFilters() {
add(createEmployeeFilter());
add(createTeamFilter());
add(createCategoryFilter());
add(createStateFilter());
}
private void setupRequestGrid() {
requestGrid.setColumns(
"category",
"state",
"availableDays",
"expiration",
"startDate",
"endDate",
"daysToBeTake",
"daysBalance"
);
requestGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Employee");
requestGrid.addComponentColumn(this::createTeamSpan).setHeader("Team");
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
requestGrid.setPageSize(5);
requestGrid.asSingleSelect().addValueChangeListener(event -> {
TimeOffRequest selectedRequest = event.getValue();
if (selectedRequest != null) {
selectedEmployeeId = selectedRequest.getEmployee().getId();
}
});
}
private HorizontalLayout createActionButtons() {
Button viewButton = new Button("View", event -> {
if (selectedEmployeeId != null) {
navigateToTimeOffRequestView(selectedEmployeeId);
} else {
Notification.show("Please select a request to view.", 3000, Notification.Position.MIDDLE);
}
});
Button editButton = new Button("Edit");
Button saveButton = new Button("Save");
Button closeButton = new Button("Close", event -> navigateToMainView());
return new HorizontalLayout(viewButton, editButton, saveButton, closeButton);
}
private void refreshRequestGrid(final Employee employee,
final Team team,
final TimeOffRequestType category,
final TimeOffRequest.Status state) {
requestGrid.setPagingDataProvider((page, pageSize) -> {
requests = requestService.findAllTimeOffRequests();
int start = (int) (page * requestGrid.getPageSize());
if (allFiltersAreNull(employee, team, category, state)) {
return fetchTimeOffRequests(start, pageSize);
} else {
return fetchFilteredTimeOffRequests(start, pageSize, employee, team, category, state);
}
});
requestGrid.getDataProvider().refreshAll();
}
private boolean allFiltersAreNull(final Employee employee,
final Team team,
final TimeOffRequestType category,
final TimeOffRequest.Status state) {
return employee == null && team == null && category == null && state == null;
}
private List<TimeOffRequest> fetchTimeOffRequests(final int start, final int pageSize) {
int end = start + pageSize;
if (end > requests.size()) {
end = requests.size();
}
return requests.subList(start, end);
}
private List<TimeOffRequest> fetchFilteredTimeOffRequests(final int start,
final int pageSize,
final Employee employee,
final Team team,
final TimeOffRequestType category,
final TimeOffRequest.Status state) {
if (employee != null && !"ALL".equals(employee.getFirstName())) {
requests = requests.stream()
.filter(request -> request.getEmployee().getId().equals(employee.getId()))
.collect(Collectors.toList());
}
if (team != null && !"ALL".equals(team.getName())) {
requests = requests.stream()
.filter(request -> request.getEmployee().getTeam().getId().equals(team.getId()))
.collect(Collectors.toList());
}
if (category != null && !"ALL".equals(category.name())) {
requests = requests.stream()
.filter(request -> request.getCategory().equals(category))
.collect(Collectors.toList());
}
if (state != null && !"ALL".equals(state.name())) {
requests = requests.stream()
.filter(request -> request.getState().equals(state))
.collect(Collectors.toList());
}
int end = start + pageSize;
if (end > requests.size()) {
end = requests.size();
}
return requests.subList(start, end);
}
private Span createEmployeeSpan(final TimeOffRequest timeOffRequest) {
Employee employee = timeOffRequest.getEmployee();
return new Span(employee.getFirstName() + " " + employee.getLastName());
}
private Span createTeamSpan(final TimeOffRequest timeOffRequest) {
return new Span(timeOffRequest.getEmployee().getTeam().getName());
}
private ComboBox<Employee> createEmployeeFilter() {
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 ->
refreshRequestGrid(
event.getValue(),
teamFilter.getValue(),
categoryFilter.getValue(),
stateFilter.getValue()
)
);
return employeeFilter;
}
private ComboBox<Team> createTeamFilter() {
teamFilter = new ComboBox<>("Team");
List<Team> teams = teamService.findAllTeams();
teams.addFirst(createAllTeamsOption());
teamFilter.setItems(teams);
teamFilter.setItemLabelGenerator(this::getTeamLabel);
teamFilter.setValue(teams.getFirst());
teamFilter.addValueChangeListener(event ->
refreshRequestGrid(
employeeFilter.getValue(),
event.getValue(),
categoryFilter.getValue(),
stateFilter.getValue()
)
);
return teamFilter;
}
private ComboBox<TimeOffRequestType> createCategoryFilter() {
categoryFilter = new ComboBox<>("Category");
categoryFilter.setItems(TimeOffRequestType.values());
categoryFilter.setValue(TimeOffRequestType.values()[0]);
categoryFilter.addValueChangeListener(event ->
refreshRequestGrid(
employeeFilter.getValue(),
teamFilter.getValue(),
event.getValue(),
stateFilter.getValue()
)
);
return categoryFilter;
}
private ComboBox<TimeOffRequest.Status> createStateFilter() {
stateFilter = new ComboBox<>("State");
stateFilter.setItems(TimeOffRequest.Status.values());
stateFilter.setValue(TimeOffRequest.Status.values()[0]);
stateFilter.addValueChangeListener(event ->
refreshRequestGrid(
employeeFilter.getValue(),
teamFilter.getValue(),
categoryFilter.getValue(),
event.getValue()
)
);
return stateFilter;
}
private Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("ALL");
return allEmployeesOption;
}
private Team createAllTeamsOption() {
Team allTeamsOption = new Team();
allTeamsOption.setName("ALL");
return allTeamsOption;
}
private String getEmployeeLabel(final Employee employee) {
return "ALL".equals(employee.getFirstName()) ? "ALL" : employee.getFirstName() + " " + employee.getLastName();
}
private String getTeamLabel(final Team team) {
return "ALL".equals(team.getName()) ? "ALL" : team.getName();
}
private void navigateToMainView() {
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
private void navigateToTimeOffRequestView(final UUID idEmployee) {
getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString()));
}
}

View File

@ -1,71 +0,0 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.service.AccountService;
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.formlayout.FormLayout;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import java.util.List;
@Route("reset-password")
@PageTitle("PFS Intra")
@AnonymousAllowed
public class ResetPasswordView extends VerticalLayout implements BeforeEnterObserver {
private String username;
private String token;
public ResetPasswordView(final AccountService accountService) {
setSizeFull();
setAlignItems(Alignment.CENTER);
setJustifyContentMode(JustifyContentMode.CENTER);
final VerticalLayout vl = new VerticalLayout();
vl.setJustifyContentMode(JustifyContentMode.CENTER);
vl.setWidth("400px");
final PasswordField password = new PasswordField("Password");
final PasswordField confirmPassword = new PasswordField("Confirm Password");
final FormLayout formLayout = new FormLayout(password, confirmPassword);
formLayout.setColspan(password, 3);
formLayout.setColspan(confirmPassword, 3);
final Button primaryButton = new Button("Submit");
primaryButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
primaryButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
accountService.resetPassword(username, password.getValue(), token);
getUI().ifPresent(ui -> ui.navigate(MainView.class));
});
final Button secondaryButton = new Button("Cancel");
secondaryButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
getUI().ifPresent(ui -> ui.navigate(MainView.class)));
HorizontalLayout hl = new HorizontalLayout(secondaryButton, primaryButton);
vl.add(new H3("PFS - Reset Password"));
vl.add(formLayout);
vl.add(hl);
add(vl);
}
@Override
public void beforeEnter(final BeforeEnterEvent beforeEnterEvent) {
final Location location = beforeEnterEvent.getLocation();
final QueryParameters queryParameters = location.getQueryParameters();
this.username = queryParameters.getParameters().getOrDefault("username", List.of()).stream()
.findFirst().orElse(null);
this.token = queryParameters.getParameters().getOrDefault("token", List.of()).stream()
.findFirst().orElse(null);
}
}

View File

@ -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();

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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"));
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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
);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -1,714 +0,0 @@
package com.primefactorsolutions.views.employee;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.Team;
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.vaadin.componentfactory.pdfviewer.PdfViewer;
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.dialog.Dialog;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.component.icon.VaadinIcon;
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;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
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 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;
import java.util.List;
import java.util.UUID;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Employee")
@Route(value = "/employees/:employeeId?/:action?", layout = MainLayout.class)
@Slf4j
public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlParameter<String> {
private final EmployeeService employeeService;
private final ReportService reportService;
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 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 residenceAddress = createTextField("Dirección de Residencia", 50, false);
private final TextField localAddress = createTextField("Departamento y Provincia de Residencia", 30, false);
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
private final TextField numberOfChildren = createTextField("Numero de Hijos", 1, false);
private final TextField ci = createTextField("CI", 10, false);
private final TextField issuedIn = createTextField("Lugar de Expedicion", 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 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 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);
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 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 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 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 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 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:");
private final H3 contEmerg = new H3("Contacto de Emergencia");
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");
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,
final ReportService reportService,
final TeamService teamService) {
super(authenticationContext, Employee.class);
this.employeeService = employeeService;
this.reportService = reportService;
this.teamService = teamService;
excelReportButton.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();
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());
dateOfEntry.addValueChangeListener(event -> calculateSeniority());
dateOfExit.addValueChangeListener(event -> {
if (event.getValue() != null) {
status.setValue(Employee.Status.INACTIVE);
} else {
status.setValue(Employee.Status.ACTIVE);
}
});
reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
var employee = getEntity();
byte[] pdfContent = reportService.writeAsPdf("ficha", employee);
var resource = new StreamResource("ficha.pdf", () -> new ByteArrayInputStream(pdfContent));
pdfViewer.setSrc(resource);
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();
}
private void validateNameField(final TextField textField, final String value) {
if (!value.matches("^[a-zA-ZáéíóúÁÉÍÓÚñÑ\\s]*$")) {
textField.setInvalid(true);
textField.setErrorMessage("Este campo solo debe contener letras.");
} else {
textField.setInvalid(false);
}
}
private void calculateAge() {
if (birthday.getValue() != null) {
int currentYear = java.time.LocalDate.now().getYear();
int birthYear = birthday.getValue().getYear();
int ages = currentYear - birthYear;
age.setValue(String.valueOf(ages));
birthday.setInvalid(ages < 18);
}
}
private void calculateSeniority() {
LocalDate entryDate = dateOfEntry.getValue();
LocalDate exitDate = dateOfExit.getValue() != null ? dateOfExit.getValue() : LocalDate.now();
if (entryDate != null) {
long yearsOfService = ChronoUnit.YEARS.between(entryDate, exitDate);
String seniorityValue = yearsOfService + " años ";
seniority.setValue(seniorityValue);
} else {
seniority.setValue("No disponible");
}
}
private void 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);
upload.addSucceededListener(event -> {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
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);
} catch (Exception e) {
Notification.show("Error en el servidor al procesar la imagen.");
log.error("Error uploading image", e);
}
});
}
private void validatePhoneNumber(final TextField textField, final String value) {
if (!value.matches("\\d*")) {
textField.setErrorMessage(PHONE_NUMBER_ERROR_MESSAGE);
}
}
private void initDialog() {
pdfViewer.setSizeFull();
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");
dialogLayout.getStyle().set("display", "flex");
dialogLayout.getStyle().set("flex-direction", "column");
dialogLayout.setPadding(false);
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);
}
private ComboBox<Employee.MaritalStatus> createMaritalStatusComboBox() {
ComboBox<Employee.MaritalStatus> comboBox = new ComboBox<>("Estado Civil");
comboBox.setItems(Employee.MaritalStatus.values());
comboBox.setItemLabelGenerator(Employee.MaritalStatus::name);
return comboBox;
}
private ComboBox<Employee.Status> createStatusComboBox() {
ComboBox<Employee.Status> comboBox = new ComboBox<>("Estado");
comboBox.setItems(Employee.Status.values());
comboBox.setItemLabelGenerator(Employee.Status::name);
comboBox.setRequiredIndicatorVisible(true);
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%");
return contentLayout;
}
private TextField createTextField(final String label, final int maxLength, final boolean required) {
TextField textField = new TextField(label);
textField.setWidthFull();
textField.setMaxLength(maxLength);
textField.setRequired(required);
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);
return emailField;
}
private void createTeamComboBox() {
List<Team> teams = teamService.findAllTeams();
team.setItems(teams);
team.setItemLabelGenerator(Team::getName);
team.setWidthFull();
}
private ComboBox<Employee.Gender> createGenderComboBox() {
ComboBox<Employee.Gender> comboBox = new ComboBox<>("Genero");
comboBox.setItems(Employee.Gender.values());
comboBox.setItemLabelGenerator(Employee.Gender::name);
comboBox.setRequiredIndicatorVisible(true);
return comboBox;
}
private boolean validateForm() {
return !firstName.isEmpty() && !lastName.isEmpty() && status.getValue() != null;
}
private void setVacationDuration(
final Employee employee,
final TimeOffRequest request,
final LocalDate referenceDate) {
double yearsOfService = ChronoUnit.YEARS.between(employee.getDateOfEntry(), referenceDate);
request.setAvailableDays(calculateAvailableDays(yearsOfService));
}
private double calculateAvailableDays(final double yearsOfService) {
if (yearsOfService > 10) {
return 30.0;
} else if (yearsOfService > 5) {
return 20.0;
} else if (yearsOfService > 1) {
return 15.0;
} else {
return 0.0;
}
}
private void saveEmployee(final Employee employee) {
if (validateForm()) {
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));
} else {
Notification.show(NOTIFICATION_VALIDATE_ERROR, 3000, Notification.Position.MIDDLE);
}
}
private void enableEditMode() {
setFieldsEditable();
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
final String s = params.get("employeeId").orElse(null);
if ("new".equals(action)) {
setEntityWithEnabledSave(new Employee());
setFieldsEditable();
upload.setVisible(true);
profileImagePreview.setVisible(true);
salaryTotal.setValue(BigDecimal.valueOf(0.0));
} else {
final Employee 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);
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")));
setFieldsReadOnly();
displayProfileImage(employee);
salaryTotal.setValue(employee.getSalaryTotal());
}
}
}
private void displayProfileImage(final Employee employee) {
if (employee.getProfileImage() != null && !employee.getProfileImage().isEmpty()) {
profileImagePreview.setSrc("data:image/jpeg;base64," + employee.getProfileImage());
profileImagePreview.setVisible(true);
profileImagePreview.setMaxWidth("250px");
profileImagePreview.setMaxHeight("250px");
upload.setVisible(true);
} else {
profileImagePreview.setVisible(true);
upload.setVisible(true);
}
}
private void setFieldsReadOnly() {
username.setReadOnly(true);
firstName.setReadOnly(true);
lastName.setReadOnly(true);
status.setReadOnly(true);
birthday.setReadOnly(true);
birthCity.setReadOnly(true);
residenceAddress.setReadOnly(true);
localAddress.setReadOnly(true);
maritalStatus.setReadOnly(true);
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);
profileImagePreview.setVisible(true);
age.setReadOnly(true);
gender.setReadOnly(true);
status.setReadOnly(true);
ci.setReadOnly(true);
issuedIn.setReadOnly(true);
educationTitles.setReadOnly(true);
certifications.setReadOnly(true);
recognition.setReadOnly(true);
achievements.setReadOnly(true);
languages.setReadOnly(true);
cod.setReadOnly(true);
leadManager.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);
bankName.setReadOnly(true);
accountNumber.setReadOnly(true);
gpss.setReadOnly(true);
sss.setReadOnly(true);
beneficiarie1.setReadOnly(true);
beneficiarie2.setReadOnly(true);
}
private void setFieldsEditable() {
username.setReadOnly(false);
firstName.setReadOnly(false);
lastName.setReadOnly(false);
status.setReadOnly(false);
birthday.setReadOnly(false);
birthCity.setReadOnly(false);
residenceAddress.setReadOnly(false);
localAddress.setReadOnly(false);
maritalStatus.setReadOnly(false);
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);
profileImagePreview.setVisible(true);
age.setReadOnly(false);
gender.setReadOnly(false);
status.setReadOnly(false);
ci.setReadOnly(false);
issuedIn.setReadOnly(false);
educationTitles.setReadOnly(false);
certifications.setReadOnly(false);
recognition.setReadOnly(false);
achievements.setReadOnly(false);
languages.setReadOnly(false);
cod.setReadOnly(false);
leadManager.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);
bankName.setReadOnly(false);
accountNumber.setReadOnly(false);
gpss.setReadOnly(false);
sss.setReadOnly(false);
beneficiarie1.setReadOnly(false);
beneficiarie2.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
);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

Some files were not shown because too many files have changed in this diff Show More