Compare commits
No commits in common. "main" and "Listado-General-Vacaciones" have entirely different histories.
main
...
Listado-Ge
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
2089
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
42
package.json
42
package.json
@ -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
64
pom.xml
@ -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.
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -15,33 +15,30 @@ import java.util.Optional;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Exam extends BaseEntity {
|
||||
public class Assessment extends BaseEntity {
|
||||
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
|
||||
@JoinTable(name = "EXAM_QUESTIONS", joinColumns = @JoinColumn(name = "exam_id"),
|
||||
@JoinTable(name = "ASSESSMENT_QUESTIONS", joinColumns = @JoinColumn(name = "assessment_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "question_id"))
|
||||
private List<Question> questions = new ArrayList<>();
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "exam", cascade = {CascadeType.ALL})
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "assessment", cascade = {CascadeType.ALL})
|
||||
private List<Submission> submissions = new ArrayList<>();
|
||||
|
||||
@ManyToOne
|
||||
private Candidate candidate;
|
||||
|
||||
@ManyToOne
|
||||
private Evaluation evaluation;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
|
||||
@JoinColumn(name = "EXAM_ID")
|
||||
private List<ExamEvent> examEvents = new ArrayList<>();
|
||||
@JoinColumn(name = "ASSESSMENT_ID")
|
||||
private List<AssessmentEvent> assessmentEvents = new ArrayList<>();
|
||||
|
||||
public Submission getCurrentSubmission() {
|
||||
return submissions.getLast();
|
||||
}
|
||||
|
||||
public Long getRemainingTimeSeconds() {
|
||||
final Optional<Instant> started = examEvents.stream()
|
||||
.filter(e -> e.getStatus() == ExamStatus.STARTED)
|
||||
.map(ExamEvent::getTimestamp)
|
||||
final Optional<Instant> started = assessmentEvents.stream()
|
||||
.filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst();
|
||||
|
||||
final Integer totalTimeMinutes = questions.stream()
|
||||
@ -55,24 +52,24 @@ public class Exam extends BaseEntity {
|
||||
}
|
||||
|
||||
public Instant getStartingTime() {
|
||||
final Optional<Instant> started = examEvents.stream()
|
||||
.filter(e -> e.getStatus() == ExamStatus.STARTED)
|
||||
.map(ExamEvent::getTimestamp)
|
||||
final Optional<Instant> started = assessmentEvents.stream()
|
||||
.filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst();
|
||||
|
||||
return started.orElse(null);
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return examEvents.stream().filter(e -> e.getStatus() == ExamStatus.COMPLETED)
|
||||
.map(ExamEvent::getTimestamp)
|
||||
return assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.COMPLETED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
public boolean isStarted() {
|
||||
return examEvents.stream().filter(e -> e.getStatus() == ExamStatus.STARTED)
|
||||
.map(ExamEvent::getTimestamp)
|
||||
return assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
@ -13,7 +13,7 @@ import java.time.Instant;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ExamEvent extends BaseEntity {
|
||||
public class AssessmentEvent extends BaseEntity {
|
||||
private Instant timestamp;
|
||||
private ExamStatus status;
|
||||
private AssessmentStatus status;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum ExamStatus {
|
||||
public enum AssessmentStatus {
|
||||
CREATED,
|
||||
STARTED,
|
||||
COMPLETED
|
@ -1,11 +1,7 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
@MappedSuperclass
|
||||
@ -13,18 +9,22 @@ public abstract class BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Getter
|
||||
@Setter
|
||||
private UUID id;
|
||||
|
||||
@Version
|
||||
@Getter
|
||||
private int version;
|
||||
@Getter
|
||||
@ColumnDefault("NOW()")
|
||||
private Instant created;
|
||||
@ColumnDefault("NOW()")
|
||||
@Getter
|
||||
private Instant updated;
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
@ -44,14 +44,4 @@ public abstract class BaseEntity {
|
||||
}
|
||||
return super.equals(that);
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
public void updateCreated() {
|
||||
this.created = Instant.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void updateUpdated() {
|
||||
this.updated = Instant.now();
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,17 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Candidate extends BaseEntity implements HasLabel {
|
||||
public class Candidate extends BaseEntity {
|
||||
@Column(unique = true)
|
||||
private String email;
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return email;
|
||||
}
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "candidate")
|
||||
private List<Assessment> assessments;
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public record Certification(String title, Integer year) {
|
||||
}
|
@ -1,34 +1,36 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum DocumentType {
|
||||
CARNET_DE_IDENTIDAD,
|
||||
RECIBOS_DE_PAGO,
|
||||
CONTRATO_DE_TRABAJO,
|
||||
CERTIFICADO_DE_TRABAJO,
|
||||
All,
|
||||
ID_CARD,
|
||||
PAY_STUB,
|
||||
PAY_SLIPS,
|
||||
EMPLOYMENT_CONTRACT,
|
||||
WORK_CERTIFICATES,
|
||||
NDA,
|
||||
MEMORÁNDUMS,
|
||||
APROBACIÓN_DE_CONTRATO_MTEPS,
|
||||
CERTIFICADO_DE_ANTECEDENTES,
|
||||
EVALUACIÓN_PRE_EMPLEO,
|
||||
FORMULARIO_DE_INSCRIPCIÓN_AL_SEGURO,
|
||||
FORMULARIO_DE_CANCELACIÓN_DE_SEGURO,
|
||||
TÍTULO_PROFESIONAL_1,
|
||||
CERTIFICACIÓN_PROFESIONAL_1,
|
||||
TÍTULO_PROFESIONAL_2,
|
||||
CERTIFICACIÓN_PROFECIONAL_2,
|
||||
TÍTULO_PROFESIONAL_3,
|
||||
CERTIFICACIÓN_PROFECIONAL_3,
|
||||
NORMATIVA_LABORAL_GENERAL,
|
||||
NORMAS_DE_TRABAJO_REMOTO,
|
||||
NORMAS_DE_SEGURIDAD,
|
||||
INSTRUCTIVOS_DE_RECURSOS_HUMANOS,
|
||||
MANUAL_DE_FUNCIONES_DE_ADMINISTRACIÓN,
|
||||
MANUAL_DE_FUNCIONES_DE_INGENIERÍA,
|
||||
LEY_GENERAL_DEL_TRABAJO,
|
||||
DECRETOS_SUPREMOS,
|
||||
RESOLUCIONES_O_DISPOSICIONES_REGLAMENTARIAS,
|
||||
NORMATIVA_COMPLEMENTARIA,
|
||||
LEY_GRAL_DE_HIGIENE_SALUD_SEGURIDAD_OCUPACIONAL_Y_BIENESTAR,
|
||||
NORMATIVA_REGLAMENTARIA_PARA_DESARROLLO_DE_PASANTÍAS,
|
||||
OTROS
|
||||
MEMORANDUMS,
|
||||
CONTRACT_APPROVAL_MTEPS,
|
||||
BACKGROUND_CHECK_CERTIFICATE,
|
||||
PRE_EMPLOYMENT_EVALUATION,
|
||||
INSURANCE_REGISTRATION_FORM,
|
||||
INSURANCE_CANCELLATION_FORM,
|
||||
PROFESSIONAL_DEGREE_1,
|
||||
PROFESSIONAL_CERTIFICATE_1,
|
||||
PROFESSIONAL_DEGREE_2,
|
||||
PROFESSIONAL_CERTIFICATE_2,
|
||||
PROFESSIONAL_DEGREE_3,
|
||||
PROFESSIONAL_CERTIFICATE_3,
|
||||
GENERAL_LABOR_REGULATIONS,
|
||||
REMOTE_WORK_GUIDELINES,
|
||||
SAFETY_REGULATIONS,
|
||||
HUMAN_RESOURCES_GUIDELINES,
|
||||
ADMINISTRATION_FUNCTIONS_MANUAL,
|
||||
ENGINEERING_FUNCTIONS_MANUAL,
|
||||
GENERAL_LABOR_LAW,
|
||||
SUPREME_DECREE,
|
||||
REGULATORY_RESOLUTION,
|
||||
COMPLEMENTARY_REGULATION,
|
||||
HEALTH_SAFETY_LAW,
|
||||
INTERNSHIP_RULES,
|
||||
OTHER
|
||||
}
|
||||
|
@ -1,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
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum EmployeePosition {
|
||||
JUNIOR_DEV,
|
||||
JUNIOR_QA,
|
||||
SENIOR_DEV,
|
||||
SENIOR_QA
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Evaluation extends BaseEntity {
|
||||
@ManyToOne
|
||||
private Candidate candidate;
|
||||
private Integer points;
|
||||
private EmployeePosition candidatePosition;
|
||||
private Employee interviewer;
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
@ColumnDefault("JSON_ARRAY()")
|
||||
private List<SkillEvaluation> skillEvaluations = Lists.newArrayList();
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "evaluation")
|
||||
private List<Exam> exams;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public interface HasLabel {
|
||||
String getLabel();
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public record Language(String name, Proficiency proficiency) {
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = MinAgeValidator.class)
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MinAge {
|
||||
String message() default "El empleado debe ser mayor de {value} años";
|
||||
Class<?>[] groups() default {};
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
int value();
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
public class MinAgeValidator implements ConstraintValidator<MinAge, LocalDate> {
|
||||
private int minAge;
|
||||
|
||||
@Override
|
||||
public void initialize(final MinAge constraintAnnotation) {
|
||||
this.minAge = constraintAnnotation.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(final LocalDate birthday, final ConstraintValidatorContext context) {
|
||||
if (birthday == null) {
|
||||
return true;
|
||||
}
|
||||
return ChronoUnit.YEARS.between(birthday, LocalDate.now()) >= minAge;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum Proficiency {
|
||||
BASIC,
|
||||
ADVANCED,
|
||||
FLUENT
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public record SkillEvaluation(SkillType skillType, SkillLevel level, String comments) {
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum SkillLevel {
|
||||
NEEDS_IMPROVEMENT,
|
||||
FAIR,
|
||||
VERY_GOOD,
|
||||
EXCELLENT
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum SkillType {
|
||||
TECHNICAL_KNOWLEDGE,
|
||||
ASSERTIVENESS,
|
||||
COMPLETENESS_AND_CONCISENESS,
|
||||
COMMUNICATION
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
@ -7,6 +9,9 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@ -18,12 +23,14 @@ public class Submission extends BaseEntity {
|
||||
private Question question;
|
||||
|
||||
@Lob
|
||||
private String text;
|
||||
private String response;
|
||||
|
||||
private String output;
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
private Map<String, Object> results;
|
||||
|
||||
private SubmissionStatus submissionStatus;
|
||||
|
||||
@ManyToOne
|
||||
private Exam exam;
|
||||
private Assessment assessment;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum TimeOffRequestStatus {
|
||||
TODOS,
|
||||
TOMADO,
|
||||
APROBADO,
|
||||
EN_USO,
|
||||
PENDIENTE,
|
||||
RECHAZADO,
|
||||
VENCIDO,
|
||||
|
||||
SOLICITADO,
|
||||
}
|
@ -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
|
||||
}
|
||||
|
21
src/main/java/com/primefactorsolutions/model/Timesheet.java
Normal file
21
src/main/java/com/primefactorsolutions/model/Timesheet.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Timesheet extends BaseEntity {
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
private List<TimesheetEntry> entries;
|
||||
}
|
@ -1,30 +1,13 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.IsoFields;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TimesheetEntry extends BaseEntity {
|
||||
|
||||
private String task;
|
||||
private String details;
|
||||
private LocalDate date;
|
||||
private double hours;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "employee_id")
|
||||
private Employee employee;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "team_id")
|
||||
private Team team;
|
||||
|
||||
public int getWeekNumber() {
|
||||
return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
}
|
||||
public class TimesheetEntry {
|
||||
private TaskType taskType;
|
||||
private int hours;
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.IsoFields;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public record Week(LocalDate from, LocalDate to) {
|
||||
@Override
|
||||
public String toString() {
|
||||
final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
return String.format("from %s to %s", formatter.format(from), formatter.format(to));
|
||||
}
|
||||
|
||||
public static Week getCurrent() {
|
||||
final int weekNumber = LocalDate.now().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
final LocalDate from = getFirstDayOfWeek(weekNumber);
|
||||
|
||||
return new Week(from, from.plusDays(6));
|
||||
}
|
||||
|
||||
public static List<Week> getLastWeeks(final int numberOfWeeks) {
|
||||
final int weekNumber = LocalDate.now().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
LocalDate from = getFirstDayOfWeek(weekNumber);
|
||||
final ArrayList<Week> result = Lists.newArrayList();
|
||||
|
||||
for (int i = 0; i < numberOfWeeks; i++) {
|
||||
result.add(new Week(from, from.plusDays(6)));
|
||||
from = from.minusDays(7);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static LocalDate getFirstDayOfWeek(final int weekNumber) {
|
||||
return LocalDate
|
||||
.now()
|
||||
.with(WeekFields.of(Locale.US).getFirstDayOfWeek())
|
||||
.with(WeekFields.of(Locale.US).weekOfWeekBasedYear(), weekNumber);
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.Evaluation;
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EvaluationRepository extends JpaRepository<Evaluation, UUID> {
|
||||
public interface AssessmentRepository extends JpaRepository<Assessment, UUID> {
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.repositories.AssessmentRepository;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class AssessmentService {
|
||||
|
||||
private final AssessmentRepository assessmentRepository;
|
||||
private final EntityManager entityManager;
|
||||
private final JavaMailSender emailSender;
|
||||
|
||||
public Assessment createOrUpdate(final Assessment assessment) {
|
||||
final Assessment saved = assessmentRepository.save(assessment);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
public void sendEmail(final Assessment assessment) {
|
||||
try {
|
||||
final String evaluationLink = String.format("https://careers.primefactorsolutions.com/evaluation/%s",
|
||||
assessment.getId());
|
||||
final SimpleMailMessage message = new SimpleMailMessage();
|
||||
message.setFrom("no-reply@primefactorsolutions.com");
|
||||
message.setBcc("no-reply@primefactorsolutions.com");
|
||||
message.setTo(assessment.getCandidate().getEmail());
|
||||
message.setSubject("PFS - Evaluacion Tecnica");
|
||||
message.setText(String.format("Estimado candidato,\n\nGracias por su candidatura. En esta etapa del "
|
||||
+ "proceso de seleccion, usted debe completar "
|
||||
+ "una evaluacion tecnica de programacion en JAVA. La prueba tiene una duracion de 30 minutos y "
|
||||
+ "puede completarla cuando tenga una buena "
|
||||
+ "conexion de internet.\n\n"
|
||||
+ "Haga click aca: " + evaluationLink + "\n\n"
|
||||
+ "Exito!"));
|
||||
|
||||
emailSender.send(message);
|
||||
log.info("Sent email to {}", assessment.getCandidate().getEmail());
|
||||
} catch (Exception e) {
|
||||
log.error("Error sending email to {}", assessment.getCandidate().getEmail(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Assessment> getAssessments() {
|
||||
return assessmentRepository.findAll();
|
||||
}
|
||||
|
||||
public Assessment getAssessment(final UUID id) {
|
||||
return assessmentRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public Assessment startAssessment(final UUID id) {
|
||||
final Assessment assessment = assessmentRepository.findById(id).get();
|
||||
if (assessment.isStarted()) {
|
||||
return assessment;
|
||||
}
|
||||
|
||||
assessment.getAssessmentEvents().add(new AssessmentEvent(Instant.now(), AssessmentStatus.STARTED));
|
||||
|
||||
return assessmentRepository.save(assessment);
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final UUID assessmentId, final UUID currSubmissionId) {
|
||||
return getNextSubmission(assessmentId, currSubmissionId, true);
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final UUID assessmentId, final UUID currSubmissionId,
|
||||
final boolean checkCompleted) {
|
||||
final Assessment assessment = assessmentRepository.findById(assessmentId).get();
|
||||
|
||||
Assessment saved;
|
||||
if (currSubmissionId == null) {
|
||||
if (checkCompleted && assessment.isCompleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question firstQuestion = assessment.getQuestions().stream().findFirst().get();
|
||||
final Optional<Submission> submissionToReturn = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(firstQuestion))
|
||||
.findFirst();
|
||||
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
final Submission result = new Submission(firstQuestion, firstQuestion.getContent(), Map.of(),
|
||||
SubmissionStatus.FAIL, assessment);
|
||||
assessment.getSubmissions().add(result);
|
||||
}
|
||||
|
||||
saved = assessmentRepository.save(assessment);
|
||||
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(firstQuestion))
|
||||
.findFirst();
|
||||
|
||||
return submissionToReturn2.get();
|
||||
}
|
||||
|
||||
final Submission currSubmission = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getId().equals(currSubmissionId))
|
||||
.findFirst().get();
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
int idx = assessment.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == assessment.getQuestions().size() - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question nextQuestion = assessment.getQuestions().get(idx + 1);
|
||||
|
||||
final Optional<Submission> submissionToReturn = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
final Submission result = new Submission(nextQuestion, nextQuestion.getContent(), Map.of(),
|
||||
SubmissionStatus.FAIL, assessment);
|
||||
assessment.getSubmissions().add(result);
|
||||
}
|
||||
|
||||
saved = assessmentRepository.save(assessment);
|
||||
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
return submissionToReturn2.get();
|
||||
}
|
||||
|
||||
public Submission getPrevSubmission(final UUID assessmentId, final Submission currSubmission) {
|
||||
if (currSubmission == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
Assessment assessment = assessmentRepository.findById(assessmentId).get();
|
||||
int idx = assessment.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question prevQuestion = assessment.getQuestions().get(idx - 1);
|
||||
|
||||
return assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(prevQuestion))
|
||||
.findFirst().orElseThrow(() -> new IllegalStateException("submission invalid"));
|
||||
}
|
||||
|
||||
public Assessment completeAssessment(final UUID id) {
|
||||
Assessment assessment = assessmentRepository.findById(id).get();
|
||||
Optional<AssessmentEvent> completed = assessment.getAssessmentEvents().stream()
|
||||
.filter(e -> e.getStatus() == AssessmentStatus.COMPLETED)
|
||||
.findFirst();
|
||||
|
||||
if (completed.isPresent()) {
|
||||
return assessment;
|
||||
}
|
||||
|
||||
assessment.getAssessmentEvents().add(new AssessmentEvent(Instant.now(), AssessmentStatus.COMPLETED));
|
||||
Assessment saved = assessmentRepository.save(assessment);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
public void saveSubmission(final UUID id, final Submission currSubmission) {
|
||||
Assessment assessment = assessmentRepository.findById(id).get();
|
||||
final Submission submission = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getId().equals(currSubmission.getId()))
|
||||
.findFirst().get();
|
||||
|
||||
submission.setResponse(currSubmission.getResponse());
|
||||
Assessment saved = assessmentRepository.save(assessment);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Assessment saveAssessment(final Assessment assessment) {
|
||||
Candidate merged = entityManager.merge(assessment.getCandidate());
|
||||
List<Question> mergedQuestions = assessment.getQuestions().stream().map(entityManager::merge)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assessment.setCandidate(merged);
|
||||
assessment.setQuestions(mergedQuestions);
|
||||
|
||||
return assessmentRepository.save(assessment);
|
||||
}
|
||||
}
|
@ -13,8 +13,8 @@ import java.util.UUID;
|
||||
public class CandidateService {
|
||||
private final CandidateRepository candidateRepository;
|
||||
|
||||
public Candidate createOrUpdate(final Candidate candidate) {
|
||||
final Candidate saved = candidateRepository.save(candidate);
|
||||
public Candidate createOrUpdate(final Candidate assessment) {
|
||||
final Candidate saved = candidateRepository.save(assessment);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
@ -1,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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.Evaluation;
|
||||
import com.primefactorsolutions.repositories.EvaluationRepository;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class EvaluationService {
|
||||
private final EvaluationRepository evaluationRepository;
|
||||
|
||||
public Evaluation createOrUpdate(final Evaluation candidate) {
|
||||
return evaluationRepository.save(candidate);
|
||||
}
|
||||
|
||||
public List<Evaluation> getEvaluations() {
|
||||
return evaluationRepository.findAll();
|
||||
}
|
||||
|
||||
public Evaluation getEvaluation(final UUID id) {
|
||||
return evaluationRepository.findById(id).orElse(null);
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.repositories.ExamRepository;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class ExamService {
|
||||
|
||||
private final ExamRepository examRepository;
|
||||
private final EntityManager entityManager;
|
||||
private final JavaMailSender emailSender;
|
||||
|
||||
public Exam createOrUpdate(final Exam exam) {
|
||||
return examRepository.save(exam);
|
||||
}
|
||||
|
||||
public void sendEmail(final Exam exam) {
|
||||
try {
|
||||
final String evaluationLink = String.format("https://careers.primefactorsolutions.com/candidate-exam/%s",
|
||||
exam.getId());
|
||||
final SimpleMailMessage message = new SimpleMailMessage();
|
||||
message.setFrom("no-reply@primefactorsolutions.com");
|
||||
message.setBcc("no-reply@primefactorsolutions.com");
|
||||
message.setTo(exam.getCandidate().getEmail());
|
||||
message.setSubject("PFS - Evaluacion Tecnica");
|
||||
message.setText(String.format("Estimado candidato,\n\nGracias por su candidatura. En esta etapa del "
|
||||
+ "proceso de seleccion, usted debe completar "
|
||||
+ "una evaluacion tecnica de programacion en JAVA. La prueba tiene una duracion de 30 minutos y "
|
||||
+ "puede completarla cuando tenga una buena "
|
||||
+ "conexion de internet.\n\n"
|
||||
+ "Haga click aca: " + evaluationLink + "\n\n"
|
||||
+ "Exito!"));
|
||||
|
||||
emailSender.send(message);
|
||||
log.info("Sent email to {}", exam.getCandidate().getEmail());
|
||||
} catch (Exception e) {
|
||||
log.error("Error sending email to {}", exam.getCandidate().getEmail(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Exam> getExams() {
|
||||
return examRepository.findAll();
|
||||
}
|
||||
|
||||
public Exam getExam(final UUID id) {
|
||||
return examRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public Exam startExam(final UUID id) {
|
||||
final Exam exam = examRepository.findById(id).get();
|
||||
|
||||
if (exam.isStarted()) {
|
||||
return exam;
|
||||
}
|
||||
|
||||
exam.getExamEvents().add(new ExamEvent(Instant.now(), ExamStatus.STARTED));
|
||||
|
||||
return examRepository.save(exam);
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final UUID examId, final UUID currSubmissionId) {
|
||||
return getNextSubmission(examId, currSubmissionId, true);
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final UUID examId, final UUID currSubmissionId,
|
||||
final boolean checkCompleted) {
|
||||
final Exam exam = examRepository.findById(examId).get();
|
||||
|
||||
Exam saved;
|
||||
if (currSubmissionId == null) {
|
||||
if (checkCompleted && exam.isCompleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question firstQuestion = exam.getQuestions().stream().findFirst().get();
|
||||
final Optional<Submission> submissionToReturn = exam.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(firstQuestion))
|
||||
.findFirst();
|
||||
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
final Submission result = new Submission(firstQuestion, firstQuestion.getContent(), null,
|
||||
SubmissionStatus.FAIL, exam);
|
||||
exam.getSubmissions().add(result);
|
||||
}
|
||||
|
||||
saved = examRepository.save(exam);
|
||||
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(firstQuestion))
|
||||
.findFirst();
|
||||
|
||||
return submissionToReturn2.get();
|
||||
}
|
||||
|
||||
final Submission currSubmission = exam.getSubmissions().stream()
|
||||
.filter(s -> s.getId().equals(currSubmissionId))
|
||||
.findFirst().get();
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
int idx = exam.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == exam.getQuestions().size() - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question nextQuestion = exam.getQuestions().get(idx + 1);
|
||||
|
||||
final Optional<Submission> submissionToReturn = exam.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
final Submission result = new Submission(nextQuestion, nextQuestion.getContent(), null,
|
||||
SubmissionStatus.FAIL, exam);
|
||||
exam.getSubmissions().add(result);
|
||||
}
|
||||
|
||||
saved = examRepository.save(exam);
|
||||
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
return submissionToReturn2.get();
|
||||
}
|
||||
|
||||
public Submission getPrevSubmission(final UUID examId, final Submission currSubmission) {
|
||||
if (currSubmission == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
Exam exam = examRepository.findById(examId).get();
|
||||
int idx = exam.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question prevQuestion = exam.getQuestions().get(idx - 1);
|
||||
|
||||
return exam.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(prevQuestion))
|
||||
.findFirst().orElseThrow(() -> new IllegalStateException("submission invalid"));
|
||||
}
|
||||
|
||||
public Exam completeExam(final UUID id) {
|
||||
Exam exam = examRepository.findById(id).get();
|
||||
Optional<ExamEvent> completed = exam.getExamEvents().stream()
|
||||
.filter(e -> e.getStatus() == ExamStatus.COMPLETED)
|
||||
.findFirst();
|
||||
|
||||
if (completed.isPresent()) {
|
||||
return exam;
|
||||
}
|
||||
|
||||
exam.getExamEvents().add(new ExamEvent(Instant.now(), ExamStatus.COMPLETED));
|
||||
Exam saved = examRepository.save(exam);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
public void saveSubmission(final UUID id, final Submission currSubmission) {
|
||||
Exam exam = examRepository.findById(id).get();
|
||||
final Submission submission = exam.getSubmissions().stream()
|
||||
.filter(s -> s.getId().equals(currSubmission.getId()))
|
||||
.findFirst().get();
|
||||
|
||||
submission.setText(currSubmission.getText());
|
||||
Exam saved = examRepository.save(exam);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Exam saveExam(final Exam exam) {
|
||||
Candidate merged = entityManager.merge(exam.getCandidate());
|
||||
List<Question> mergedQuestions = exam.getQuestions().stream().map(entityManager::merge)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
exam.setCandidate(merged);
|
||||
exam.setQuestions(mergedQuestions);
|
||||
|
||||
return examRepository.save(exam);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.TimeOffRequestType;
|
||||
import com.primefactorsolutions.model.TimeOff;
|
||||
import com.primefactorsolutions.repositories.TimeOffRepository;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class TimeOffService {
|
||||
private final TimeOffRepository timeOffRepository;
|
||||
|
||||
public TimeOff getTimeOff(final UUID id) {
|
||||
return timeOffRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public void saveTimeOff(final TimeOff timeOff) {
|
||||
timeOffRepository.save(timeOff);
|
||||
}
|
||||
|
||||
public TimeOff findVacationByCategory(final TimeOffRequestType category) {
|
||||
return timeOffRepository.findByCategory(category);
|
||||
}
|
||||
|
||||
public List<TimeOff> findVacations() {
|
||||
return timeOffRepository.findAll();
|
||||
}
|
||||
|
||||
public List<TimeOff> findTimeOffs(final Integer year) {
|
||||
final LocalDate from = LocalDate.of(year, 1, 1);
|
||||
final LocalDate to = LocalDate.of(year, 12, 31);
|
||||
return timeOffRepository.findByDateBetween(from, to);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import com.primefactorsolutions.repositories.TimesheetEntryRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class TimesheetService {
|
||||
private final TimesheetEntryRepository timesheetEntryRepository;
|
||||
|
||||
@Autowired
|
||||
public TimesheetService(final TimesheetEntryRepository timesheetEntryRepository) {
|
||||
this.timesheetEntryRepository = timesheetEntryRepository;
|
||||
}
|
||||
|
||||
public List<TimesheetEntry> findAll() {
|
||||
return timesheetEntryRepository.findAll();
|
||||
}
|
||||
|
||||
public TimesheetEntry save(final TimesheetEntry timesheetEntry) {
|
||||
return timesheetEntryRepository.save(timesheetEntry);
|
||||
}
|
||||
|
||||
public TimesheetEntry getTimesheetEntry(final UUID id) {
|
||||
final Optional<TimesheetEntry> hoursWorked = timesheetEntryRepository.findById(id);
|
||||
return hoursWorked.orElse(null);
|
||||
}
|
||||
|
||||
public List<TimesheetEntry> findListHoursWorkedEmployee(final LocalDate from,
|
||||
final LocalDate to) {
|
||||
return timesheetEntryRepository.findByDateBetween(from, to);
|
||||
}
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Candidate;
|
||||
import com.primefactorsolutions.model.Exam;
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import com.primefactorsolutions.model.Question;
|
||||
import com.primefactorsolutions.service.ExamService;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.primefactorsolutions.service.QuestionService;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ItemLabelGenerator;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
@ -17,34 +15,32 @@ import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.fields.SubListSelector;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Exams")
|
||||
@Route(value = "/exams", layout = MainLayout.class)
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/assessments", layout = MainLayout.class)
|
||||
@Uses(ComboBox.class)
|
||||
public class ExamView extends BaseEntityForm<Exam> implements HasUrlParameter<String> {
|
||||
private final ExamService examService;
|
||||
public class AssessmentView extends BeanValidationForm<Assessment> implements HasUrlParameter<String> {
|
||||
private final AssessmentService assessmentService;
|
||||
|
||||
private final ComboBox<Candidate> candidate;
|
||||
private final SubListSelector<Question> questions;
|
||||
private ComboBox<Candidate> candidate = null;
|
||||
private SubListSelector<Question> questions = null;
|
||||
|
||||
public ExamView(final AuthenticationContext authorizationContext,
|
||||
final ExamService examService,
|
||||
final QuestionService questionService,
|
||||
final CandidateService candidateService) {
|
||||
super(authorizationContext, Exam.class);
|
||||
public AssessmentView(final AssessmentService assessmentService, final QuestionService questionService,
|
||||
final CandidateService candidateService) {
|
||||
super(Assessment.class);
|
||||
|
||||
this.examService = examService;
|
||||
this.assessmentService = assessmentService;
|
||||
|
||||
candidate = new ComboBox<>("Candidate", candidateService.getCandidates());
|
||||
candidate.setItemLabelGenerator((ItemLabelGenerator<Candidate>) Candidate::getEmail);
|
||||
@ -55,20 +51,20 @@ public class ExamView extends BaseEntityForm<Exam> implements HasUrlParameter<St
|
||||
questions.setReadOnly(false);
|
||||
questions.setAvailableOptions(questionService.getQuestions());
|
||||
|
||||
setSavedHandler((SavedHandler<Exam>) exam -> {
|
||||
this.examService.saveExam(exam);
|
||||
goTo(ExamsListView.class);
|
||||
setSavedHandler((SavedHandler<Assessment>) assessment -> {
|
||||
final var saved = this.assessmentService.saveAssessment(assessment);
|
||||
setEntityWithEnabledSave(saved);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
|
||||
final var exam = examService.getExam(UUID.fromString(s));
|
||||
final var assessment = assessmentService.getAssessment(UUID.fromString(s));
|
||||
|
||||
setEntityWithEnabledSave(exam);
|
||||
setEntityWithEnabledSave(assessment);
|
||||
} else {
|
||||
setEntityWithEnabledSave(new Exam());
|
||||
setEntityWithEnabledSave(new Assessment());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,127 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.DataProvider;
|
||||
import com.vaadin.flow.data.provider.DataProviderListener;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.addon.stefan.clipboard.ClientsideClipboard;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/assessments", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class AssessmentsListView extends Main {
|
||||
|
||||
public AssessmentsListView(final AssessmentService assessmentService) {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addAssessment = new Button("Add Assessment");
|
||||
addAssessment.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.getUI().get().navigate(AssessmentView.class, "new");
|
||||
});
|
||||
hl.add(addAssessment);
|
||||
|
||||
final VGrid<Assessment> grid = new VGrid<>(Assessment.class);
|
||||
grid.setColumns("id", "candidate.email");
|
||||
final Grid.Column<Assessment> statusColumn = grid.addColumn((ValueProvider<Assessment, Object>) assessment ->
|
||||
assessment.getAssessmentEvents().isEmpty()
|
||||
? "N/A"
|
||||
: assessment.getAssessmentEvents().getLast().getStatus().name());
|
||||
statusColumn.setHeader("Status");
|
||||
|
||||
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> {
|
||||
var result = new Button("Result", event ->
|
||||
this.getUI().get().navigate(SubmissionView.class, assessment.getId().toString())
|
||||
);
|
||||
result.setEnabled(assessment.isCompleted());
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> new Button("Copy Link", event ->
|
||||
ClientsideClipboard.writeToClipboard(
|
||||
String.format("email: %s link: https://careers.primefactorsolutions.com/evaluation/%s",
|
||||
assessment.getCandidate().getEmail(),
|
||||
assessment.getId()))
|
||||
));
|
||||
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment ->
|
||||
new Button("Send Email", event -> {
|
||||
ConfirmDialog dialog = new ConfirmDialog();
|
||||
dialog.setHeader("Send Link Email");
|
||||
dialog.setText(String.format("Enviar link por email al candidato %s?",
|
||||
assessment.getCandidate().getEmail()));
|
||||
dialog.setCancelable(true);
|
||||
dialog.setConfirmText("Enviar");
|
||||
dialog.setConfirmButtonTheme("primary");
|
||||
dialog.addConfirmListener((ComponentEventListener<ConfirmDialog.ConfirmEvent>) confirmEvent -> {
|
||||
try {
|
||||
assessmentService.sendEmail(assessment);
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error sending email: " + e.getMessage(), 10_000,
|
||||
Notification.Position.TOP_CENTER);
|
||||
}
|
||||
});
|
||||
dialog.open();
|
||||
}));
|
||||
|
||||
grid.setDataProvider(new DataProvider<>() {
|
||||
@Override
|
||||
public boolean isInMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(final Query<Assessment, Object> query) {
|
||||
return assessmentService.getAssessments().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Assessment> fetch(final Query<Assessment, Object> query) {
|
||||
int limit = query.getLimit();
|
||||
int pagerSize = query.getPageSize();
|
||||
int page = query.getPage();
|
||||
|
||||
return assessmentService.getAssessments().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshItem(final Assessment assessment) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAll() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registration addDataProviderListener(final DataProviderListener<Assessment> dataProviderListener) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
grid.setAllRowsVisible(true);
|
||||
|
||||
add(hl, grid);
|
||||
}
|
||||
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.views.util.AuthUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public abstract class BaseEntityForm<T> extends BeanValidationForm<T> {
|
||||
private final AuthenticationContext authenticationContext;
|
||||
private Button editButton;
|
||||
private EditHandler<T> editHandler;
|
||||
|
||||
public BaseEntityForm(final AuthenticationContext authenticationContext, final Class<T> entityType) {
|
||||
super(entityType);
|
||||
this.authenticationContext = authenticationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HorizontalLayout getToolbar() {
|
||||
return new HorizontalLayout(getCancelButton(), getEditButton(), getSaveButton());
|
||||
}
|
||||
|
||||
public Button getEditButton() {
|
||||
if (editButton == null) {
|
||||
editButton = new Button("Edit");
|
||||
editButton.setEnabled(false);
|
||||
editButton.addClickListener(__ -> {
|
||||
if (editHandler != null) {
|
||||
editHandler.onEdit(getEntity());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return editButton;
|
||||
}
|
||||
|
||||
public void setEditHandler(final EditHandler<T> editHandler) {
|
||||
this.editHandler = editHandler;
|
||||
getEditButton().setEnabled(editHandler != null);
|
||||
}
|
||||
|
||||
protected <V extends Component> void goTo(final Class<V> view) {
|
||||
getUI().ifPresent(ui -> ui.navigate(view));
|
||||
}
|
||||
|
||||
protected Button getCancelButton() {
|
||||
final Button cancelButton = new Button("Cancel");
|
||||
cancelButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
|
||||
UI.getCurrent().getPage().getHistory().back());
|
||||
|
||||
return cancelButton;
|
||||
}
|
||||
|
||||
protected boolean isRoleAdmin() {
|
||||
return AuthUtils.isAdmin(this.authenticationContext);
|
||||
}
|
||||
|
||||
protected Optional<UUID> getEmployeeId() {
|
||||
return AuthUtils.getEmployeeId(this.authenticationContext);
|
||||
}
|
||||
|
||||
public interface EditHandler<T> extends Serializable {
|
||||
|
||||
void onEdit(T entity);
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.views.util.AuthUtils;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public abstract class BaseView extends Main {
|
||||
|
||||
private final VerticalLayout currentPageLayout;
|
||||
private final AuthenticationContext authenticationContext;
|
||||
|
||||
public BaseView(final AuthenticationContext authenticationContext) {
|
||||
this.authenticationContext = authenticationContext;
|
||||
currentPageLayout = new VerticalLayout();
|
||||
add(currentPageLayout);
|
||||
}
|
||||
|
||||
protected boolean isRoleAdmin() {
|
||||
return AuthUtils.isAdmin(this.authenticationContext);
|
||||
}
|
||||
|
||||
protected Optional<UUID> getEmployeeId() {
|
||||
return AuthUtils.getEmployeeId(this.authenticationContext);
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
public class CalendarView {
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Candidate;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.textfield.EmailField;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
@ -11,35 +9,34 @@ import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Candidates")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/candidates", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class CandidateView extends BaseEntityForm<Candidate> implements HasUrlParameter<String> {
|
||||
@PermitAll
|
||||
public class CandidateView extends BeanValidationForm<Candidate> implements HasUrlParameter<String> {
|
||||
private final CandidateService candidateService;
|
||||
|
||||
private EmailField email = null;
|
||||
|
||||
public CandidateView(final AuthenticationContext authenticationContext,
|
||||
final CandidateService candidateService) {
|
||||
super(authenticationContext, Candidate.class);
|
||||
public CandidateView(final CandidateService candidateService) {
|
||||
super(Candidate.class);
|
||||
this.candidateService = candidateService;
|
||||
email = new EmailField();
|
||||
email.setWidthFull();
|
||||
email.setLabel("Email");
|
||||
|
||||
setSavedHandler((SavedHandler<Candidate>) candidate -> {
|
||||
candidateService.createOrUpdate(candidate);
|
||||
goTo(CandidatesListView.class);
|
||||
final Candidate saved = candidateService.createOrUpdate(candidate);
|
||||
setEntityWithEnabledSave(saved);
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Candidate;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.DataProvider;
|
||||
import com.vaadin.flow.data.provider.DataProviderListener;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Candidates")
|
||||
@Route(value = "/candidates", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class CandidatesListView extends Main {
|
||||
private final CandidateService candidateService;
|
||||
|
||||
public CandidatesListView(final CandidateService candidateService) {
|
||||
this.candidateService = candidateService;
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addCandidate = new Button("Add Candidate");
|
||||
addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.getUI().get().navigate(CandidateView.class, "new");
|
||||
});
|
||||
hl.add(addCandidate);
|
||||
|
||||
final VGrid<Candidate> grid = new VGrid<>(Candidate.class);
|
||||
grid.setColumns("id", "email");
|
||||
grid.setAllRowsVisible(true);
|
||||
grid.addComponentColumn((ValueProvider<Candidate, Component>) candidate -> {
|
||||
final Button edit = new Button("Edit");
|
||||
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
|
||||
this.getUI().get().navigate(CandidateView.class, candidate.getId().toString()));
|
||||
return edit;
|
||||
});
|
||||
|
||||
grid.setDataProvider(new DataProvider<>() {
|
||||
@Override
|
||||
public boolean isInMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(final Query<Candidate, Object> query) {
|
||||
return candidateService.getCandidates().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Candidate> fetch(final Query<Candidate, Object> query) {
|
||||
int limit = query.getLimit();
|
||||
int pagerSize = query.getPageSize();
|
||||
int page = query.getPage();
|
||||
return candidateService.getCandidates().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshItem(final Candidate candidate) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAll() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registration addDataProviderListener(final DataProviderListener<Candidate> dataProviderListener) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
add(hl, grid);
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
public class Constants {
|
||||
public static final int PAGE_SIZE = 10;
|
||||
}
|
@ -1,16 +1,13 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Document;
|
||||
import com.primefactorsolutions.model.DocumentType;
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.DocumentService;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
@ -26,6 +23,7 @@ import elemental.json.JsonObject;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -40,29 +38,28 @@ import java.io.InputStream;
|
||||
@Scope("prototype")
|
||||
@PageTitle("Document")
|
||||
@Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class)
|
||||
public class DocumentView extends BaseEntityForm<Document> implements HasUrlParameter<String> {
|
||||
private final TextField fileName = new TextField("Nombre del documento");
|
||||
private final ComboBox<DocumentType> documentType = new ComboBox<>("Tipo de documento");
|
||||
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
|
||||
private final Span pdfOnlyMessage = new Span("Únicamente se permite la carga de archivos en formato PDF.");
|
||||
public class DocumentView extends BeanValidationForm<Document> implements HasUrlParameter<String> {
|
||||
private final TextField fileName = new TextField("Document Name");
|
||||
private final ComboBox<DocumentType> documentType = new ComboBox<>("Document Type");
|
||||
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Employee");
|
||||
private final MemoryBuffer buffer = new MemoryBuffer();
|
||||
private final Upload uploadButton = new Upload(buffer);
|
||||
private final DocumentService documentService;
|
||||
private final EmployeeService employeeService;
|
||||
private final AuthenticationContext authContext;
|
||||
private boolean fileUploaded = false;
|
||||
private Button saveButton;
|
||||
private Button viewDocumentButton;
|
||||
|
||||
public DocumentView(final AuthenticationContext authenticationContext,
|
||||
final DocumentService documentService,
|
||||
|
||||
public DocumentView(final DocumentService documentService,
|
||||
final EmployeeService employeeService,
|
||||
final AuthenticationContext authContext) {
|
||||
super(authenticationContext, Document.class);
|
||||
super(Document.class);
|
||||
this.documentService = documentService;
|
||||
this.employeeService = employeeService;
|
||||
this.authContext = authContext;
|
||||
initializeView();
|
||||
this.setSavedHandler(this::saveDocument);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
@ -70,8 +67,20 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
configureUploadButton();
|
||||
}
|
||||
|
||||
protected Button createSaveButton() {
|
||||
saveButton = new Button("Save");
|
||||
saveButton.addClickListener(event -> saveDocument());
|
||||
return saveButton;
|
||||
}
|
||||
|
||||
protected Button createCloseButton() {
|
||||
Button closeButton = new Button("Close");
|
||||
closeButton.addClickListener(event -> closeForm());
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
protected Button createViewDocumentButton() {
|
||||
viewDocumentButton = new Button("Ver documento");
|
||||
viewDocumentButton = new Button("View Document");
|
||||
viewDocumentButton.setEnabled(false);
|
||||
viewDocumentButton.addClickListener(event -> viewDocument());
|
||||
return viewDocumentButton;
|
||||
@ -121,12 +130,17 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
});
|
||||
} catch (IOException e) {
|
||||
Notification.show("Error al leer el archivo.");
|
||||
Notification.show("Error reading file.");
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDocument(final Document document) {
|
||||
private void navigateToDocumentsListView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(DocumentsListView.class));
|
||||
}
|
||||
|
||||
private void saveDocument() {
|
||||
if (isFormValid()) {
|
||||
Document document = getEntity();
|
||||
document.setFileName(fileName.getValue());
|
||||
document.setDocumentType(documentType.getValue());
|
||||
document.setEmployee(employeeComboBox.getValue());
|
||||
@ -134,12 +148,17 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
setDocumentCreator(document);
|
||||
|
||||
documentService.saveDocument(document);
|
||||
getUI().ifPresent(ui -> ui.navigate(DocumentsListView.class));
|
||||
Notification.show("File saved successfully.");
|
||||
clearForm();
|
||||
} else {
|
||||
Notification.show("Error al guardar: Por favor, complete todos los campos y cargue un archivo.");
|
||||
Notification.show("Save failed: Please complete all fields and upload a file.");
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
navigateToDocumentsListView();
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return !fileName.isEmpty()
|
||||
&& documentType.getValue() != null
|
||||
@ -147,11 +166,20 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
&& fileUploaded;
|
||||
}
|
||||
|
||||
private void clearForm() {
|
||||
fileName.clear();
|
||||
documentType.clear();
|
||||
employeeComboBox.clear();
|
||||
fileUploaded = false;
|
||||
uploadButton.getElement().setPropertyJson("files", Json.createArray());
|
||||
viewDocumentButton.setEnabled(false);
|
||||
}
|
||||
|
||||
private byte[] readFileData() {
|
||||
try {
|
||||
return buffer.getInputStream().readAllBytes();
|
||||
} catch (IOException e) {
|
||||
Notification.show("Error al leer los datos del archivo.");
|
||||
Notification.show("Error reading file data.");
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
@ -168,10 +196,23 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
fileUploaded = true;
|
||||
}
|
||||
|
||||
private void updateSaveButtonState() {
|
||||
boolean isModified = !fileName.getValue().equals(getEntity().getFileName())
|
||||
|| documentType.getValue() != getEntity().getDocumentType()
|
||||
|| employeeComboBox.getValue() != getEntity().getEmployee()
|
||||
|| fileUploaded;
|
||||
saveButton.setEnabled(isModified);
|
||||
}
|
||||
|
||||
private void configureComponents() {
|
||||
setFileNameProperties();
|
||||
setDocumentTypeProperties();
|
||||
setEmployeeComboBoxProperties();
|
||||
fileName.addValueChangeListener(e -> updateSaveButtonState());
|
||||
documentType.addValueChangeListener(e -> updateSaveButtonState());
|
||||
employeeComboBox.addValueChangeListener(e -> updateSaveButtonState());
|
||||
uploadButton.addSucceededListener(e -> updateSaveButtonState());
|
||||
uploadButton.getElement().addEventListener("file-remove", event -> updateSaveButtonState());
|
||||
}
|
||||
|
||||
private void configureUploadButton() {
|
||||
@ -179,16 +220,31 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
uploadButton.setAcceptedFileTypes(".pdf");
|
||||
uploadButton.addSucceededListener(event -> {
|
||||
fileUploaded = true;
|
||||
Notification.show("Archivo cargado correctamente.");
|
||||
Notification.show("File uploaded successfully.");
|
||||
viewDocumentButton.setEnabled(true);
|
||||
updateSaveButtonState();
|
||||
});
|
||||
uploadButton.getElement().addEventListener("file-remove", event -> {
|
||||
fileUploaded = false;
|
||||
Notification.show("Archivo eliminado.");
|
||||
Notification.show("File removed.");
|
||||
viewDocumentButton.setEnabled(false);
|
||||
updateSaveButtonState();
|
||||
});
|
||||
}
|
||||
|
||||
private void configureViewOrEditAction(final String action, final String documentIdString) {
|
||||
if ("edit".equals(action) && !documentIdString.isEmpty()) {
|
||||
setFieldsReadOnly(false);
|
||||
preLoadFile(getEntity());
|
||||
viewDocumentButton.setEnabled(true);
|
||||
} else if ("view".equals(action) && !documentIdString.isEmpty()) {
|
||||
setFieldsReadOnly(true);
|
||||
preLoadFile(getEntity());
|
||||
saveButton.setEnabled(false);
|
||||
viewDocumentButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
@ -200,30 +256,18 @@ public class DocumentView extends BaseEntityForm<Document> implements HasUrlPara
|
||||
assert documentIdString != null;
|
||||
UUID documentId = UUID.fromString(documentIdString);
|
||||
Document document = documentService.getDocument(documentId);
|
||||
|
||||
setEntity(document);
|
||||
employeeComboBox.setValue(document.getEmployee());
|
||||
preLoadFile(document);
|
||||
|
||||
if ("edit".equals(action) && !documentIdString.isEmpty()) {
|
||||
setEntityWithEnabledSave(document);
|
||||
setFieldsReadOnly(false);
|
||||
preLoadFile(document);
|
||||
viewDocumentButton.setEnabled(true);
|
||||
} else if ("view".equals(action) && !documentIdString.isEmpty()) {
|
||||
setEntity(document);
|
||||
setFieldsReadOnly(true);
|
||||
preLoadFile(document);
|
||||
viewDocumentButton.setEnabled(true);
|
||||
}
|
||||
configureViewOrEditAction(action, documentIdString);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
final HorizontalLayout buttonLayout = new HorizontalLayout();
|
||||
HorizontalLayout buttonLayout = new HorizontalLayout();
|
||||
buttonLayout.add(uploadButton, createViewDocumentButton());
|
||||
buttonLayout.setSpacing(true);
|
||||
|
||||
return List.of(documentType, fileName, employeeComboBox, pdfOnlyMessage, buttonLayout);
|
||||
return List.of(fileName, documentType, employeeComboBox, buttonLayout, createCloseButton());
|
||||
}
|
||||
}
|
@ -1,36 +1,22 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.primefactorsolutions.model.Document;
|
||||
import com.primefactorsolutions.model.DocumentType;
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.DocumentService;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.contextmenu.MenuItem;
|
||||
import com.vaadin.flow.component.grid.GridSortOrder;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.menubar.MenuBar;
|
||||
import com.vaadin.flow.component.menubar.MenuBarVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.SortDirection;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamRegistration;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
@ -38,14 +24,13 @@ import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.List;
|
||||
|
||||
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Documents")
|
||||
@Route(value = "/documents", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class DocumentsListView extends BaseView {
|
||||
public class DocumentsListView extends Main {
|
||||
|
||||
private final DocumentService documentService;
|
||||
private final EmployeeService employeeService;
|
||||
@ -53,10 +38,7 @@ public class DocumentsListView extends BaseView {
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<DocumentType> documentTypeFilter;
|
||||
|
||||
public DocumentsListView(final AuthenticationContext authenticationContext,
|
||||
final DocumentService documentService,
|
||||
final EmployeeService employeeService) {
|
||||
super(authenticationContext);
|
||||
public DocumentsListView(final DocumentService documentService, final EmployeeService employeeService) {
|
||||
this.documentService = documentService;
|
||||
this.employeeService = employeeService;
|
||||
initializeView();
|
||||
@ -64,24 +46,16 @@ public class DocumentsListView extends BaseView {
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
getCurrentPageLayout().add(createActionButton("Añadir documento", this::navigateToAddDocumentView, true));
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createDocumentTypeFilter());
|
||||
hl.add(createEmployeeFilter());
|
||||
|
||||
getCurrentPageLayout().add(hl);
|
||||
|
||||
configureDocumentGrid();
|
||||
getCurrentPageLayout().add(documentGrid);
|
||||
add(createActionButton("Add Document", this::navigateToAddDocumentView));
|
||||
add(createDocumentTypeFilter());
|
||||
add(createEmployeeFilter());
|
||||
add(documentGrid);
|
||||
}
|
||||
|
||||
private void configureDocumentGrid() {
|
||||
documentGrid.setColumns("fileName", "documentType", "creator");
|
||||
documentGrid.getColumnByKey("fileName").setHeader("Nombre archivo");
|
||||
documentGrid.getColumnByKey("documentType").setHeader("Tipo");
|
||||
documentGrid.getColumnByKey("creator").setHeader("Creador");
|
||||
documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Empleado");
|
||||
documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Employee");
|
||||
addActionColumns();
|
||||
configurePagination();
|
||||
}
|
||||
@ -93,38 +67,25 @@ public class DocumentsListView extends BaseView {
|
||||
}
|
||||
|
||||
private void addActionColumns() {
|
||||
documentGrid.addComponentColumn((ValueProvider<Document, Component>) document -> {
|
||||
final MenuBar menuBar = new MenuBar();
|
||||
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||
final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.EYE, "Ver");
|
||||
viewItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
|
||||
navigateToDocumentView(document));
|
||||
final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Editar");
|
||||
editItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
|
||||
navigateToEditDocumentView(document));
|
||||
final MenuItem downloadItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.DOWNLOAD, "Descargar");
|
||||
downloadItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
|
||||
downloadDocument(document));
|
||||
return menuBar;
|
||||
});
|
||||
addDocumentActionColumn("View", this::navigateToDocumentView);
|
||||
addDocumentActionColumn("Edit", this::navigateToEditDocumentView);
|
||||
addDocumentActionColumn("Download", this::downloadDocument);
|
||||
}
|
||||
|
||||
private Button createActionButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
|
||||
private void addDocumentActionColumn(final String label, final DocumentActionHandler handler) {
|
||||
documentGrid.addComponentColumn(document -> createActionButton(label, () -> handler.handle(document)));
|
||||
}
|
||||
|
||||
private Button createActionButton(final String label, final Runnable onClickAction) {
|
||||
Button actionButton = new Button(label);
|
||||
|
||||
if (isPrimary) {
|
||||
actionButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
}
|
||||
|
||||
actionButton.addClickListener(event -> onClickAction.run());
|
||||
return actionButton;
|
||||
}
|
||||
|
||||
private ComboBox<DocumentType> createDocumentTypeFilter() {
|
||||
documentTypeFilter = new ComboBox<>("Tipo de documento");
|
||||
documentTypeFilter.setClearButtonVisible(true);
|
||||
documentTypeFilter.setPlaceholder("Seleccionar ...");
|
||||
documentTypeFilter = new ComboBox<>("Document Type");
|
||||
documentTypeFilter.setItems(DocumentType.values());
|
||||
documentTypeFilter.setValue(DocumentType.values()[0]);
|
||||
documentTypeFilter.addValueChangeListener(event -> {
|
||||
updateDocumentGrid(event.getValue(), employeeFilter.getValue());
|
||||
});
|
||||
@ -132,32 +93,26 @@ public class DocumentsListView extends BaseView {
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
|
||||
final List<Employee> employees;
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
employees = employeeService.findAllEmployees();
|
||||
employeeFilter.setItems(employees);
|
||||
} else {
|
||||
Employee employee = employeeService.getEmployee(getEmployeeId().get());
|
||||
employees = Lists.newArrayList(employee);
|
||||
employeeFilter.setItems(employees);
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
employeeFilter.setReadOnly(true);
|
||||
}
|
||||
|
||||
employeeFilter = new ComboBox<>("Employee");
|
||||
List<Employee> employees = employeeService.findAllEmployees();
|
||||
employees.addFirst(createAllEmployeesOption());
|
||||
employeeFilter.setItems(employees);
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeLabel);
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
employeeFilter.addValueChangeListener(event -> {
|
||||
updateDocumentGrid(documentTypeFilter.getValue(), event.getValue());
|
||||
});
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private Employee createAllEmployeesOption() {
|
||||
Employee allEmployeesOption = new Employee();
|
||||
allEmployeesOption.setFirstName("All");
|
||||
return allEmployeesOption;
|
||||
}
|
||||
|
||||
private String getEmployeeLabel(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
return employee.getFirstName().equals("All") ? "All" : employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private void navigateToEditDocumentView(final Document document) {
|
||||
@ -178,7 +133,7 @@ public class DocumentsListView extends BaseView {
|
||||
|
||||
private void configurePagination() {
|
||||
documentGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
documentGrid.setPageSize(PAGE_SIZE);
|
||||
documentGrid.setPageSize(5);
|
||||
}
|
||||
|
||||
private void updateDocumentGrid(final DocumentType documentType, final Employee employee) {
|
||||
@ -242,4 +197,9 @@ public class DocumentsListView extends BaseView {
|
||||
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface DocumentActionHandler {
|
||||
void handle(Document document);
|
||||
}
|
||||
}
|
487
src/main/java/com/primefactorsolutions/views/EmployeeView.java
Normal file
487
src/main/java/com/primefactorsolutions/views/EmployeeView.java
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,108 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
import com.vaadin.flow.component.grid.GridSortOrder;
|
||||
import com.vaadin.flow.data.provider.SortDirection;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Employees")
|
||||
@Route(value = "/employees", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class EmployeesListView extends Main {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class);
|
||||
|
||||
public EmployeesListView(final EmployeeService employeeService) {
|
||||
this.employeeService = employeeService;
|
||||
setupView();
|
||||
refreshGrid();
|
||||
}
|
||||
|
||||
private void setupView() {
|
||||
add(new H2("Employee List"));
|
||||
configureTable();
|
||||
add(createAddEmployeeButton());
|
||||
add(table);
|
||||
}
|
||||
|
||||
private void configureTable() {
|
||||
table.setColumns("firstName", "lastName", "status");
|
||||
addEditButtonColumn("View", this::navigateToEmployeeView);
|
||||
addEditButtonColumn("Edit", this::navigateToEditView);
|
||||
setupPagingGrid();
|
||||
}
|
||||
|
||||
private void addEditButtonColumn(final String label, final ButtonClickHandler handler) {
|
||||
table.addComponentColumn(employee -> createButton(label, () -> handler.handle(employee)));
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction) {
|
||||
Button button = new Button(label);
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddEmployeeButton() {
|
||||
return createButton("Add Employee", this::navigateToAddEmployeeView);
|
||||
}
|
||||
|
||||
private void navigateToEditView(final Employee employee) {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/edit"));
|
||||
}
|
||||
|
||||
private void navigateToEmployeeView(final Employee employee) {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/view"));
|
||||
}
|
||||
|
||||
private void navigateToAddEmployeeView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, "new"));
|
||||
}
|
||||
|
||||
private void setupPagingGrid() {
|
||||
table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
table.setPageSize(5);
|
||||
}
|
||||
|
||||
private void refreshGrid() {
|
||||
table.setPagingDataProvider((page, pageSize) -> fetchEmployees((int) page, pageSize));
|
||||
}
|
||||
|
||||
private List<Employee> fetchEmployees(final int page, final int pageSize) {
|
||||
int start = page * pageSize;
|
||||
if (hasSortOrder()) {
|
||||
return fetchSortedEmployees(start, pageSize);
|
||||
}
|
||||
return employeeService.findEmployees(start, pageSize);
|
||||
}
|
||||
|
||||
private boolean hasSortOrder() {
|
||||
return !table.getSortOrder().isEmpty();
|
||||
}
|
||||
|
||||
private List<Employee> fetchSortedEmployees(final int start, final int pageSize) {
|
||||
GridSortOrder<Employee> sortOrder = table.getSortOrder().getFirst();
|
||||
return employeeService.findEmployees(start, pageSize,
|
||||
sortOrder.getSorted().getKey(),
|
||||
sortOrder.getDirection() == SortDirection.ASCENDING);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ButtonClickHandler {
|
||||
void handle(Employee employee);
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import com.hilerio.ace.AceEditor;
|
||||
import com.hilerio.ace.AceMode;
|
||||
import com.hilerio.ace.AceTheme;
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.ExamService;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.primefactorsolutions.service.CompilerService;
|
||||
import com.vaadin.flow.component.*;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
@ -27,15 +27,19 @@ import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Background;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.BoxSizing;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Display;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Flex;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FlexDirection;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FontSize;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FontWeight;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Gap;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Height;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Margin;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Overflow;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Padding;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.TextColor;
|
||||
import io.overcoded.vaadin.panel.Panel;
|
||||
import io.overcoded.vaadin.panel.PanelConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@ -45,23 +49,24 @@ import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@PageTitle("Evaluacion")
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@Route(value = "/candidate-exam", layout = MainLayout.class)
|
||||
@Route(value = "/evaluation", layout = MainLayout.class)
|
||||
@AnonymousAllowed
|
||||
@Slf4j
|
||||
public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
public class EvaluationView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
private final CompilerService compilerService;
|
||||
private final ExamService examService;
|
||||
private final AssessmentService assessmentService;
|
||||
|
||||
private AceEditor questionEditor = null;
|
||||
private Dialog dialog = null;
|
||||
private Dialog completeDialog = null;
|
||||
private AceEditor result = null;
|
||||
private Exam exam = null;
|
||||
private Assessment assessment = null;
|
||||
private Submission currSubmission = null;
|
||||
private Boolean isCompleted = false;
|
||||
|
||||
@ -69,48 +74,33 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
private MenuItem prev = null;
|
||||
private MenuItem next = null;
|
||||
private MenuItem reset = null;
|
||||
private MenuItem finish = null;
|
||||
private Panel candidatePanel = null;
|
||||
private Section sidebar = null;
|
||||
private SimpleTimer timer = null;
|
||||
private DescriptionList dl = null;
|
||||
private Section editorSection = null;
|
||||
private Section startSection = null;
|
||||
private Section completedSection = null;
|
||||
private H3 questionTitle = null;
|
||||
private Text questionDescription = null;
|
||||
|
||||
public CandidateExamView(final CompilerService compilerService, final ExamService examService) {
|
||||
public EvaluationView(final CompilerService compilerService, final AssessmentService assessmentService) {
|
||||
this.compilerService = compilerService;
|
||||
this.examService = examService;
|
||||
this.assessmentService = assessmentService;
|
||||
|
||||
addClassNames(Display.FLEX, Flex.GROW, Height.FULL);
|
||||
|
||||
initStartSection();
|
||||
initCompletedSection();
|
||||
initEditorSection();
|
||||
initResultDialog();
|
||||
initCompleteDialog();
|
||||
initTimer(examService);
|
||||
initCandidatePanel();
|
||||
initSidebar();
|
||||
|
||||
VerticalLayout vl = new VerticalLayout();
|
||||
vl.add(completedSection, candidatePanel, startSection, editorSection, dialog);
|
||||
add(vl);
|
||||
add(completedSection, startSection, editorSection, sidebar, dialog);
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private void initTimer(final ExamService examService) {
|
||||
timer = new SimpleTimer(0);
|
||||
timer.setMinutes(true);
|
||||
timer.addTimerEndEvent((ComponentEventListener<SimpleTimer.TimerEndedEvent>) timerEndedEvent -> {
|
||||
Notification.show("Tiempo completado.", 5_000, Notification.Position.TOP_CENTER);
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.examService.saveSubmission(exam.getId(), this.currSubmission);
|
||||
this.exam = examService.completeExam(exam.getId());
|
||||
goToCompleted();
|
||||
updateUI();
|
||||
});
|
||||
timer.setFractions(false);
|
||||
}
|
||||
|
||||
private void initResultDialog() {
|
||||
dialog = new Dialog();
|
||||
dialog.setHeaderTitle("Resultados");
|
||||
@ -138,8 +128,8 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
dialog.add(dialogLayout);
|
||||
|
||||
final Button saveButton = new Button("Guardar y Siguiente", e -> {
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.examService.saveSubmission(exam.getId(), this.currSubmission);
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
|
||||
dialog.close();
|
||||
goToNext();
|
||||
});
|
||||
@ -166,9 +156,9 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
final Button completeButton = new Button("Terminar", e -> {
|
||||
completeDialog.close();
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.examService.saveSubmission(exam.getId(), this.currSubmission);
|
||||
this.exam = examService.completeExam(exam.getId());
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
|
||||
this.assessment = assessmentService.completeAssessment(assessment.getId());
|
||||
goToCompleted();
|
||||
updateUI();
|
||||
});
|
||||
@ -205,26 +195,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();
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Profile")
|
||||
@Route(value = "/profiles", layout = MainLayout.class)
|
||||
public class ProfileView extends Main {
|
||||
}
|
||||
|
@ -1,29 +1,27 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Question;
|
||||
import com.primefactorsolutions.service.QuestionService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.textfield.IntegerField;
|
||||
import com.vaadin.flow.component.textfield.TextArea;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Questions")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/questions", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class QuestionView extends BaseEntityForm<Question> implements HasUrlParameter<String> {
|
||||
@PermitAll
|
||||
public class QuestionView extends BeanValidationForm<Question> implements HasUrlParameter<String> {
|
||||
private final QuestionService questionService;
|
||||
|
||||
private TextField title = null;
|
||||
@ -31,9 +29,8 @@ public class QuestionView extends BaseEntityForm<Question> implements HasUrlPara
|
||||
private TextArea content = null;
|
||||
private IntegerField timeMinutes = null;
|
||||
|
||||
public QuestionView(final AuthenticationContext authenticationContext,
|
||||
final QuestionService questionService) {
|
||||
super(authenticationContext, Question.class);
|
||||
public QuestionView(final QuestionService questionService) {
|
||||
super(Question.class);
|
||||
this.questionService = questionService;
|
||||
title = new TextField();
|
||||
title.setWidthFull();
|
||||
@ -51,8 +48,8 @@ public class QuestionView extends BaseEntityForm<Question> implements HasUrlPara
|
||||
content.setLabel("Content");
|
||||
|
||||
setSavedHandler((SavedHandler<Question>) question -> {
|
||||
questionService.createOrUpdate(question);
|
||||
goTo(QuestionsListView.class);
|
||||
final Question saved = questionService.createOrUpdate(question);
|
||||
setEntityWithEnabledSave(saved);
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Question;
|
||||
import com.primefactorsolutions.service.QuestionService;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.DataProvider;
|
||||
import com.vaadin.flow.data.provider.DataProviderListener;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Questions")
|
||||
@Route(value = "/questions", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class QuestionsListView extends Main {
|
||||
private final QuestionService questionService;
|
||||
|
||||
public QuestionsListView(final QuestionService questionService) {
|
||||
this.questionService = questionService;
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addQuestion = new Button("Add Question");
|
||||
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.getUI().get().navigate(QuestionView.class, "new");
|
||||
});
|
||||
hl.add(addQuestion);
|
||||
|
||||
final VGrid<Question> grid = new VGrid<>(Question.class);
|
||||
grid.setColumns("id", "title");
|
||||
grid.addComponentColumn((ValueProvider<Question, Component>) question -> {
|
||||
final Button edit = new Button("Edit");
|
||||
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
|
||||
this.getUI().get().navigate(QuestionView.class, question.getId().toString()));
|
||||
return edit;
|
||||
});
|
||||
grid.setDataProvider(new DataProvider<>() {
|
||||
@Override
|
||||
public boolean isInMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(final Query<Question, Object> query) {
|
||||
return questionService.getQuestions().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Question> fetch(final Query<Question, Object> query) {
|
||||
int limit = query.getLimit();
|
||||
int pagerSize = query.getPageSize();
|
||||
int page = query.getPage();
|
||||
return questionService.getQuestions().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshItem(final Question question) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAll() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registration addDataProviderListener(final DataProviderListener<Question> dataProviderListener) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
grid.setAllRowsVisible(true);
|
||||
|
||||
add(hl, grid);
|
||||
}
|
||||
}
|
156
src/main/java/com/primefactorsolutions/views/RequestView.java
Normal file
156
src/main/java/com/primefactorsolutions/views/RequestView.java
Normal 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));
|
||||
}
|
||||
}
|
@ -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()));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ package com.primefactorsolutions.views;
|
||||
import com.hilerio.ace.AceEditor;
|
||||
import com.hilerio.ace.AceMode;
|
||||
import com.hilerio.ace.AceTheme;
|
||||
import com.primefactorsolutions.model.Exam;
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import com.primefactorsolutions.model.Submission;
|
||||
import com.primefactorsolutions.service.ExamService;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.primefactorsolutions.service.CompilerService;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
@ -20,9 +20,9 @@ import com.vaadin.flow.component.menubar.MenuBarVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.*;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@ -37,17 +37,18 @@ import java.util.stream.Collectors;
|
||||
@PageTitle("Evaluacion")
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@Route(value = "/submissions", layout = MainLayout.class)
|
||||
@Route(value = "/submission", layout = MainLayout.class)
|
||||
@AnonymousAllowed
|
||||
@Slf4j
|
||||
public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
private final CompilerService compilerService;
|
||||
private final ExamService examService;
|
||||
private final AssessmentService assessmentService;
|
||||
|
||||
private AceEditor questionEditor = null;
|
||||
private AceEditor result = null;
|
||||
private Dialog dialog = null;
|
||||
private Exam exam = null;
|
||||
private Assessment assessment = null;
|
||||
private Submission currSubmission = null;
|
||||
private MenuItem prev = null;
|
||||
private MenuItem next = null;
|
||||
@ -56,9 +57,9 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
private Section editorSection = null;
|
||||
private H3 questionTitle = null;
|
||||
|
||||
public SubmissionView(final CompilerService compilerService, final ExamService examService) {
|
||||
public SubmissionView(final CompilerService compilerService, final AssessmentService assessmentService) {
|
||||
this.compilerService = compilerService;
|
||||
this.examService = examService;
|
||||
this.assessmentService = assessmentService;
|
||||
|
||||
addClassNames(Display.FLEX, Flex.GROW, Height.FULL);
|
||||
|
||||
@ -130,13 +131,13 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
prev = navMenuBar.addItem("Anterior pregunta",
|
||||
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
log.info(">>> prev");
|
||||
this.currSubmission = this.examService.getPrevSubmission(exam.getId(),
|
||||
this.currSubmission = this.assessmentService.getPrevSubmission(assessment.getId(),
|
||||
this.currSubmission);
|
||||
updateUI();
|
||||
});
|
||||
next = navMenuBar.addItem("Siguiente pregunta",
|
||||
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
this.currSubmission.setText(this.questionEditor.getValue());
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
goToNext();
|
||||
});
|
||||
|
||||
@ -185,7 +186,7 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
private void goToNext() {
|
||||
log.info(">>> next");
|
||||
Submission found = this.examService.getNextSubmission(exam.getId(),
|
||||
Submission found = this.assessmentService.getNextSubmission(assessment.getId(),
|
||||
this.currSubmission.getId(), false);
|
||||
|
||||
if (found != null) {
|
||||
@ -208,25 +209,25 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if (exam == null || !exam.isStarted()) {
|
||||
if (assessment == null || !assessment.isStarted()) {
|
||||
editorSection.setVisible(false);
|
||||
sidebar.setVisible(false);
|
||||
} else {
|
||||
if (currSubmission != null) {
|
||||
questionEditor.setValue(this.currSubmission.getText());
|
||||
questionEditor.setValue(this.currSubmission.getResponse());
|
||||
questionTitle.setText(this.currSubmission.getQuestion().getTitle());
|
||||
}
|
||||
|
||||
editorSection.setVisible(true);
|
||||
sidebar.setVisible(true);
|
||||
|
||||
prev.setEnabled(currSubmission != null && !exam.isFirst(currSubmission));
|
||||
next.setEnabled(currSubmission == null || !exam.isLast(currSubmission));
|
||||
prev.setEnabled(currSubmission != null && !assessment.isFirst(currSubmission));
|
||||
next.setEnabled(currSubmission == null || !assessment.isLast(currSubmission));
|
||||
|
||||
if (dl.getChildren().collect(Collectors.toList()).isEmpty()) {
|
||||
dl.add(
|
||||
createItem("Candidato:", exam.getCandidate().getEmail()),
|
||||
createItem("Hora de inicio:", Optional.ofNullable(exam.getStartingTime())
|
||||
createItem("Candidato:", assessment.getCandidate().getEmail()),
|
||||
createItem("Hora de inicio:", Optional.ofNullable(assessment.getStartingTime())
|
||||
.map(t -> ZonedDateTime.ofInstant(t,
|
||||
ZoneId.of("GMT-4")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
|
||||
.orElse("N/A"))
|
||||
@ -258,14 +259,14 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
this.exam = this.examService.getExam(UUID.fromString(s));
|
||||
this.assessment = this.assessmentService.getAssessment(UUID.fromString(s));
|
||||
|
||||
if (this.exam == null) {
|
||||
if (this.assessment == null) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
this.currSubmission = this.exam.isStarted()
|
||||
? this.examService.getNextSubmission(exam.getId(), null, false)
|
||||
this.currSubmission = this.assessment.isStarted()
|
||||
? this.assessmentService.getNextSubmission(assessment.getId(), null, false)
|
||||
: null;
|
||||
|
||||
updateUI();
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Timeoff")
|
||||
@Route(value = "/timeoffs/me", layout = MainLayout.class)
|
||||
public class TimeoffView extends Main {
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Timesheets")
|
||||
@Route(value = "/timesheets", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class TimesheestReportView extends Main {
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Timesheet")
|
||||
@Route(value = "/timesheets/me", layout = MainLayout.class)
|
||||
public class TimesheetView extends Main {
|
||||
}
|
||||
|
@ -1,135 +0,0 @@
|
||||
package com.primefactorsolutions.views.admin;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.TimeOffService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.contextmenu.MenuItem;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.menubar.MenuBar;
|
||||
import com.vaadin.flow.component.menubar.MenuBarVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Registro de Vacaciones")
|
||||
@Route(value = "/time-off/list", layout = MainLayout.class)
|
||||
public class TimeOffListView extends BaseView {
|
||||
|
||||
private final TimeOffService timeOffService;
|
||||
private final VGrid<TimeOff> timeOffGrid = new VGrid<>();
|
||||
private ComboBox<Integer> yearFilter;
|
||||
|
||||
public TimeOffListView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffService timeOffService) {
|
||||
super(authenticationContext);
|
||||
this.timeOffService = timeOffService;
|
||||
|
||||
initializeView();
|
||||
}
|
||||
|
||||
private void refreshGridListHoursWorked(final Integer year) {
|
||||
final List<TimeOff> timeOffs = timeOffService.findTimeOffs(year);
|
||||
timeOffGrid.setDataProvider(new ListDataProvider<>(timeOffs));
|
||||
timeOffGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
getCurrentPageLayout().add(createAddTimeOff());
|
||||
setupFilters();
|
||||
setupListHoursWorkedGrid();
|
||||
getCurrentPageLayout().add(timeOffGrid);
|
||||
refreshGridListHoursWorked(yearFilter.getValue());
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createYearFilter());
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private ComboBox<Integer> createYearFilter() {
|
||||
yearFilter = new ComboBox<>("Year");
|
||||
final int nowYear = LocalDate.now().getYear();
|
||||
final List<Integer> years = IntStream.range(0, 2).mapToObj(y -> nowYear - y).toList();
|
||||
|
||||
yearFilter.setItems(years);
|
||||
yearFilter.setValue(years.getFirst());
|
||||
yearFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(event.getValue())
|
||||
);
|
||||
|
||||
return yearFilter;
|
||||
}
|
||||
|
||||
private void setupListHoursWorkedGrid() {
|
||||
timeOffGrid.addColumn(e -> e.getCategory().name())
|
||||
.setHeader("Categoria");
|
||||
timeOffGrid.addColumn(e -> e.getType().name())
|
||||
.setHeader("Tipo");
|
||||
timeOffGrid.addColumn(TimeOff::getDate)
|
||||
.setHeader("Fecha");
|
||||
timeOffGrid.addColumn(TimeOff::getDuration).setHeader("Duracion");
|
||||
timeOffGrid.addColumn(TimeOff::getExpiration).setHeader("Expiracion");
|
||||
timeOffGrid.addComponentColumn((ValueProvider<TimeOff, Component>) timeOff -> {
|
||||
final MenuBar menuBar = new MenuBar();
|
||||
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||
final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.EYE, "Ver");
|
||||
viewItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
navigateToTimeOffView(timeOff.getId(), "view");
|
||||
});
|
||||
final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Editar");
|
||||
editItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
navigateToTimeOffView(timeOff.getId(), "edit");
|
||||
});
|
||||
return menuBar;
|
||||
});
|
||||
}
|
||||
|
||||
private void navigateToTimeOffView(final UUID idRecord, final String action) {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffView.class, idRecord.toString() + "/" + action));
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
|
||||
final Button button = new Button(label);
|
||||
|
||||
if (isPrimary) {
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
}
|
||||
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddTimeOff() {
|
||||
return createButton("Agregar Vacacion", this::navigateToTimeOff, true);
|
||||
}
|
||||
|
||||
private void navigateToTimeOff() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffView.class, "new"));
|
||||
}
|
||||
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
package com.primefactorsolutions.views.admin;
|
||||
|
||||
import com.primefactorsolutions.model.TimeOff;
|
||||
import com.primefactorsolutions.model.TimeOffRequestType;
|
||||
import com.primefactorsolutions.service.TimeOffService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.datepicker.VDatePicker;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Vacaciones")
|
||||
@Route(value = "/time-off/:hours-workedId?/:action?", layout = MainLayout.class)
|
||||
public class TimeOffView extends BaseEntityForm<TimeOff> implements HasUrlParameter<String> {
|
||||
private final ComboBox<TimeOffRequestType> category = new ComboBox<>("Categoria");
|
||||
private final ComboBox<TimeOff.Type> type = new ComboBox<>("Tipo");
|
||||
private final VDatePicker date = new VDatePicker("Fecha");
|
||||
private final NumberField duration = new NumberField("Duracion");
|
||||
private final NumberField expiration = new NumberField("Expiracion");
|
||||
|
||||
private final TimeOffService timeOffService;
|
||||
|
||||
public TimeOffView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffService timeOffService) {
|
||||
super(authenticationContext, TimeOff.class);
|
||||
this.timeOffService = timeOffService;
|
||||
|
||||
initializeDateField();
|
||||
category.setItems(TimeOffRequestType.values());
|
||||
type.setItems(TimeOff.Type.values());
|
||||
|
||||
this.setSavedHandler(this::saveTimeOff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
final String s = params.get("hours-workedId").orElse(null);
|
||||
|
||||
if ("new".equals(action) || s == null) {
|
||||
setEntityWithEnabledSave(new TimeOff());
|
||||
} else {
|
||||
final UUID timeOffId = UUID.fromString(s);
|
||||
final TimeOff timeOff = timeOffService.getTimeOff(timeOffId);
|
||||
|
||||
if ("edit".equals(action) && !s.isEmpty()) {
|
||||
setEntityWithEnabledSave(timeOff);
|
||||
} else if ("view".equals(action) && !s.isEmpty()) {
|
||||
setEntity(timeOff);
|
||||
duration.setReadOnly(true);
|
||||
expiration.setReadOnly(true);
|
||||
category.setReadOnly(true);
|
||||
type.setReadOnly(true);
|
||||
date.setReadOnly(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
category,
|
||||
type,
|
||||
date,
|
||||
duration,
|
||||
expiration
|
||||
);
|
||||
}
|
||||
|
||||
private void initializeDateField() {
|
||||
final LocalDate today = LocalDate.now();
|
||||
final YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth());
|
||||
final LocalDate startOfMonth = currentMonth.atDay(1);
|
||||
|
||||
date.setWidthFull();
|
||||
date.setMin(startOfMonth);
|
||||
date.setMax(today);
|
||||
date.setValue(today);
|
||||
}
|
||||
|
||||
private void saveTimeOff(final TimeOff timeOff) {
|
||||
if (isFormValid()) {
|
||||
timeOffService.saveTimeOff(timeOff);
|
||||
closeForm();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffListView.class));
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return date.getValue() != null;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.Candidate;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Candidates")
|
||||
@Route(value = "/candidates", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class CandidatesListView extends BaseView {
|
||||
|
||||
public CandidatesListView(final AuthenticationContext authenticationContext,
|
||||
final CandidateService candidateService) {
|
||||
super(authenticationContext);
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addCandidate = new Button("Add Candidate");
|
||||
addCandidate.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, "new"));
|
||||
});
|
||||
hl.add(addCandidate);
|
||||
|
||||
final VGrid<Candidate> grid = new VGrid<>(Candidate.class);
|
||||
grid.setColumns("email");
|
||||
grid.setAllRowsVisible(true);
|
||||
grid.addComponentColumn(candidate ->
|
||||
MenuBarUtils.menuBar(Map.of(Pair.of("Edit", VaadinIcon.PENCIL), menuItemClickEvent ->
|
||||
getUI().flatMap(ui -> ui.navigate(CandidateView.class, candidate.getId().toString())))));
|
||||
grid.setDataProvider(new ListDataProvider<>(candidateService.getCandidates()));
|
||||
|
||||
getCurrentPageLayout().add(hl, grid);
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.CandidateService;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.EvaluationService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.EntityComboBox;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.Text;
|
||||
import com.vaadin.flow.component.textfield.IntegerField;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.fields.ElementCollectionField;
|
||||
import org.vaadin.firitin.fields.EnumSelect;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Evaluations")
|
||||
@Route(value = "/evaluations", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class EvaluationView extends BaseEntityForm<Evaluation> implements HasUrlParameter<String> {
|
||||
private final CandidateService candidateService;
|
||||
private final EvaluationService evaluationService;
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
private final EntityComboBox<Candidate> candidate = new EntityComboBox<>("Candidate");
|
||||
private final EntityComboBox<Employee> interviewer = new EntityComboBox<>("Interviewer");
|
||||
private final EnumSelect<EmployeePosition> candidatePosition =
|
||||
new EnumSelect<>("Position", EmployeePosition.class);
|
||||
private final IntegerField points = new IntegerField("Points");
|
||||
private final Text skillLabel = new Text("Skills");
|
||||
private final ElementCollectionField<SkillEvaluation> skillEvaluations =
|
||||
new ElementCollectionField<>(SkillEvaluation.class);
|
||||
|
||||
public EvaluationView(final AuthenticationContext authenticationContext,
|
||||
final CandidateService candidateService,
|
||||
final EmployeeService employeeService,
|
||||
final EvaluationService evaluationService) {
|
||||
super(authenticationContext, Evaluation.class);
|
||||
this.employeeService = employeeService;
|
||||
this.evaluationService = evaluationService;
|
||||
this.candidateService = candidateService;
|
||||
|
||||
this.candidate.setWidthFull();
|
||||
this.interviewer.setWidthFull();
|
||||
this.points.setWidthFull();
|
||||
this.skillEvaluations.setWidthFull();
|
||||
this.candidatePosition.setWidthFull();
|
||||
this.candidate.setItems(this.candidateService.getCandidates());
|
||||
this.interviewer.setItems(this.employeeService.getEmployees());
|
||||
|
||||
setSavedHandler(evaluation -> goTo(EvaluationsListView.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
|
||||
var evaluation = evaluationService.getEvaluation(UUID.fromString(s));
|
||||
setEntityWithEnabledSave(evaluation);
|
||||
} else {
|
||||
setEntityWithEnabledSave(new Evaluation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
candidate,
|
||||
points,
|
||||
candidatePosition,
|
||||
skillLabel,
|
||||
skillEvaluations,
|
||||
interviewer
|
||||
);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.Evaluation;
|
||||
import com.primefactorsolutions.service.EvaluationService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Evaluations")
|
||||
@Route(value = "/evaluations", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class EvaluationsListView extends BaseView {
|
||||
|
||||
public EvaluationsListView(final AuthenticationContext authenticationContext,
|
||||
final EvaluationService evaluationService) {
|
||||
super(authenticationContext);
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addEvaluation = new Button("Add Evaluation");
|
||||
addEvaluation.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
addEvaluation.addClickListener(buttonClickEvent -> {
|
||||
this.getUI().flatMap(ui -> ui.navigate(EvaluationView.class, "new"));
|
||||
});
|
||||
hl.add(addEvaluation);
|
||||
|
||||
final VGrid<Evaluation> grid = new VGrid<>(Evaluation.class);
|
||||
grid.setColumns("candidate.email");
|
||||
grid.setAllRowsVisible(true);
|
||||
grid.addComponentColumn(evaluation ->
|
||||
MenuBarUtils.menuBar(Map.of(Pair.of("Edit", VaadinIcon.PENCIL), menuItemClickEvent ->
|
||||
getUI().flatMap(ui -> ui.navigate(EvaluationView.class, evaluation.getId().toString())))));
|
||||
grid.setDataProvider(new ListDataProvider<>(evaluationService.getEvaluations()));
|
||||
|
||||
getCurrentPageLayout().add(hl, grid);
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.Exam;
|
||||
import com.primefactorsolutions.service.ExamService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.SubmissionView;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.addon.stefan.clipboard.ClientsideClipboard;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Exams")
|
||||
@Route(value = "/exams", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class ExamsListView extends BaseView {
|
||||
|
||||
public ExamsListView(final AuthenticationContext authenticationContext,
|
||||
final ExamService examService) {
|
||||
super(authenticationContext);
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addExam = new Button("Add Exam");
|
||||
addExam.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
addExam.addClickListener(buttonClickEvent ->
|
||||
getUI().flatMap(ui -> ui.navigate(ExamView.class, "new")));
|
||||
hl.add(addExam);
|
||||
|
||||
final VGrid<Exam> grid = new VGrid<>(Exam.class);
|
||||
grid.setColumns("id", "candidate.email");
|
||||
final Grid.Column<Exam> statusColumn = grid.addColumn(exam ->
|
||||
exam.getExamEvents().isEmpty()
|
||||
? "N/A"
|
||||
: exam.getExamEvents().getLast().getStatus().name());
|
||||
statusColumn.setHeader("Status");
|
||||
|
||||
grid.addComponentColumn(exam -> MenuBarUtils.menuBar(
|
||||
Pair.of("View", __ ->
|
||||
getUI().flatMap(ui -> ui.navigate(SubmissionView.class, exam.getId().toString()))),
|
||||
Pair.of("Copy", __ ->
|
||||
ClientsideClipboard.writeToClipboard(
|
||||
String.format("email: %s link: "
|
||||
+ "https://intra.primefactorsolutions.com/candidate-exam/%s",
|
||||
exam.getCandidate().getEmail(),
|
||||
exam.getId()))),
|
||||
Pair.of("Email", __ -> {
|
||||
ConfirmDialog dialog = new ConfirmDialog();
|
||||
dialog.setHeader("Send Link Email");
|
||||
dialog.setText(String.format("Enviar link por email al candidato %s?",
|
||||
exam.getCandidate().getEmail()));
|
||||
dialog.setCancelable(true);
|
||||
dialog.setConfirmText("Enviar");
|
||||
dialog.setConfirmButtonTheme("primary");
|
||||
dialog.addConfirmListener(confirmEvent -> {
|
||||
try {
|
||||
examService.sendEmail(exam);
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error sending email: " + e.getMessage(), 10_000,
|
||||
Notification.Position.TOP_CENTER);
|
||||
}
|
||||
});
|
||||
dialog.open();
|
||||
})
|
||||
));
|
||||
|
||||
grid.setDataProvider(new ListDataProvider<>(examService.getExams()));
|
||||
grid.setAllRowsVisible(true);
|
||||
|
||||
getCurrentPageLayout().add(hl, grid);
|
||||
}
|
||||
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package com.primefactorsolutions.views.assessment;
|
||||
|
||||
import com.primefactorsolutions.model.Question;
|
||||
import com.primefactorsolutions.service.QuestionService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Questions")
|
||||
@Route(value = "/questions", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
public class QuestionsListView extends BaseView {
|
||||
|
||||
public QuestionsListView(final AuthenticationContext authenticationContext, final QuestionService questionService) {
|
||||
super(authenticationContext);
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
final Button addQuestion = new Button("Add Question");
|
||||
addQuestion.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
|
||||
this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, "new")));
|
||||
hl.add(addQuestion);
|
||||
|
||||
final VGrid<Question> grid = new VGrid<>(Question.class);
|
||||
grid.setColumns("title", "description", "timeMinutes");
|
||||
grid.addComponentColumn(question ->
|
||||
MenuBarUtils.menuBar(Pair.of("Edit", __ ->
|
||||
getUI().flatMap(ui -> ui.navigate(QuestionView.class, question.getId().toString())))));
|
||||
grid.setDataProvider(new ListDataProvider<>(questionService.getQuestions()));
|
||||
grid.setAllRowsVisible(true);
|
||||
|
||||
getCurrentPageLayout().add(hl, grid);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.ReportService;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.html.Anchor;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.UUID;
|
||||
@PermitAll
|
||||
@PageTitle("Reporte excel")
|
||||
@Route("employee-report")
|
||||
public class EmployeeReportView extends VerticalLayout implements HasUrlParameter<String> {
|
||||
private final EmployeeService employeeService;
|
||||
private final ReportService reportService;
|
||||
|
||||
public EmployeeReportView(final EmployeeService employeeService, final ReportService reportService) {
|
||||
this.employeeService = employeeService;
|
||||
this.reportService = reportService;
|
||||
Button backButton = new Button("Volver al Reporte de Empleados", event ->
|
||||
UI.getCurrent().navigate(EmployeesListView.class));
|
||||
backButton.addClassName("back-button");
|
||||
add(backButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent event, @OptionalParameter final String employeeId) {
|
||||
if (employeeId != null) {
|
||||
UUID id = UUID.fromString(employeeId);
|
||||
Employee employee = employeeService.getEmployee(id);
|
||||
if (employee != null) {
|
||||
generateExcelReport(employee);
|
||||
} else {
|
||||
Notification.show("Empleado no encontrado", 3000, Notification.Position.MIDDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateExcelReport(final Employee employee) {
|
||||
try {
|
||||
byte[] excelContent = reportService.generateExcelReport(employee);
|
||||
StreamResource resource = new StreamResource(
|
||||
employee.getFirstName() + "_" + employee.getLastName() + "_report.xlsx",
|
||||
() -> new ByteArrayInputStream(excelContent)
|
||||
);
|
||||
Anchor downloadLink = new Anchor(resource, "Descargar Reporte Excel");
|
||||
downloadLink.getElement().setAttribute("download", true);
|
||||
add(downloadLink);
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error al generar el reporte Excel", 3000, Notification.Position.MIDDLE);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,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
|
||||
);
|
||||
}
|
||||
}
|
@ -1,206 +0,0 @@
|
||||
package com.primefactorsolutions.views.employee;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.Constants;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.html.Anchor;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
import com.vaadin.flow.component.grid.GridSortOrder;
|
||||
import com.vaadin.flow.data.provider.SortDirection;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Employees")
|
||||
@Route(value = "/employees", layout = MainLayout.class)
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@Slf4j
|
||||
public class EmployeesListView extends BaseView {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
private final PagingGrid<Employee> employeePagingGrid = new PagingGrid<>(Employee.class);
|
||||
|
||||
public EmployeesListView(final AuthenticationContext authenticationContext, final EmployeeService employeeService) {
|
||||
super(authenticationContext);
|
||||
this.employeeService = employeeService;
|
||||
setupView();
|
||||
refreshGrid();
|
||||
}
|
||||
|
||||
private void setupView() {
|
||||
configureTable();
|
||||
final HorizontalLayout hl = new HorizontalLayout(createAddEmployeeButton(), createExportButton());
|
||||
getCurrentPageLayout().add(hl);
|
||||
getCurrentPageLayout().add(employeePagingGrid);
|
||||
}
|
||||
|
||||
private Button createExportButton() {
|
||||
final StreamResource excelResource = new StreamResource("employees.xlsx", this::generateExcel);
|
||||
final Anchor downloadLink = new Anchor(excelResource, "Export Employees");
|
||||
downloadLink.getElement().setAttribute("download", true); // Forzar descarga
|
||||
|
||||
return new Button("Exportar como Excel", e -> getCurrentPageLayout().add(downloadLink));
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generateExcel() {
|
||||
final List<Employee> employees = employeeService.findAllEmployees();
|
||||
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
Sheet sheet = workbook.createSheet("Employees");
|
||||
Row headerRow = sheet.createRow(0);
|
||||
String[] headers = {
|
||||
"ID", "Nombres", "Apellidos", "Status", "Genero", "Fecha de Nacimiento", "Edad",
|
||||
"Ciudad y Pais de Nacimiento", "Dirección de Residencia", "Departamento y Provincia de Residencia",
|
||||
"Marital Status",
|
||||
"Numero de Hijos", "CI", "Expedido en", "Teléfono", "E-mail",
|
||||
"Teléfono Laboral", "E-mail Laboral", "Codigo de Empleado", "Cargo",
|
||||
"Equipo", "Lead/Manager", "Fecha de Ingreso", "Fecha de Retiro", "Tipo de Contrato",
|
||||
"Otro Tipo de Contrato", "Antiguedad", "Salario Total"
|
||||
};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
style.setFont(font);
|
||||
cell.setCellStyle(style);
|
||||
}
|
||||
int rowIndex = 1;
|
||||
for (Employee employee : employees) {
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
row.createCell(0).setCellValue(employee.getId().toString());
|
||||
row.createCell(1).setCellValue(employee.getFirstName());
|
||||
row.createCell(2).setCellValue(employee.getLastName());
|
||||
row.createCell(3).setCellValue(employee.getStatus().toString());
|
||||
row.createCell(4).setCellValue(employee.getGender() != null ? employee.getGender().toString() : "");
|
||||
row.createCell(5).setCellValue(employee.getBirthday() != null ? employee.getBirthday()
|
||||
.toString() : "");
|
||||
row.createCell(6).setCellValue(employee.getAge() != null ? employee.getAge() : "");
|
||||
row.createCell(7).setCellValue(employee.getBirthCity() != null ? employee.getBirthCity() : "");
|
||||
row.createCell(8).setCellValue(employee.getResidenceAddress() != null ? employee
|
||||
.getResidenceAddress() : "");
|
||||
row.createCell(9).setCellValue(employee.getLocalAddress() != null ? employee.getLocalAddress() : "");
|
||||
row.createCell(10).setCellValue(employee.getMaritalStatus() != null ? employee.getMaritalStatus()
|
||||
.toString() : "");
|
||||
row.createCell(11).setCellValue(employee.getNumberOfChildren() != null ? employee
|
||||
.getNumberOfChildren() : "");
|
||||
row.createCell(12).setCellValue(employee.getCi() != null ? employee.getCi() : "");
|
||||
row.createCell(13).setCellValue(employee.getIssuedIn() != null ? employee.getIssuedIn() : "");
|
||||
row.createCell(14).setCellValue(employee.getPhoneNumber() != null ? employee.getPhoneNumber() : "");
|
||||
row.createCell(15).setCellValue(employee.getPersonalEmail() != null ? employee
|
||||
.getPersonalEmail() : "");
|
||||
row.createCell(16).setCellValue(employee.getPhoneNumberProfessional() != null ? employee
|
||||
.getPhoneNumberProfessional() : "");
|
||||
row.createCell(17).setCellValue(employee.getProfessionalEmail() != null ? employee
|
||||
.getProfessionalEmail() : "");
|
||||
row.createCell(18).setCellValue(employee.getCod() != null ? employee.getCod() : "");
|
||||
row.createCell(19).setCellValue(employee.getPosition() != null ? employee.getPosition() : "");
|
||||
row.createCell(20).setCellValue(employee.getTeam() != null ? employee.getTeam().getName() : "");
|
||||
row.createCell(21).setCellValue(employee.getLeadManager() != null ? employee.getLeadManager() : "");
|
||||
row.createCell(22).setCellValue(employee.getDateOfEntry() != null ? employee.getDateOfEntry()
|
||||
.toString() : "");
|
||||
row.createCell(23).setCellValue(employee.getDateOfExit() != null ? employee.getDateOfExit()
|
||||
.toString() : "");
|
||||
row.createCell(24).setCellValue(employee.getContractType() != null ? employee.getContractType()
|
||||
.toString() : "");
|
||||
row.createCell(25).setCellValue(employee.getCustomContractType() != null
|
||||
? employee.getCustomContractType()
|
||||
: "");
|
||||
row.createCell(26).setCellValue(employee.getSeniority() != null ? employee.getSeniority() : "");
|
||||
row.createCell(27).setCellValue(employee.getSalaryTotal() != null
|
||||
? employee.getSalaryTotal().toString()
|
||||
: "");
|
||||
}
|
||||
workbook.write(out);
|
||||
return new ByteArrayInputStream(out.toByteArray());
|
||||
} catch (IOException e) {
|
||||
log.error("Error generating excel", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void configureTable() {
|
||||
employeePagingGrid.setColumns("username", "firstName", "lastName", "status");
|
||||
employeePagingGrid.addComponentColumn(employee -> MenuBarUtils.menuBar(
|
||||
Pair.of("View", __ -> navigateToEmployeeView(employee)),
|
||||
Pair.of("Edit", __ -> navigateToEditView(employee))));
|
||||
setupPagingGrid();
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
|
||||
final Button button = new Button(label);
|
||||
|
||||
if (isPrimary) {
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
}
|
||||
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddEmployeeButton() {
|
||||
return createButton("Add Employee", this::navigateToAddEmployeeView, true);
|
||||
}
|
||||
|
||||
private void navigateToEditView(final Employee employee) {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/edit"));
|
||||
}
|
||||
|
||||
private void navigateToEmployeeView(final Employee employee) {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/view"));
|
||||
}
|
||||
|
||||
private void navigateToAddEmployeeView() {
|
||||
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, "new"));
|
||||
}
|
||||
|
||||
private void setupPagingGrid() {
|
||||
employeePagingGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
employeePagingGrid.setPageSize(Constants.PAGE_SIZE);
|
||||
}
|
||||
|
||||
private void refreshGrid() {
|
||||
employeePagingGrid.setPagingDataProvider((page, pageSize) -> fetchEmployees((int) page, pageSize));
|
||||
}
|
||||
|
||||
private List<Employee> fetchEmployees(final int page, final int pageSize) {
|
||||
int start = page * pageSize;
|
||||
if (hasSortOrder()) {
|
||||
return fetchSortedEmployees(start, pageSize);
|
||||
}
|
||||
return employeeService.findEmployees(start, pageSize);
|
||||
}
|
||||
|
||||
private boolean hasSortOrder() {
|
||||
return !employeePagingGrid.getSortOrder().isEmpty();
|
||||
}
|
||||
|
||||
private List<Employee> fetchSortedEmployees(final int start, final int pageSize) {
|
||||
final GridSortOrder<Employee> sortOrder = employeePagingGrid.getSortOrder().getFirst();
|
||||
|
||||
return employeeService.findEmployees(start, pageSize,
|
||||
sortOrder.getSorted().getKey(),
|
||||
sortOrder.getDirection() == SortDirection.ASCENDING);
|
||||
}
|
||||
}
|
@ -1,525 +0,0 @@
|
||||
package com.primefactorsolutions.views.timeoff;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.service.TimeOffService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.primefactorsolutions.views.util.ComponentUtils.withFullWidth;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Request")
|
||||
@Route(value = "/requests/:requestId?/:action?", layout = MainLayout.class)
|
||||
public class TimeOffRequestView extends BaseEntityForm<TimeOffRequest> implements HasUrlParameter<String> {
|
||||
|
||||
private final ComboBox<TimeOffRequestType> category = withFullWidth(new ComboBox<>("Categoría"));
|
||||
private final ComboBox<TimeOffRequestStatus> state = withFullWidth(new ComboBox<>("Estado de la solicitud"));
|
||||
private final DatePicker expiration = withFullWidth(new DatePicker("Vencimiento"));
|
||||
private final DatePicker startDate = withFullWidth(new DatePicker("Fecha de inicio"));
|
||||
private final DatePicker endDate = withFullWidth(new DatePicker("Fecha de fin"));
|
||||
private final NumberField daysToBeTake = withFullWidth(new NumberField("Días a tomar"));
|
||||
private final NumberField daysBalance = withFullWidth(new NumberField("Días disponibles"));
|
||||
|
||||
private final TimeOffService timeOffService;
|
||||
private final TimeOffRequestService timeOffRequestService;
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
private Employee employee;
|
||||
|
||||
public TimeOffRequestView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffRequestService timeOffRequestService,
|
||||
final TimeOffService timeOffService,
|
||||
final EmployeeService employeeService) {
|
||||
super(authenticationContext, TimeOffRequest.class);
|
||||
this.timeOffService = timeOffService;
|
||||
this.timeOffRequestService = timeOffRequestService;
|
||||
this.employeeService = employeeService;
|
||||
state.setItems(List.of(TimeOffRequestStatus.values()));
|
||||
|
||||
this.setSavedHandler(this::saveRequest);
|
||||
|
||||
initView();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
category.addValueChangeListener(event -> {
|
||||
onCategoryChange(event.getValue());
|
||||
handleCategorySelection(event.getValue());
|
||||
});
|
||||
startDate.addValueChangeListener(event -> {
|
||||
LocalDate selectedDate = event.getValue();
|
||||
if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6
|
||||
|| selectedDate.getDayOfWeek().getValue() == 7)) {
|
||||
startDate.setValue(selectedDate.minusDays(1));
|
||||
}
|
||||
updateDatePickerMinValues();
|
||||
});
|
||||
endDate.addValueChangeListener(event -> {
|
||||
if (endDate.getValue() != null) {
|
||||
endDate.setMin(endDate.getValue());
|
||||
}
|
||||
final LocalDate selectedDate = event.getValue();
|
||||
|
||||
if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6
|
||||
|| selectedDate.getDayOfWeek().getValue() == 7)) {
|
||||
endDate.setValue(selectedDate.minusDays(1));
|
||||
}
|
||||
calculateDays();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
final String requestIdString = params.get("requestId").orElse(null);
|
||||
final UUID employeeId = getEmployeeId().get();
|
||||
employee = employeeService.getEmployee(employeeId);
|
||||
filterCategories(employee);
|
||||
|
||||
if ("new".equals(action)) {
|
||||
setEntityWithEnabledSave(new TimeOffRequest());
|
||||
} else {
|
||||
assert requestIdString != null;
|
||||
UUID requestId = UUID.fromString(requestIdString);
|
||||
TimeOffRequest timeOffRequest = timeOffRequestService.findTimeOffRequest(requestId);
|
||||
|
||||
if ("edit".equals(action)) {
|
||||
setEntityWithEnabledSave(timeOffRequest);
|
||||
setFieldsReadOnly(false);
|
||||
} else if ("view".equals(action)) {
|
||||
setEntity(timeOffRequest);
|
||||
setFieldsReadOnly(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
category,
|
||||
state,
|
||||
expiration,
|
||||
startDate,
|
||||
endDate,
|
||||
daysBalance,
|
||||
daysToBeTake
|
||||
);
|
||||
}
|
||||
|
||||
private void filterCategories(final Employee employee) {
|
||||
category.clear();
|
||||
List<TimeOffRequest> employeeRequests = timeOffRequestService.findRequestsByEmployeeId(employee.getId());
|
||||
List<TimeOffRequestType> allCategories = Arrays.asList(TimeOffRequestType.values());
|
||||
List<TimeOffRequestType> availableCategories = allCategories.stream()
|
||||
.filter(category -> isCategoryAvailable(employeeRequests, category))
|
||||
.filter(category -> isCategoryAllowedByGender(category, employee.getGender()))
|
||||
.filter(category -> shouldIncludeVacationGestionActual(employeeRequests, category))
|
||||
.filter(category -> shouldIncludeVacationGestionAnterior(employeeRequests, category))
|
||||
.toList();
|
||||
|
||||
category.setItems(availableCategories);
|
||||
}
|
||||
|
||||
private boolean shouldIncludeVacationGestionActual(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category != TimeOffRequestType.VACACION_GESTION_ACTUAL) {
|
||||
return true;
|
||||
}
|
||||
return employeeRequests.stream()
|
||||
.anyMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
&& request.getDaysBalance() == 0
|
||||
&& request.getState() == TimeOffRequestStatus.TOMADO);
|
||||
}
|
||||
|
||||
private boolean shouldIncludeVacationGestionAnterior(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category != TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
return true;
|
||||
}
|
||||
return employeeRequests.stream()
|
||||
.noneMatch(request -> request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
&& request.getDaysBalance() == 0);
|
||||
}
|
||||
|
||||
private void onCategoryChange(final TimeOffRequestType selectedCategory) {
|
||||
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
startDate.setReadOnly(false);
|
||||
endDate.setReadOnly(false);
|
||||
} else {
|
||||
startDate.setReadOnly(false);
|
||||
endDate.setReadOnly(true);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCategoryAvailable(final List<TimeOffRequest> employeeRequests,
|
||||
final TimeOffRequestType category) {
|
||||
if (category == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<TimeOffRequest> requestsByCategory = employeeRequests.stream()
|
||||
.filter(request -> request.getCategory() == category)
|
||||
.toList();
|
||||
|
||||
if (requestsByCategory.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TimeOffRequest latestRequest = requestsByCategory.stream()
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.orElse(null);
|
||||
|
||||
boolean isSpecialCategory = category == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| category == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| category == TimeOffRequestType.VACACION_GESTION_ANTERIOR;
|
||||
|
||||
if (isSpecialCategory) {
|
||||
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getDaysBalance() > 0)
|
||||
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO
|
||||
|| (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getDaysBalance() == 0
|
||||
&& latestRequest.getExpiration().isBefore(LocalDate.now()));
|
||||
} else {
|
||||
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& latestRequest.getExpiration().isBefore(LocalDate.now()))
|
||||
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCategoryAllowedByGender(final TimeOffRequestType category, final Employee.Gender gender) {
|
||||
if (gender == Employee.Gender.MALE) {
|
||||
return category != TimeOffRequestType.MATERNIDAD
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL
|
||||
&& category != TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL;
|
||||
} else {
|
||||
return category != TimeOffRequestType.DIA_DEL_PADRE
|
||||
&& category != TimeOffRequestType.PATERNIDAD;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCategorySelection(final TimeOffRequestType selectedCategory) {
|
||||
if (selectedCategory != null) {
|
||||
updateAvailableDays(selectedCategory);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAvailableDays(final TimeOffRequestType selectedCategory) {
|
||||
final TimeOff timeoff = timeOffService.findVacationByCategory(selectedCategory);
|
||||
final List<TimeOffRequest> requests =
|
||||
timeOffRequestService.findByEmployeeAndCategory(employee.getId(), selectedCategory);
|
||||
|
||||
final TimeOffRequest requestWithBalance = requests.stream()
|
||||
.filter(request -> request.getDaysBalance() > 0
|
||||
&& request.getState() != TimeOffRequestStatus.VENCIDO
|
||||
&& request.getState() != TimeOffRequestStatus.RECHAZADO)
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.orElse(null);
|
||||
|
||||
if (requestWithBalance != null) {
|
||||
if (requestWithBalance.getState() == TimeOffRequestStatus.TOMADO
|
||||
&& requestWithBalance.getDaysBalance() > 0) {
|
||||
daysBalance.setValue(requestWithBalance.getDaysBalance());
|
||||
} else {
|
||||
daysBalance.setValue(timeoff.getDuration());
|
||||
}
|
||||
} else if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
final LocalDate dateOfEntry = employee.getDateOfEntry();
|
||||
final LocalDate currentDate = LocalDate.now();
|
||||
long yearsOfService = ChronoUnit.YEARS.between(dateOfEntry, currentDate);
|
||||
|
||||
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
|
||||
yearsOfService -= 1;
|
||||
}
|
||||
|
||||
if (yearsOfService > 10) {
|
||||
daysBalance.setValue(30.0);
|
||||
} else if (yearsOfService > 5) {
|
||||
daysBalance.setValue(20.0);
|
||||
} else if (yearsOfService > 1) {
|
||||
daysBalance.setValue(15.0);
|
||||
} else {
|
||||
daysBalance.setValue(0.0);
|
||||
}
|
||||
} else {
|
||||
daysBalance.setValue(timeoff.getDuration());
|
||||
}
|
||||
|
||||
setDatePickerLimits(timeoff);
|
||||
}
|
||||
|
||||
private void setDatePickerLimits(final TimeOff timeoff) {
|
||||
LocalDate startDateValue;
|
||||
LocalDate endDateValue = null;
|
||||
|
||||
final UUID employeeId = employee.getId();
|
||||
final List<TimeOffRequest> previousRequests
|
||||
= timeOffRequestService.findByEmployeeAndCategory(employeeId, timeoff.getCategory());
|
||||
|
||||
final int startYear = calculateStartYear(previousRequests);
|
||||
|
||||
startDateValue = determineStartDate(timeoff, startYear);
|
||||
|
||||
if (startDateValue.isBefore(LocalDate.now())) {
|
||||
startDateValue = determineStartDate(timeoff, startYear + 1);
|
||||
}
|
||||
|
||||
if (startDateValue != null) {
|
||||
if (timeoff.getExpiration() != null) {
|
||||
endDateValue = startDateValue.plusDays(timeoff.getExpiration().intValue() - 1);
|
||||
} else {
|
||||
endDateValue = LocalDate.of(startDateValue.getYear(), 12, 31);
|
||||
}
|
||||
} else {
|
||||
startDateValue = LocalDate.now();
|
||||
}
|
||||
|
||||
setPickerValues(timeoff, startDateValue);
|
||||
setPickerLimits(startDateValue, endDateValue);
|
||||
}
|
||||
|
||||
private int calculateStartYear(final List<TimeOffRequest> previousRequests) {
|
||||
if (previousRequests.isEmpty()) {
|
||||
return LocalDate.now().getYear();
|
||||
}
|
||||
|
||||
int lastRequestYear = previousRequests.stream()
|
||||
.max(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.map(request -> request.getStartDate().getYear())
|
||||
.orElse(LocalDate.now().getYear());
|
||||
|
||||
if (previousRequests.getLast().getState() != TimeOffRequestStatus.RECHAZADO) {
|
||||
lastRequestYear = lastRequestYear + 1;
|
||||
}
|
||||
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
return Math.max(lastRequestYear, currentYear);
|
||||
}
|
||||
|
||||
private LocalDate determineStartDate(final TimeOff timeoff, final int startYear) {
|
||||
if (timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() != null) {
|
||||
return LocalDate.of(startYear, employee.getBirthday().getMonth(), employee.getBirthday().getDayOfMonth());
|
||||
}
|
||||
|
||||
if (timeoff.getDate() != null) {
|
||||
return LocalDate.of(startYear, timeoff.getDate().getMonthValue(), timeoff.getDate().getDayOfMonth());
|
||||
}
|
||||
|
||||
return LocalDate.now();
|
||||
}
|
||||
|
||||
private void setPickerValues(final TimeOff timeoff, final LocalDate startDateValue) {
|
||||
startDate.setValue(startDateValue);
|
||||
|
||||
if ((timeoff.getDuration() != null && timeoff.getDuration() == 0.5)
|
||||
|| timeoff.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| timeoff.getCategory() == TimeOffRequestType.CUMPLEAÑOS) {
|
||||
|
||||
endDate.setValue(startDateValue);
|
||||
} else {
|
||||
int durationDays = (timeoff.getDuration() != null ? timeoff.getDuration().intValue() - 1 : 0);
|
||||
endDate.setValue(startDateValue.plusDays(durationDays));
|
||||
}
|
||||
}
|
||||
|
||||
private void setPickerLimits(final LocalDate startDateValue, final LocalDate endDateValue) {
|
||||
startDate.setMin(startDateValue);
|
||||
startDate.setMax(endDateValue);
|
||||
endDate.setMin(startDateValue);
|
||||
endDate.setMax(endDateValue);
|
||||
}
|
||||
|
||||
private void updateDatePickerMinValues() {
|
||||
LocalDate startDateValue = startDate.getValue();
|
||||
if (daysBalance.getValue() != null) {
|
||||
if (daysBalance.getValue() == 0.5) {
|
||||
endDate.setValue(startDateValue.plusDays(0));
|
||||
} else {
|
||||
endDate.setValue(startDateValue.plusDays(daysBalance.getValue().intValue() - 1));
|
||||
}
|
||||
|
||||
calculateDays();
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateDays() {
|
||||
LocalDate startDateValue = startDate.getValue();
|
||||
LocalDate endDateValue = endDate.getValue();
|
||||
Double availableDaysValue = daysBalance.getValue();
|
||||
|
||||
if (areDatesValid(startDateValue, endDateValue)) {
|
||||
long workDays = countWorkDaysBetween(startDateValue, endDateValue);
|
||||
|
||||
daysToBeTake.setValue((double) workDays);
|
||||
daysBalance.setValue(daysBalance.getValue() - workDays);
|
||||
|
||||
double daysToBeTaken = calculateDaysBetween(startDateValue, endDateValue);
|
||||
setDaysToBeTakenField(daysToBeTaken);
|
||||
|
||||
double balanceDays = calculateBalanceDays(availableDaysValue, daysToBeTake.getValue());
|
||||
daysBalance.setValue(balanceDays);
|
||||
|
||||
if (balanceDays < 0.0) {
|
||||
clearFields();
|
||||
}
|
||||
|
||||
if (daysToBeTake.getValue() > 10
|
||||
&& (category.getValue() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
|| category.getValue() == TimeOffRequestType.VACACION_GESTION_ACTUAL)) {
|
||||
clearFields();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearFields() {
|
||||
daysToBeTake.clear();
|
||||
daysBalance.clear();
|
||||
endDate.clear();
|
||||
}
|
||||
|
||||
private boolean areDatesValid(final LocalDate startDate, final LocalDate endDate) {
|
||||
return startDate != null && endDate != null;
|
||||
}
|
||||
|
||||
private long countWorkDaysBetween(final LocalDate startDate, final LocalDate endDate) {
|
||||
return startDate.datesUntil(endDate.plusDays(1))
|
||||
.filter(date -> date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY)
|
||||
.count();
|
||||
}
|
||||
|
||||
private double calculateDaysBetween(final LocalDate startDate, final LocalDate endDate) {
|
||||
return startDate.datesUntil(endDate.plusDays(1))
|
||||
.filter(date -> {
|
||||
DayOfWeek day = date.getDayOfWeek();
|
||||
return day != DayOfWeek.SATURDAY && day != DayOfWeek.SUNDAY;
|
||||
})
|
||||
.count();
|
||||
}
|
||||
|
||||
private void setDaysToBeTakenField(final double daysToBeTaken) {
|
||||
final TimeOffRequest timeOffRequest = getEntity();
|
||||
|
||||
if (timeOffRequest.getCategory() == TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.CUMPLEAÑOS
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.DIA_DEL_PADRE
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL
|
||||
|| timeOffRequest.getCategory() == TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL) {
|
||||
daysToBeTake.setValue(0.5);
|
||||
} else {
|
||||
daysToBeTake.setValue(daysToBeTaken);
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateBalanceDays(final double availableDays, final double daysToBeTaken) {
|
||||
return availableDays - daysToBeTaken;
|
||||
}
|
||||
|
||||
private void setFieldsReadOnly(final boolean option) {
|
||||
state.setReadOnly(option);
|
||||
expiration.setReadOnly(option);
|
||||
startDate.setReadOnly(option);
|
||||
endDate.setReadOnly(option);
|
||||
daysBalance.setReadOnly(option);
|
||||
daysToBeTake.setReadOnly(option);
|
||||
}
|
||||
|
||||
private void saveRequest(final TimeOffRequest request) {
|
||||
if (!isFormValid()) {
|
||||
Notification.show("Por favor, complete los campos antes de guardar");
|
||||
return;
|
||||
}
|
||||
|
||||
prepareRequest(request);
|
||||
|
||||
if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL) {
|
||||
handleVacationRequest(request);
|
||||
} else {
|
||||
handleExistingRequests(request);
|
||||
}
|
||||
|
||||
long differentDays = ChronoUnit.DAYS.between(LocalDate.now(), request.getStartDate());
|
||||
if (differentDays >= -15 && differentDays <= 90) {
|
||||
timeOffRequestService.saveTimeOffRequest(request);
|
||||
Notification.show("Solicitud guardada correctamente.");
|
||||
closeForm();
|
||||
} else {
|
||||
Notification.show(
|
||||
"La fecha de inicio debe encontrarse dentro del rango de 15 días a 3 meses de anticipación."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareRequest(final TimeOffRequest request) {
|
||||
request.setStartDate(startDate.getValue());
|
||||
request.setAvailableDays(daysBalance.getValue());
|
||||
request.setExpiration(endDate.getValue());
|
||||
request.setState(TimeOffRequestStatus.SOLICITADO);
|
||||
}
|
||||
|
||||
private void handleExistingRequests(final TimeOffRequest request) {
|
||||
final List<TimeOffRequest> existingRequests =
|
||||
timeOffRequestService.findByEmployeeAndCategory(employee.getId(), request.getCategory());
|
||||
|
||||
int maxRequests = request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD
|
||||
&& !request.getCategory().name().startsWith("VACACION")
|
||||
&& request.getCategory() != TimeOffRequestType.CUMPLEAÑOS
|
||||
? 2 : 1;
|
||||
|
||||
if (existingRequests.size() >= maxRequests) {
|
||||
existingRequests.stream()
|
||||
.min(Comparator.comparing(TimeOffRequest::getStartDate))
|
||||
.ifPresent(oldestRequest -> timeOffRequestService.deleteTimeOffRequest(oldestRequest.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleVacationRequest(final TimeOffRequest request) {
|
||||
final List<TimeOffRequest> existingRequests = timeOffRequestService.findByEmployeeAndCategory(
|
||||
employee.getId(),
|
||||
TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
);
|
||||
if (!existingRequests.isEmpty()) {
|
||||
TimeOffRequest existingRequest = existingRequests.getFirst();
|
||||
existingRequest.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR);
|
||||
timeOffRequestService.saveTimeOffRequest(existingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffRequestsListView.class));
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return !state.isEmpty()
|
||||
&& expiration.getValue() != null
|
||||
&& startDate.getValue() != null
|
||||
&& endDate.getValue() != null
|
||||
&& daysBalance.getValue() != null
|
||||
&& daysToBeTake.getValue() != null;
|
||||
}
|
||||
}
|
@ -1,343 +0,0 @@
|
||||
package com.primefactorsolutions.views.timeoff;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.contextmenu.MenuItem;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.menubar.MenuBar;
|
||||
import com.vaadin.flow.component.menubar.MenuBarVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamRegistration;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Time-Off Requests")
|
||||
@Route(value = "/time-off/requests", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class TimeOffRequestsListView extends BaseView {
|
||||
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final PagingGrid<TimeOffRequest> requestsGrid = new PagingGrid<>();
|
||||
private final ComboBox<Employee> employeeFilter = new ComboBox<>("Empleado");
|
||||
private final ComboBox<Team> teamFilter = new ComboBox<>("Equipo");
|
||||
private final ComboBox<TimeOffRequestType> categoryFilter = new ComboBox<>("Categoría");
|
||||
private final ComboBox<TimeOffRequestStatus> stateFilter = new ComboBox<>("Estado");
|
||||
|
||||
public TimeOffRequestsListView(final AuthenticationContext authenticationContext,
|
||||
final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext);
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
initializeView();
|
||||
refreshGeneralRequestsGrid(employeeFilter.getValue(), teamFilter.getValue(), categoryFilter.getValue(),
|
||||
stateFilter.getValue());
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
final Button newRequestButton = new Button("Crear nueva peticion", event -> navigateToAddNew());
|
||||
newRequestButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
|
||||
final HorizontalLayout hl = new HorizontalLayout(newRequestButton);
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final Button downloadReportButton = new Button("Descargar reporte de rechazos", event -> downloadReport());
|
||||
hl.add(downloadReportButton);
|
||||
}
|
||||
|
||||
getCurrentPageLayout().add(hl);
|
||||
setupFilters();
|
||||
setupRequestsGrid();
|
||||
}
|
||||
|
||||
private void navigateToAddNew() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimeOffRequestView.class, "new"));
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createEmployeeFilter());
|
||||
hl.add(createTeamFilter());
|
||||
hl.add(createCategoryFilter());
|
||||
hl.add(createStateFilter());
|
||||
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private void setupRequestsGrid() {
|
||||
requestsGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
|
||||
requestsGrid.addColumn(this::getTeamName).setHeader("Equipo");
|
||||
requestsGrid.addColumn(this::getCategory).setHeader("Categoría");
|
||||
requestsGrid.addColumn(this::getDates).setHeader("Dias");
|
||||
requestsGrid.addColumn(this::getState).setHeader("Estado");
|
||||
requestsGrid.addColumn(this::getUpdated).setHeader("Fecha Actualizacion");
|
||||
requestsGrid.addComponentColumn((ValueProvider<TimeOffRequest, Component>) timeOffRequest -> {
|
||||
final MenuBar menuBar = new MenuBar();
|
||||
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||
final MenuItem approveItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.CHECK, "Aprobar");
|
||||
approveItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
|
||||
actionForRequest(timeOffRequest.getId(), TimeOffRequestStatus.APROBADO));
|
||||
final MenuItem rejectItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.BAN, "Rechazar");
|
||||
rejectItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
|
||||
actionForRequest(timeOffRequest.getId(), TimeOffRequestStatus.RECHAZADO));
|
||||
return menuBar;
|
||||
});
|
||||
|
||||
requestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
requestsGrid.setPageSize(PAGE_SIZE);
|
||||
|
||||
getCurrentPageLayout().add(requestsGrid);
|
||||
}
|
||||
|
||||
private void actionForRequest(final UUID selectedRequestId, final TimeOffRequestStatus status) {
|
||||
TimeOffRequest request = requestService.findTimeOffRequest(selectedRequestId);
|
||||
request.setState(status);
|
||||
requestService.saveTimeOffRequest(request);
|
||||
refreshGeneralRequestsGrid(null, null, null, null);
|
||||
}
|
||||
|
||||
private void refreshGeneralRequestsGrid(final Employee employee,
|
||||
final Team team,
|
||||
final TimeOffRequestType category,
|
||||
final TimeOffRequestStatus timeOffRequestStatus) {
|
||||
requestsGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
int start = (int) (page * requestsGrid.getPageSize());
|
||||
return fetchFilteredRequests(start, pageSize, employee, team, category, timeOffRequestStatus);
|
||||
});
|
||||
requestsGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<TimeOffRequest> fetchFilteredRequests(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final TimeOffRequestType category,
|
||||
final TimeOffRequestStatus timeOffRequestStatus) {
|
||||
List<TimeOffRequest> filteredRequests = requestService.findAllTimeOffRequests();
|
||||
|
||||
if (employee != null) {
|
||||
filteredRequests = filteredRequests.stream()
|
||||
.filter(emp -> emp.getEmployee().getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null) {
|
||||
filteredRequests = filteredRequests.stream()
|
||||
.filter(emp -> emp.getEmployee().getTeam() != null
|
||||
&& emp.getEmployee().getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (category != null) {
|
||||
filteredRequests = filteredRequests.stream()
|
||||
.filter(emp -> emp.getCategory().equals(category))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (timeOffRequestStatus != null) {
|
||||
filteredRequests = filteredRequests.stream()
|
||||
.filter(emp -> emp.getState().equals(timeOffRequestStatus))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredRequests.size());
|
||||
return filteredRequests.subList(start, end);
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final TimeOffRequest request) {
|
||||
Employee employee = request.getEmployee();
|
||||
return getEmployeeFullNameLabel(employee);
|
||||
}
|
||||
|
||||
private String getEmployeeFullNameLabel(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private String getTeamName(final TimeOffRequest request) {
|
||||
Team team = request.getEmployee().getTeam();
|
||||
return team != null ? team.getName() : "Sin asignar";
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return team.getName();
|
||||
}
|
||||
|
||||
private String getCategory(final TimeOffRequest request) {
|
||||
return String.valueOf(request.getCategory());
|
||||
}
|
||||
|
||||
private String getDates(final TimeOffRequest request) {
|
||||
return String.format("de %s a %s", request.getStartDate(), request.getEndDate());
|
||||
}
|
||||
|
||||
private String getState(final TimeOffRequest request) {
|
||||
return request.getState().name();
|
||||
}
|
||||
|
||||
private String getUpdated(final TimeOffRequest request) {
|
||||
return DateTimeFormatter.ofPattern("yyyy/dd/MM hh:mm")
|
||||
.format(request.getUpdated().atOffset(ZoneOffset.ofHours(-5)));
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final List<Employee> employees = employeeService.findAllEmployees();
|
||||
employeeFilter.setItems(employees);
|
||||
} else {
|
||||
final Employee employee = employeeService.getEmployee(getEmployeeId().get());
|
||||
employeeFilter.setItems(List.of(employee));
|
||||
employeeFilter.setValue(employee);
|
||||
employeeFilter.setReadOnly(true);
|
||||
}
|
||||
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullNameLabel);
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestsGrid(
|
||||
event.getValue(),
|
||||
teamFilter.getValue(),
|
||||
categoryFilter.getValue(),
|
||||
stateFilter.getValue()
|
||||
)
|
||||
);
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
final List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestsGrid(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue(),
|
||||
categoryFilter.getValue(),
|
||||
stateFilter.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private ComboBox<TimeOffRequestType> createCategoryFilter() {
|
||||
categoryFilter.setPlaceholder("Seleccionar ...");
|
||||
categoryFilter.setClearButtonVisible(true);
|
||||
categoryFilter.setItems(TimeOffRequestType.values());
|
||||
categoryFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestsGrid(
|
||||
employeeFilter.getValue(),
|
||||
teamFilter.getValue(),
|
||||
event.getValue(),
|
||||
stateFilter.getValue()
|
||||
)
|
||||
);
|
||||
return categoryFilter;
|
||||
}
|
||||
|
||||
private ComboBox<TimeOffRequestStatus> createStateFilter() {
|
||||
stateFilter.setPlaceholder("Seleccionar ...");
|
||||
stateFilter.setClearButtonVisible(true);
|
||||
stateFilter.setItems(TimeOffRequestStatus.values());
|
||||
stateFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestsGrid(
|
||||
employeeFilter.getValue(),
|
||||
teamFilter.getValue(),
|
||||
categoryFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
|
||||
return stateFilter;
|
||||
}
|
||||
|
||||
private void downloadReport() {
|
||||
StreamResource resource = generateGeneralVacationReport();
|
||||
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
|
||||
}
|
||||
|
||||
private StreamResource generateGeneralVacationReport() {
|
||||
List<TimeOffRequest> requests = requestService.findAllTimeOffRequests().stream()
|
||||
.filter(request -> request.getState() == TimeOffRequestStatus.RECHAZADO)
|
||||
.collect(Collectors.toList());
|
||||
ByteArrayInputStream excelStream = generateExcelReport(requests);
|
||||
return new StreamResource("reporte_de_solicitudes_rechazadas_" + LocalDate.now() + ".xlsx",
|
||||
() -> excelStream);
|
||||
}
|
||||
|
||||
private void openDocumentStream(final StreamResource resource, final UI ui) {
|
||||
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generateExcelReport(final List<TimeOffRequest> requests) {
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
Sheet sheet = workbook.createSheet("REPORTE_DE_SOLICITUDES_DE_VACACIONES_RECHAZADAS");
|
||||
Row headerRow = sheet.createRow(0);
|
||||
|
||||
String[] headers = {"Empleado", "Categoria", "Observaciones"};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
}
|
||||
|
||||
int rowIndex = 1;
|
||||
for (TimeOffRequest request : requests) {
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
row.createCell(0).setCellValue(getEmployeeFullName(request));
|
||||
row.createCell(1).setCellValue(getCategory(request));
|
||||
}
|
||||
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
workbook.write(out);
|
||||
return new ByteArrayInputStream(out.toByteArray());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error al generar el archivo Excel", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,809 +0,0 @@
|
||||
package com.primefactorsolutions.views.timeoff;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.service.TimeOffRequestService;
|
||||
import com.primefactorsolutions.service.TimeOffService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamRegistration;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
import java.time.Year;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Requests")
|
||||
@Route(value = "/time-off/summary", layout = MainLayout.class)
|
||||
@PermitAll
|
||||
public class TimeOffSummaryListView extends BaseView {
|
||||
|
||||
private static final Map<UUID, TimeOffSummary> SUMMARY_MAP = new ConcurrentHashMap<>();
|
||||
private final TimeOffRequestService requestService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final TimeOffService timeOffService;
|
||||
private final PagingGrid<Employee> requestGrid = new PagingGrid<>();
|
||||
private final ComboBox<Employee> employeeFilter = new ComboBox<>("Empleado");;
|
||||
private final ComboBox<Team> teamFilter = new ComboBox<>("Equipo");
|
||||
|
||||
public TimeOffSummaryListView(
|
||||
final AuthenticationContext authenticationContext,
|
||||
final TimeOffRequestService requestService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService,
|
||||
final TimeOffService timeOffService) {
|
||||
super(authenticationContext);
|
||||
this.requestService = requestService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
this.timeOffService = timeOffService;
|
||||
initializeView();
|
||||
refreshGeneralRequestGrid(employeeFilter.getValue(), teamFilter.getValue());
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
requestService.updateRequestStatuses();
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final Button downloadButton = new Button("Descargar reporte", event -> downloadReport());
|
||||
getCurrentPageLayout().add(downloadButton);
|
||||
}
|
||||
|
||||
setupFilters();
|
||||
setupRequestGrid();
|
||||
getCurrentPageLayout().add(requestGrid);
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createEmployeeFilter());
|
||||
hl.add(createTeamFilter());
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private void setupRequestGrid() {
|
||||
requestGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
|
||||
requestGrid.addColumn(this::getTeamName).setHeader("Equipo");
|
||||
requestGrid.addColumn(this::getRemainingHolidays).setHeader("Remaining holiday");
|
||||
requestGrid.addColumn(this::getRemainingPersonal).setHeader("Remaining personal");
|
||||
requestGrid.addColumn(this::getRemainingVacation).setHeader("Remaining vacation");
|
||||
requestGrid.addColumn(this::getRemainingTotal).setHeader("Remaining total");
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
requestGrid.addComponentColumn(employee -> MenuBarUtils.menuBar(
|
||||
Pair.of("Download", __ -> downloadEmployeeReport(employee.getId()))));
|
||||
}
|
||||
|
||||
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
requestGrid.setPageSize(PAGE_SIZE);
|
||||
}
|
||||
|
||||
private Double getRemainingHolidays(final Employee employee) {
|
||||
final TimeOffSummary summary = SUMMARY_MAP.computeIfAbsent(employee.getId(), __ -> getTimeOffSummary(employee));
|
||||
|
||||
return summary.remainingHolidayDays;
|
||||
}
|
||||
|
||||
private Double getRemainingPersonal(final Employee employee) {
|
||||
final TimeOffSummary summary = SUMMARY_MAP.computeIfAbsent(employee.getId(), __ -> getTimeOffSummary(employee));
|
||||
|
||||
return summary.remainingPersonalDays;
|
||||
}
|
||||
|
||||
private Double getRemainingVacation(final Employee employee) {
|
||||
final TimeOffSummary summary = SUMMARY_MAP.computeIfAbsent(employee.getId(), __ -> getTimeOffSummary(employee));
|
||||
|
||||
return summary.remainingVacationDays;
|
||||
}
|
||||
|
||||
private Double getRemainingTotal(final Employee employee) {
|
||||
final TimeOffSummary summary = SUMMARY_MAP.computeIfAbsent(employee.getId(), __ -> getTimeOffSummary(employee));
|
||||
|
||||
return summary.getTotalRemaining();
|
||||
}
|
||||
|
||||
private void refreshGeneralRequestGrid(final Employee employee,
|
||||
final Team team) {
|
||||
requestGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
int start = (int) (page * requestGrid.getPageSize());
|
||||
return fetchFilteredEmployees(start, pageSize, employee, team);
|
||||
});
|
||||
requestGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<Employee> fetchFilteredEmployees(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team) {
|
||||
List<Employee> filteredEmployees = employeeService.findAllEmployees();
|
||||
|
||||
if (employee != null) {
|
||||
filteredEmployees = filteredEmployees.stream()
|
||||
.filter(emp -> emp.getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null) {
|
||||
filteredEmployees = filteredEmployees.stream()
|
||||
.filter(emp -> emp.getTeam() != null && emp.getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredEmployees.size());
|
||||
return filteredEmployees.subList(start, end);
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private String getTeamName(final Employee employee) {
|
||||
Team team = employee.getTeam();
|
||||
return team != null ? team.getName() : "Sin asignar";
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return team.getName();
|
||||
}
|
||||
|
||||
private String getEmployeeStatus(final Employee employee) {
|
||||
Optional<TimeOffRequest> activeRequest = requestService
|
||||
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
|
||||
return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES";
|
||||
}
|
||||
|
||||
private String getGeneralTotal(final Employee employee) {
|
||||
final List<TimeOffRequest> employeeRequests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
final List<TimeOff> timeOffs = timeOffService.findVacations();
|
||||
final List<Double> vacationDays = calculateVacationDays(employee);
|
||||
double utilizedVacationCurrentDays = vacationDays.get(1);
|
||||
|
||||
final List<TimeOffRequest> vacationCurrentRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.VACACION_GESTION_ACTUAL);
|
||||
|
||||
if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) {
|
||||
utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance();
|
||||
}
|
||||
double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays);
|
||||
|
||||
double utilizedVacationPreviousDays = vacationDays.get(0);
|
||||
final List<TimeOffRequest> vacationPreviousRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.VACACION_GESTION_ANTERIOR);
|
||||
|
||||
if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) {
|
||||
utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance();
|
||||
}
|
||||
|
||||
final double totalVacationPreviousDays = vacationDays.getFirst()
|
||||
- (vacationDays.getFirst() - utilizedVacationPreviousDays);
|
||||
|
||||
|
||||
final double totalUtilized = calculateTotalUtilized(employeeRequests);
|
||||
final double totalVacations = totalVacationCurrentDays + totalVacationPreviousDays;
|
||||
final double totalAvailable = calculateTotalAvailable(timeOffs, employeeRequests, employee);
|
||||
|
||||
double generalTotal = totalAvailable + totalVacations - totalUtilized;
|
||||
|
||||
if (employee.getDateOfExit() != null
|
||||
&& (employee.getDateOfExit().isBefore(LocalDate.now())
|
||||
|| employee.getDateOfExit().isEqual(LocalDate.now()))) {
|
||||
generalTotal = 0;
|
||||
}
|
||||
|
||||
return String.valueOf(generalTotal);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getExcludedCategories() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.MATERNIDAD,
|
||||
TimeOffRequestType.PATERNIDAD,
|
||||
TimeOffRequestType.MATRIMONIO,
|
||||
TimeOffRequestType.DUELO_1ER_GRADO,
|
||||
TimeOffRequestType.DUELO_2ER_GRADO,
|
||||
TimeOffRequestType.DIA_DEL_PADRE,
|
||||
TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getGenderSpecificExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
|
||||
);
|
||||
}
|
||||
|
||||
private double calculateTotalUtilized(final List<TimeOffRequest> employeeRequests) {
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
return employeeRequests.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(request -> request.getState() == TimeOffRequestStatus.APROBADO
|
||||
|| request.getState() == TimeOffRequestStatus.TOMADO)
|
||||
.filter(request -> request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ACTUAL)
|
||||
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR)
|
||||
.filter(request -> request.getStartDate() != null && (
|
||||
request.getStartDate().getYear() == currentYear
|
||||
|| (request.getCategory().name().startsWith("VACACION")
|
||||
&& request.getStartDate().getYear() == currentYear - 1)
|
||||
))
|
||||
.mapToDouble(request -> request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0.0)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private List<Double> calculateVacationDays(final Employee employee) {
|
||||
List<Double> vacationDays = new ArrayList<>();
|
||||
|
||||
if (employee.getDateOfEntry() != null) {
|
||||
LocalDate entryDate = employee.getDateOfEntry();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
boolean hasAnniversaryPassed = entryDate.getMonthValue() < today.getMonthValue()
|
||||
|| (entryDate.getMonthValue() == today.getMonthValue() && entryDate.getDayOfMonth()
|
||||
<= today.getDayOfMonth());
|
||||
|
||||
LocalDate previousVacationYearDate;
|
||||
LocalDate currentVacationYearDate;
|
||||
|
||||
if (hasAnniversaryPassed) {
|
||||
previousVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 1,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
currentVacationYearDate = LocalDate.of(
|
||||
today.getYear(),
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
} else {
|
||||
previousVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 2,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
currentVacationYearDate = LocalDate.of(
|
||||
today.getYear() - 1,
|
||||
entryDate.getMonth(),
|
||||
entryDate.getDayOfMonth()
|
||||
);
|
||||
}
|
||||
|
||||
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, previousVacationYearDate));
|
||||
vacationDays.add(calculateVacationDaysSinceEntry(entryDate, currentVacationYearDate));
|
||||
} else {
|
||||
vacationDays.add(0.0);
|
||||
vacationDays.add(0.0);
|
||||
}
|
||||
|
||||
return vacationDays;
|
||||
}
|
||||
|
||||
private double calculateTotalAvailable(final List<TimeOff> timeOffs, final List<TimeOffRequest> employeeRequests,
|
||||
final Employee employee) {
|
||||
Set<TimeOffRequestType> excludedCategories = getExcludedCategories();
|
||||
Set<TimeOffRequestType> genderSpecificExclusions = getGenderSpecificExclusions();
|
||||
Set<TimeOffRequestType> employeeRequestCategories = employeeRequests.stream()
|
||||
.map(TimeOffRequest::getCategory)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
double healthLicence = 2;
|
||||
List<TimeOffRequest> healthRequests = requestService
|
||||
.findByEmployeeAndCategory(employee.getId(), TimeOffRequestType.PERMISOS_DE_SALUD);
|
||||
if (healthRequests != null && !healthRequests.isEmpty()) {
|
||||
healthLicence = healthRequests.getLast().getDaysBalance();
|
||||
}
|
||||
|
||||
double totalAvailable = timeOffs.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(vacation -> vacation.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.filter(vacation -> shouldIncludeVacation(
|
||||
vacation,
|
||||
excludedCategories,
|
||||
genderSpecificExclusions,
|
||||
employee, employeeRequestCategories
|
||||
))
|
||||
.mapToDouble(vacation -> vacation.getDuration() != null ? vacation.getDuration() : 0.0)
|
||||
.sum();
|
||||
|
||||
return totalAvailable + healthLicence;
|
||||
}
|
||||
|
||||
private double calculateVacationDaysSinceEntry(final LocalDate dateOfEntry, final LocalDate date) {
|
||||
int yearsOfService = dateOfEntry != null ? Period.between(dateOfEntry, date).getYears() : 0;
|
||||
if (yearsOfService > 10) {
|
||||
return 30;
|
||||
}
|
||||
if (yearsOfService > 5) {
|
||||
return 20;
|
||||
}
|
||||
if (yearsOfService > 1) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean shouldIncludeVacation(final TimeOff timeoff,
|
||||
final Set<TimeOffRequestType> excludedCategories,
|
||||
final Set<TimeOffRequestType> genderSpecificExclusions,
|
||||
final Employee employee,
|
||||
final Set<TimeOffRequestType> employeeRequestCategories) {
|
||||
if (excludedCategories.contains(timeoff.getCategory())
|
||||
&& !employeeRequestCategories.contains(timeoff.getCategory())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isFemale(employee) || !genderSpecificExclusions.contains(timeoff.getCategory());
|
||||
}
|
||||
|
||||
private boolean isFemale(final Employee employee) {
|
||||
return employee.getGender() == Employee.Gender.FEMALE;
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
|
||||
if (isRoleAdmin()) {
|
||||
final List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
employeeFilter.setItems(employees);
|
||||
} else {
|
||||
final Employee employee = employeeService.getEmployee(getEmployeeId().get());
|
||||
employeeFilter.setItems(List.of(employee));
|
||||
employeeFilter.setValue(employee);
|
||||
employeeFilter.setReadOnly(true);
|
||||
}
|
||||
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestGrid(
|
||||
event.getValue(),
|
||||
teamFilter.getValue()
|
||||
)
|
||||
);
|
||||
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
final List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGeneralRequestGrid(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generateExcelReport(final List<Employee> employees) {
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
final Sheet sheet = workbook.createSheet("REPORTE_GENERAL_DE_VACACIONES");
|
||||
final Row headerRow = sheet.createRow(0);
|
||||
|
||||
String[] headers = {"Empleado", "Equipo", "Estado", "Total Horas"};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
}
|
||||
|
||||
int rowIndex = 1;
|
||||
for (final Employee employee : employees) {
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
row.createCell(0).setCellValue(getEmployeeFullName(employee));
|
||||
row.createCell(1).setCellValue(getTeamName(employee));
|
||||
row.createCell(2).setCellValue(getEmployeeStatus(employee));
|
||||
row.createCell(3).setCellValue(getGeneralTotal(employee));
|
||||
}
|
||||
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
workbook.write(out);
|
||||
return new ByteArrayInputStream(out.toByteArray());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error al generar el archivo Excel", e);
|
||||
}
|
||||
}
|
||||
|
||||
private StreamResource generateGeneralVacationReport() {
|
||||
final List<Employee> employees = employeeService.findAllEmployees();
|
||||
final ByteArrayInputStream excelStream = generateExcelReport(employees);
|
||||
return new StreamResource("reporte_general_de_vacaciones_" + LocalDate.now() + ".xlsx",
|
||||
() -> excelStream);
|
||||
}
|
||||
|
||||
private void downloadReport() {
|
||||
final StreamResource resource = generateGeneralVacationReport();
|
||||
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
|
||||
}
|
||||
|
||||
private void openDocumentStream(final StreamResource resource, final UI ui) {
|
||||
final StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
}
|
||||
|
||||
private TimeOffSummary getTimeOffSummary(final Employee employee) {
|
||||
final boolean isMale = employee.getGender() == Employee.Gender.MALE;
|
||||
final int currentYear = LocalDate.now().getYear();
|
||||
final LocalDate currentDate = LocalDate.now();
|
||||
final List<TimeOff> timeOffs = timeOffService.findVacations();
|
||||
double healthLicence = getHealthLicence(employee.getId());
|
||||
|
||||
double totalFixedAndMovableHolidays = calculateHolidayDays(timeOffs);
|
||||
double totalPersonalDays = calculatePersonalDays(timeOffs, isMale);
|
||||
final List<Double> vacationDays = calculateVacationDays(employee);
|
||||
|
||||
double totalVacationCurrentDays = calculateUtilizedVacationDays(
|
||||
employee.getId(),
|
||||
vacationDays.get(1),
|
||||
TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
);
|
||||
double totalVacationPreviousDays = calculateUtilizedVacationDays(
|
||||
employee.getId(),
|
||||
vacationDays.get(0),
|
||||
TimeOffRequestType.VACACION_GESTION_ANTERIOR
|
||||
);
|
||||
|
||||
final List<TimeOffRequest> requests = requestService.findRequestsByEmployeeId(employee.getId());
|
||||
double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(requests, currentYear);
|
||||
double utilizedPersonalDays = calculatePersonalDaysUtilized(requests, isMale, currentYear);
|
||||
|
||||
double remainingHolidayDays = calculateRemainingHolidayDays(
|
||||
totalFixedAndMovableHolidays,
|
||||
utilizedFixedAndMovableHolidays,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
double remainingPersonalDays = calculateRemainingPersonalDays(
|
||||
totalPersonalDays,
|
||||
utilizedPersonalDays,
|
||||
healthLicence,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
double remainingVacationDays = calculateRemainingVacationDays(
|
||||
totalVacationCurrentDays,
|
||||
totalVacationPreviousDays,
|
||||
employee.getDateOfExit(),
|
||||
currentDate
|
||||
);
|
||||
|
||||
return new TimeOffSummary(remainingHolidayDays, remainingPersonalDays, remainingVacationDays);
|
||||
}
|
||||
|
||||
private record TimeOffSummary(double remainingHolidayDays, double remainingPersonalDays,
|
||||
double remainingVacationDays) {
|
||||
public double getTotalRemaining() {
|
||||
return remainingHolidayDays + remainingPersonalDays + remainingVacationDays;
|
||||
}
|
||||
}
|
||||
|
||||
private double getHealthLicence(final UUID employeeId) {
|
||||
List<TimeOffRequest> healthRequests = requestService
|
||||
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD);
|
||||
return healthRequests != null && !healthRequests.isEmpty() ? healthRequests.getLast().getDaysBalance() : 2;
|
||||
}
|
||||
|
||||
private double calculateUtilizedVacationDays(final UUID employeeId, final double vacationDays,
|
||||
final TimeOffRequestType requestType) {
|
||||
List<TimeOffRequest> vacationRequests = requestService.findByEmployeeAndCategory(employeeId, requestType);
|
||||
if (vacationRequests != null && !vacationRequests.isEmpty()) {
|
||||
return vacationRequests.getLast().getDaysBalance();
|
||||
}
|
||||
return vacationDays;
|
||||
}
|
||||
|
||||
private double calculateRemainingVacationDays(final double totalVacationCurrentDays,
|
||||
final double totalVacationPreviousDays,
|
||||
final LocalDate exitDate,
|
||||
final LocalDate currentDate) {
|
||||
if (exitDate == null || exitDate.isAfter(currentDate)) {
|
||||
return totalVacationCurrentDays + totalVacationPreviousDays;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private double calculateRemainingHolidayDays(final double totalFixedAndMovableHolidays,
|
||||
final double utilizedFixedAndMovableHolidays,
|
||||
final LocalDate exitDate,
|
||||
final LocalDate currentDate) {
|
||||
if (exitDate == null || exitDate.isAfter(currentDate)) {
|
||||
return totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private double calculateRemainingPersonalDays(final double totalPersonalDays,
|
||||
final double utilizedPersonalDays,
|
||||
final double healthLicence,
|
||||
final LocalDate exitDate,
|
||||
final LocalDate currentDate) {
|
||||
if (exitDate == null || exitDate.isAfter(currentDate)) {
|
||||
return (totalPersonalDays - utilizedPersonalDays) + healthLicence;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private double calculateHolidayDays(final List<TimeOff> timeOffs) {
|
||||
return timeOffs.stream()
|
||||
.filter(req -> req.getType() != TimeOff.Type.OTHER)
|
||||
.mapToDouble(TimeOff::getDuration)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private double calculatePersonalDays(final List<TimeOff> timeOffs, final boolean isMale) {
|
||||
return timeOffs.stream()
|
||||
.filter(req -> req.getType() == TimeOff.Type.OTHER)
|
||||
.filter(req -> !getStandardExclusions().contains(req.getCategory()))
|
||||
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
|
||||
.filter(req -> !req.getCategory().name().startsWith("VACACION"))
|
||||
.filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.mapToDouble(TimeOff::getDuration)
|
||||
.sum();
|
||||
}
|
||||
|
||||
|
||||
private Set<TimeOffRequestType> getStandardExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.MATERNIDAD,
|
||||
TimeOffRequestType.PATERNIDAD,
|
||||
TimeOffRequestType.MATRIMONIO,
|
||||
TimeOffRequestType.DUELO_1ER_GRADO,
|
||||
TimeOffRequestType.DUELO_2ER_GRADO,
|
||||
TimeOffRequestType.DIA_DEL_PADRE,
|
||||
TimeOffRequestType.DIA_DE_LA_MADRE
|
||||
);
|
||||
}
|
||||
|
||||
private Set<TimeOffRequestType> getMaleSpecificExclusions() {
|
||||
return Set.of(
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_INTERNACIONAL,
|
||||
TimeOffRequestType.DIA_DE_LA_MUJER_NACIONAL
|
||||
);
|
||||
}
|
||||
|
||||
private double calculateHolidayUtilizedDays(final List<TimeOffRequest> requests, final int year) {
|
||||
return requests.stream()
|
||||
.filter(this::verificationIsHoliday)
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO
|
||||
|| req.getState() == TimeOffRequestStatus.APROBADO)
|
||||
.filter(req -> getStartDateYear(req) == year)
|
||||
.mapToDouble(TimeOffRequest::getDaysToBeTake)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private double calculatePersonalDaysUtilized(final List<TimeOffRequest> requests, final boolean isMale,
|
||||
final int year) {
|
||||
return requests.stream()
|
||||
.filter(req -> !verificationIsHoliday(req))
|
||||
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO
|
||||
|| req.getState() == TimeOffRequestStatus.APROBADO)
|
||||
.filter(req -> !getStandardExclusions().contains(req.getCategory()))
|
||||
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
|
||||
.filter(req -> !req.getCategory().name().startsWith("VACACION"))
|
||||
.filter(req -> req.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
|
||||
.filter(req -> getStartDateYear(req) == year)
|
||||
.mapToDouble(TimeOffRequest::getDaysToBeTake)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private int getStartDateYear(final TimeOffRequest request) {
|
||||
if (request.getStartDate() != null) {
|
||||
return request.getStartDate().getYear();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Boolean verificationIsHoliday(final TimeOffRequest request) {
|
||||
TimeOff timeoff = timeOffService.findVacationByCategory(request.getCategory());
|
||||
return timeoff.getType() != TimeOff.Type.OTHER;
|
||||
}
|
||||
|
||||
private String getDateString(final LocalDate date) {
|
||||
return (date != null) ? date.toString() : "";
|
||||
}
|
||||
|
||||
private ByteArrayInputStream generatePdfReport(final UUID employeeId) {
|
||||
final List<TimeOffRequest> requests = requestService.findRequestsByEmployeeId(employeeId);
|
||||
final Employee employee = employeeService.getEmployee(employeeId);
|
||||
final TimeOffSummary result = getTimeOffSummary(employee);
|
||||
|
||||
try (PDDocument document = new PDDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
PDPage page = new PDPage();
|
||||
document.addPage(page);
|
||||
|
||||
List<TimeOffRequest> filteredRequests = requests.stream()
|
||||
.filter(request ->
|
||||
(request.getStartDate() == null || Year.from(request.getStartDate()).equals(Year.now()))
|
||||
|| request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL
|
||||
|| request.getCategory() == TimeOffRequestType.VACACION_GESTION_ANTERIOR)
|
||||
.toList();
|
||||
|
||||
PDPageContentStream contentStream = null;
|
||||
try {
|
||||
contentStream = new PDPageContentStream(document, page);
|
||||
|
||||
contentStream.setFont(PDType1Font.TIMES_BOLD, 18);
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(200, 750);
|
||||
contentStream.showText("Reporte de Vacaciones");
|
||||
contentStream.endText();
|
||||
|
||||
contentStream.setFont(PDType1Font.TIMES_ROMAN, 14);
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(50, 700);
|
||||
contentStream.showText("Empleado: " + employee.getFirstName() + " " + employee.getLastName());
|
||||
contentStream.newLineAtOffset(0, -15);
|
||||
contentStream.showText("Equipo: " + employee.getTeam().getName());
|
||||
contentStream.endText();
|
||||
|
||||
float tableTopY = 650;
|
||||
float margin = 50;
|
||||
float cellHeight = 20;
|
||||
String[] headers = {"Categoría", "Estado", "Fecha de Inicio", "Fecha de Fin", "Días a Tomar"};
|
||||
int columns = headers.length;
|
||||
|
||||
float[] columnWidths = new float[columns];
|
||||
for (int i = 0; i < columns; i++) {
|
||||
columnWidths[i] = getMaxColumnWidth(headers[i], requests, i);
|
||||
}
|
||||
|
||||
contentStream.setFont(PDType1Font.TIMES_BOLD, 10);
|
||||
float currentX = margin;
|
||||
for (int i = 0; i < columns; i++) {
|
||||
contentStream.addRect(currentX, tableTopY, columnWidths[i], -cellHeight);
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(currentX + 5, tableTopY - 15);
|
||||
contentStream.showText(headers[i]);
|
||||
contentStream.endText();
|
||||
currentX += columnWidths[i];
|
||||
}
|
||||
contentStream.stroke();
|
||||
|
||||
contentStream.setFont(PDType1Font.TIMES_ROMAN, 10);
|
||||
float currentY = tableTopY - cellHeight;
|
||||
for (TimeOffRequest request : filteredRequests) {
|
||||
String startDate = getDateString(request.getStartDate());
|
||||
String endDate = getDateString(request.getEndDate());
|
||||
|
||||
String[] rowData = {
|
||||
request.getCategory().name(),
|
||||
request.getState().name(),
|
||||
startDate,
|
||||
endDate,
|
||||
String.valueOf(request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0)
|
||||
};
|
||||
|
||||
currentX = margin;
|
||||
for (int i = 0; i < columns; i++) {
|
||||
contentStream.addRect(currentX, currentY, columnWidths[i], -cellHeight);
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(currentX + 5, currentY - 15);
|
||||
contentStream.showText(rowData[i]);
|
||||
contentStream.endText();
|
||||
currentX += columnWidths[i];
|
||||
}
|
||||
contentStream.stroke();
|
||||
currentY -= cellHeight;
|
||||
|
||||
if (currentY < 50) {
|
||||
contentStream.close();
|
||||
page = new PDPage();
|
||||
document.addPage(page);
|
||||
contentStream = new PDPageContentStream(document, page);
|
||||
currentY = 750;
|
||||
}
|
||||
}
|
||||
|
||||
currentY -= 30;
|
||||
|
||||
if (currentY < 80) {
|
||||
contentStream.close();
|
||||
page = new PDPage();
|
||||
document.addPage(page);
|
||||
contentStream = new PDPageContentStream(document, page);
|
||||
currentY = 750;
|
||||
}
|
||||
|
||||
contentStream.setFont(PDType1Font.TIMES_ROMAN, 14);
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(50, currentY - 20);
|
||||
contentStream.showText("Total feriados fijos y movibles: " + result.remainingHolidayDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("Total días libres personales: " + result.remainingPersonalDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("Total vacaciones pendientes de uso: " + result.remainingVacationDays);
|
||||
contentStream.newLineAtOffset(0, -20);
|
||||
contentStream.showText("TOTAL GENERAL DE DÍAS DISPONIBLES: "
|
||||
+ (result.remainingHolidayDays + result.remainingPersonalDays + result.remainingVacationDays));
|
||||
contentStream.endText();
|
||||
|
||||
} finally {
|
||||
if (contentStream != null) {
|
||||
contentStream.close();
|
||||
}
|
||||
}
|
||||
document.save(out);
|
||||
return new ByteArrayInputStream(out.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error al generar el reporte", e);
|
||||
}
|
||||
}
|
||||
|
||||
private float getMaxColumnWidth(final String header, final List<TimeOffRequest> requests, final int columnIndex) {
|
||||
float maxWidth = header.length();
|
||||
for (TimeOffRequest request : requests) {
|
||||
String value = switch (columnIndex) {
|
||||
case 0 -> request.getCategory().name();
|
||||
case 1 -> request.getState().name();
|
||||
case 2 -> getDateString(request.getStartDate());
|
||||
case 3 -> getDateString(request.getEndDate());
|
||||
case 4 -> String.valueOf(request.getDaysToBeTake());
|
||||
default -> "";
|
||||
};
|
||||
if (value != null) {
|
||||
maxWidth = Math.max(maxWidth, value.length());
|
||||
}
|
||||
}
|
||||
return maxWidth * 7;
|
||||
}
|
||||
|
||||
private StreamResource generateVacationReport(final UUID employeeId) {
|
||||
Employee employee = employeeService.getEmployee(employeeId);
|
||||
String fileName = String.format("%s_%s-reporte_de_vacaciones_%s.pdf",
|
||||
employee.getFirstName(),
|
||||
employee.getLastName(),
|
||||
LocalDate.now());
|
||||
|
||||
ByteArrayInputStream pdfStream = generatePdfReport(employeeId);
|
||||
|
||||
return new StreamResource(fileName, () -> pdfStream)
|
||||
.setContentType("application/pdf")
|
||||
.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
}
|
||||
|
||||
private void downloadEmployeeReport(final UUID employeeId) {
|
||||
StreamResource resource = generateVacationReport(employeeId);
|
||||
getUI().ifPresent(ui -> openDocumentEmployeeStream(resource, ui));
|
||||
}
|
||||
|
||||
private void openDocumentEmployeeStream(final StreamResource resource, final UI ui) {
|
||||
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
|
||||
ui.getPage().open(registration.getResourceUri().toString());
|
||||
}
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
package com.primefactorsolutions.views.timesheet;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TimesheetService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.views.BaseEntityForm;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.datepicker.VDatePicker;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Horas Trabajadas")
|
||||
@Route(value = "/timesheet/:hours-workedId?/:action?", layout = MainLayout.class)
|
||||
public class TimesheetEntryView extends BaseEntityForm<TimesheetEntry> implements HasUrlParameter<String> {
|
||||
private final ComboBox<Team> team = new ComboBox<>("Equipo");
|
||||
private final ComboBox<Employee> employee = new ComboBox<>("Empleado");
|
||||
private final ComboBox<String> task = new ComboBox<>("Tarea");
|
||||
private final TextField details = new TextField("Detalle");
|
||||
private final VDatePicker date = new VDatePicker("Fecha");
|
||||
private final NumberField hours = new NumberField("Horas");
|
||||
|
||||
private final TimesheetService timesheetService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
|
||||
private TimesheetEntry timesheetEntry;
|
||||
|
||||
public TimesheetEntryView(final AuthenticationContext authenticationContext,
|
||||
final TimesheetService timesheetService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext, TimesheetEntry.class);
|
||||
this.timesheetService = timesheetService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
|
||||
task.setRequired(true);
|
||||
|
||||
initializeDateField();
|
||||
initializeTeamField();
|
||||
initializeEmployeeField();
|
||||
configureTasks();
|
||||
|
||||
this.setSavedHandler(this::saveHoursWorked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String action) {
|
||||
final RouteParameters params = beforeEvent.getRouteParameters();
|
||||
final String s = params.get("hours-workedId").orElse(null);
|
||||
|
||||
if ("new".equals(action) || s == null) {
|
||||
setEntityWithEnabledSave(new TimesheetEntry());
|
||||
} else {
|
||||
final UUID hoursWorkedId = UUID.fromString(s);
|
||||
timesheetEntry = timesheetService.getTimesheetEntry(hoursWorkedId);
|
||||
|
||||
if ("edit".equals(action) && !s.isEmpty()) {
|
||||
setEntityWithEnabledSave(timesheetEntry);
|
||||
} else if ("view".equals(action) && !s.isEmpty()) {
|
||||
setEntity(timesheetEntry);
|
||||
setFormReadOnly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setFormReadOnly() {
|
||||
employee.setReadOnly(true);
|
||||
team.setReadOnly(true);
|
||||
task.setReadOnly(true);
|
||||
details.setReadOnly(true);
|
||||
date.setReadOnly(true);
|
||||
hours.setReadOnly(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(
|
||||
date,
|
||||
team,
|
||||
employee,
|
||||
task,
|
||||
details,
|
||||
hours
|
||||
);
|
||||
}
|
||||
|
||||
private void configureTasks() {
|
||||
task.setWidthFull();
|
||||
task.setItems(
|
||||
"Entrevistas",
|
||||
"Reuniones",
|
||||
"Colaboraciones",
|
||||
"Aprendizajes",
|
||||
"Proyectos PFS",
|
||||
"Consulta Medica",
|
||||
"Afiliación al Seguro",
|
||||
"Fallas Tecnicas",
|
||||
"Otros");
|
||||
task.setPlaceholder("Selecciona una tarea...");
|
||||
}
|
||||
|
||||
private void initializeTeamField() {
|
||||
team.setWidthFull();
|
||||
team.setItems(teamService.findAllTeams());
|
||||
team.setItemLabelGenerator(Team::getName);
|
||||
team.addValueChangeListener(event -> {
|
||||
if (event.getOldValue() != null) {
|
||||
final Team selectedTeam = event.getValue();
|
||||
updateEmployeeField(selectedTeam);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateEmployeeField(final Team selectedTeam) {
|
||||
if (selectedTeam != null) {
|
||||
final List<Employee> employeesInTeam = employeeService.findAllEmployees().stream()
|
||||
.filter(employee -> employee.getTeam() != null && employee.getTeam().equals(selectedTeam))
|
||||
.toList();
|
||||
employee.setItems(employeesInTeam);
|
||||
employee.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeEmployeeField() {
|
||||
final List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
employee.setWidthFull();
|
||||
employee.setItems(employees);
|
||||
employee.setItemLabelGenerator(TimesheetEntryView::getEmployeeFullName);
|
||||
employee.setRequired(true);
|
||||
}
|
||||
|
||||
private static String getEmployeeFullName(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private void initializeDateField() {
|
||||
final LocalDate today = LocalDate.now();
|
||||
final YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth());
|
||||
final LocalDate startOfMonth = currentMonth.atDay(1);
|
||||
|
||||
date.setWidthFull();
|
||||
date.setMin(startOfMonth);
|
||||
date.setMax(today);
|
||||
date.setValue(today);
|
||||
}
|
||||
|
||||
private void saveHoursWorked(final TimesheetEntry timesheetEntry) {
|
||||
if (isFormValid()) {
|
||||
timesheetService.save(timesheetEntry);
|
||||
closeForm();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeForm() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimesheetListView.class));
|
||||
}
|
||||
|
||||
private boolean isFormValid() {
|
||||
return date.getValue() != null
|
||||
&& team.getValue() != null
|
||||
&& employee.getValue() != null
|
||||
&& task.getValue() != null;
|
||||
}
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
package com.primefactorsolutions.views.timesheet;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.model.Week;
|
||||
import com.primefactorsolutions.service.EmployeeService;
|
||||
import com.primefactorsolutions.service.TimesheetService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.primefactorsolutions.views.util.MenuBarUtils;
|
||||
import com.vaadin.flow.component.ClickEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.ComponentEventListener;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.contextmenu.MenuItem;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.menubar.MenuBar;
|
||||
import com.vaadin.flow.component.menubar.MenuBarVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.function.ValueProvider;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.PagingGrid;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
|
||||
|
||||
@SpringComponent
|
||||
@PermitAll
|
||||
@Scope("prototype")
|
||||
@PageTitle("Registro de Horas Trabajadas")
|
||||
@Route(value = "/timesheet/records", layout = MainLayout.class)
|
||||
public class TimesheetListView extends BaseView {
|
||||
|
||||
private final TimesheetService timesheetService;
|
||||
private final EmployeeService employeeService;
|
||||
private final TeamService teamService;
|
||||
private final PagingGrid<TimesheetEntry> timesheetPagingGrid = new PagingGrid<>();
|
||||
private ComboBox<Employee> employeeFilter;
|
||||
private ComboBox<Team> teamFilter;
|
||||
private ComboBox<Week> weekFilter;
|
||||
|
||||
public TimesheetListView(final AuthenticationContext authenticationContext,
|
||||
final TimesheetService timesheetService,
|
||||
final EmployeeService employeeService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext);
|
||||
this.timesheetService = timesheetService;
|
||||
this.employeeService = employeeService;
|
||||
this.teamService = teamService;
|
||||
|
||||
initializeView();
|
||||
}
|
||||
|
||||
private void refreshGridListHoursWorked(final Employee employee,
|
||||
final Team team,
|
||||
final Week week) {
|
||||
timesheetPagingGrid.setPagingDataProvider((page, pageSize) -> {
|
||||
final int start = (int) (page * timesheetPagingGrid.getPageSize());
|
||||
return fetchFilteredHoursWorked(start, pageSize, employee, team, week);
|
||||
});
|
||||
timesheetPagingGrid.getDataProvider().refreshAll();
|
||||
}
|
||||
|
||||
private List<TimesheetEntry> fetchFilteredHoursWorked(final int start,
|
||||
final int pageSize,
|
||||
final Employee employee,
|
||||
final Team team,
|
||||
final Week week) {
|
||||
List<TimesheetEntry> filteredTimesheetEntry = timesheetService.findListHoursWorkedEmployee(week.from(),
|
||||
week.to());
|
||||
|
||||
if (employee != null) {
|
||||
filteredTimesheetEntry = filteredTimesheetEntry.stream()
|
||||
.filter(e -> e.getEmployee().getId().equals(employee.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (team != null) {
|
||||
filteredTimesheetEntry = filteredTimesheetEntry.stream()
|
||||
.filter(e -> e.getEmployee().getTeam() != null
|
||||
&& e.getEmployee().getTeam().getId().equals(team.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int end = Math.min(start + pageSize, filteredTimesheetEntry.size());
|
||||
|
||||
return filteredTimesheetEntry.subList(start, end);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
getCurrentPageLayout().add(createAddHoursWorked());
|
||||
setupFilters();
|
||||
setupListHoursWorkedGrid();
|
||||
getCurrentPageLayout().add(timesheetPagingGrid);
|
||||
refreshGridListHoursWorked(employeeFilter.getValue(), teamFilter.getValue(), weekFilter.getValue());
|
||||
}
|
||||
|
||||
private void setupFilters() {
|
||||
final HorizontalLayout hl = new HorizontalLayout();
|
||||
hl.add(createWeekFilter());
|
||||
hl.add(createEmployeeFilter());
|
||||
hl.add(createTeamFilter());
|
||||
|
||||
getCurrentPageLayout().add(hl);
|
||||
}
|
||||
|
||||
private void setupListHoursWorkedGrid() {
|
||||
timesheetPagingGrid.addColumn(e -> e.getDate() != null ? e.getDate().toString() : "")
|
||||
.setHeader("Fecha")
|
||||
.setSortable(true);
|
||||
timesheetPagingGrid.addColumn(e -> e.getEmployee().getFirstName() + " " + e.getEmployee().getLastName())
|
||||
.setHeader("Empleado");
|
||||
timesheetPagingGrid.addColumn(e -> e.getEmployee().getTeam() != null
|
||||
? e.getEmployee().getTeam().getName()
|
||||
: "Sin asignar")
|
||||
.setHeader("Equipo");
|
||||
timesheetPagingGrid.addColumn(e -> {
|
||||
String details = e.getDetails() != null ? e.getDetails() : "Sin Detalle";
|
||||
String task = e.getTask() != null ? e.getTask() : "";
|
||||
return !task.isEmpty() ? task : details;
|
||||
}).setHeader("Details");
|
||||
timesheetPagingGrid.addColumn(TimesheetEntry::getHours).setHeader("Horas").setSortable(true);
|
||||
|
||||
timesheetPagingGrid.addComponentColumn((ValueProvider<TimesheetEntry, Component>) entry -> {
|
||||
final MenuBar menuBar = new MenuBar();
|
||||
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||
final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.EYE, "Ver");
|
||||
viewItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
navigateToTimesheetEntryView(entry.getId(), "view");
|
||||
});
|
||||
final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Editar");
|
||||
editItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
navigateToTimesheetEntryView(entry.getId(), "edit");
|
||||
});
|
||||
return menuBar;
|
||||
});
|
||||
|
||||
timesheetPagingGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
|
||||
timesheetPagingGrid.setPageSize(PAGE_SIZE);
|
||||
}
|
||||
|
||||
private void navigateToTimesheetEntryView(final UUID idRecord, final String action) {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimesheetEntryView.class, idRecord.toString() + "/" + action));
|
||||
}
|
||||
|
||||
private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
|
||||
final Button button = new Button(label);
|
||||
|
||||
if (isPrimary) {
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
}
|
||||
|
||||
button.addClickListener(event -> onClickAction.run());
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAddHoursWorked() {
|
||||
return createButton("Agregar Actividad", this::navigateToHours, true);
|
||||
}
|
||||
|
||||
private void navigateToHours() {
|
||||
getUI().ifPresent(ui -> ui.navigate(TimesheetEntryView.class, "new"));
|
||||
}
|
||||
|
||||
private ComboBox<Employee> createEmployeeFilter() {
|
||||
employeeFilter = new ComboBox<>("Empleado");
|
||||
employeeFilter.setPlaceholder("Seleccionar ...");
|
||||
employeeFilter.setClearButtonVisible(true);
|
||||
|
||||
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
|
||||
|
||||
if (!isRoleAdmin()) {
|
||||
employeeFilter.setReadOnly(true);
|
||||
employees = employees.stream()
|
||||
.filter(e -> getEmployeeId().equals(Optional.ofNullable(e.getId())))
|
||||
.toList();
|
||||
}
|
||||
|
||||
employeeFilter.setItems(employees);
|
||||
|
||||
if (!isRoleAdmin()) {
|
||||
employeeFilter.setValue(employees.getFirst());
|
||||
}
|
||||
|
||||
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
|
||||
employeeFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(
|
||||
event.getValue(),
|
||||
teamFilter.getValue(),
|
||||
weekFilter.getValue()
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
return employeeFilter;
|
||||
}
|
||||
|
||||
private String getEmployeeFullName(final Employee employee) {
|
||||
return employee.getFirstName() + " " + employee.getLastName();
|
||||
}
|
||||
|
||||
private ComboBox<Team> createTeamFilter() {
|
||||
teamFilter = new ComboBox<>("Equipo");
|
||||
final List<Team> teams = new ArrayList<>(teamService.findAllTeams());
|
||||
teamFilter.setPlaceholder("Seleccionar ...");
|
||||
teamFilter.setClearButtonVisible(true);
|
||||
teamFilter.setItems(teams);
|
||||
teamFilter.setItemLabelGenerator(this::getTeamLabel);
|
||||
teamFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(
|
||||
employeeFilter.getValue(),
|
||||
event.getValue(),
|
||||
weekFilter.getValue()
|
||||
)
|
||||
);
|
||||
return teamFilter;
|
||||
}
|
||||
|
||||
private ComboBox<Week> createWeekFilter() {
|
||||
final List<Week> weeks = Week.getLastWeeks(4);
|
||||
weekFilter = new ComboBox<>("Week");
|
||||
weekFilter.setItems(weeks);
|
||||
weekFilter.setValue(weeks.getFirst());
|
||||
weekFilter.addValueChangeListener(event ->
|
||||
refreshGridListHoursWorked(
|
||||
employeeFilter.getValue(),
|
||||
teamFilter.getValue(),
|
||||
event.getValue()
|
||||
)
|
||||
);
|
||||
|
||||
return weekFilter;
|
||||
}
|
||||
|
||||
private String getTeamLabel(final Team team) {
|
||||
return team.getName();
|
||||
}
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
package com.primefactorsolutions.views.timesheet;
|
||||
|
||||
import com.primefactorsolutions.model.TimesheetEntry;
|
||||
import com.primefactorsolutions.model.Team;
|
||||
import com.primefactorsolutions.model.Week;
|
||||
import com.primefactorsolutions.service.TimesheetService;
|
||||
import com.primefactorsolutions.service.ReportService;
|
||||
import com.primefactorsolutions.service.TeamService;
|
||||
import com.primefactorsolutions.views.BaseView;
|
||||
import com.primefactorsolutions.views.MainLayout;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.html.Anchor;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.server.StreamResource;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.IsoFields;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Route(value = "/timesheet/report", layout = MainLayout.class)
|
||||
@PageTitle("Reporte de Horas Trabajadas")
|
||||
@RolesAllowed("ROLE_ADMIN")
|
||||
@Slf4j
|
||||
public class TimesheetReportView extends BaseView {
|
||||
|
||||
private final TimesheetService timesheetService;
|
||||
private final ReportService reportService;
|
||||
private final ComboBox<Team> teamComboBox = new ComboBox<>("Equipo");
|
||||
private final ComboBox<Week> weekComboBox = new ComboBox<>("Semana");
|
||||
private final Grid<Map<String, Object>> grid = new Grid<>();
|
||||
private Anchor downloadLink;
|
||||
private final int currentYear = LocalDate.now().getYear();
|
||||
|
||||
@Autowired
|
||||
public TimesheetReportView(final AuthenticationContext authenticationContext,
|
||||
final TimesheetService timesheetService,
|
||||
final ReportService reportService,
|
||||
final TeamService teamService) {
|
||||
super(authenticationContext);
|
||||
this.timesheetService = timesheetService;
|
||||
this.reportService = reportService;
|
||||
|
||||
final List<Team> teams = teamService.findAllTeams();
|
||||
teamComboBox.setPlaceholder("Seleccionar ...");
|
||||
teamComboBox.setItems(teams);
|
||||
teamComboBox.setItemLabelGenerator(Team::getName);
|
||||
initializeWeekComboBox();
|
||||
|
||||
final Button reportButton = new Button("Generar Reporte de Horas Trabajadas",
|
||||
event -> generateHoursWorkedReport());
|
||||
getCurrentPageLayout().add(reportButton);
|
||||
final HorizontalLayout filtersLayout = new HorizontalLayout(teamComboBox, weekComboBox);
|
||||
getCurrentPageLayout().add(filtersLayout);
|
||||
|
||||
grid.addColumn(map -> map.get("Empleado")).setHeader("Empleado");
|
||||
grid.addColumn(map -> map.get("Horas Trabajadas")).setHeader("Horas Trabajadas");
|
||||
grid.addColumn(map -> map.get("Horas Pendientes")).setHeader("Horas Pendientes");
|
||||
grid.addColumn(map -> map.get("Observaciones")).setHeader("Observaciones");
|
||||
|
||||
getCurrentPageLayout().add(grid);
|
||||
}
|
||||
|
||||
private void initializeWeekComboBox() {
|
||||
weekComboBox.setItems(Week.getLastWeeks(8));
|
||||
weekComboBox.setPlaceholder("Seleccionar ...");
|
||||
}
|
||||
|
||||
private void generateHoursWorkedReport() {
|
||||
final Team selectedTeam = teamComboBox.getValue();
|
||||
final Week selectedWeek = weekComboBox.getValue();
|
||||
|
||||
if (selectedTeam == null || selectedWeek == null) {
|
||||
Notification.show("Por favor, selecciona un equipo y una semana para generar el reporte.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<TimesheetEntry> timesheetEntryList =
|
||||
timesheetService.findListHoursWorkedEmployee(selectedWeek.from(), selectedWeek.to()).stream()
|
||||
.filter(e -> e.getEmployee().getTeam().getId().equals(selectedTeam.getId()))
|
||||
.toList();
|
||||
|
||||
if (timesheetEntryList.isEmpty()) {
|
||||
Notification.show("No hay horas trabajadas disponibles para generar el reporte.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<Map<String, Object>> data = timesheetEntryList.stream()
|
||||
.map(e -> {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("ID", e.getId().toString());
|
||||
map.put("Employee ID", e.getEmployee().getId().toString());
|
||||
map.put("Empleado", e.getEmployee().getFirstName() + " "
|
||||
+ e.getEmployee().getLastName());
|
||||
map.put("Horas Trabajadas", e.getHours());
|
||||
map.put("Horas Pendientes", 40 - e.getHours());
|
||||
map.put("Observaciones", "");
|
||||
return map;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
grid.setItems(data);
|
||||
int weekNumber = weekComboBox.getValue().from().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
generateExcelDownloadLink(data, weekNumber);
|
||||
}
|
||||
|
||||
private void generateExcelDownloadLink(final List<Map<String, Object>> data, final int weekNumber) {
|
||||
try {
|
||||
final List<String> headers = List.of("Empleado", "Horas Trabajadas", "Horas Pendientes", "Observaciones");
|
||||
final String selectedTeam = teamComboBox.getValue().getName();
|
||||
final byte[] excelBytes = reportService.writeAsExcel(
|
||||
"hours_worked_report", headers, data, selectedTeam, weekNumber, currentYear);
|
||||
final StreamResource excelResource = new StreamResource("hours_worked_report.xlsx",
|
||||
() -> new ByteArrayInputStream(excelBytes));
|
||||
|
||||
if (downloadLink == null) {
|
||||
downloadLink = new Anchor(excelResource, "Descargar Reporte en Excel");
|
||||
downloadLink.getElement().setAttribute("download", true);
|
||||
getCurrentPageLayout().add(downloadLink);
|
||||
} else {
|
||||
downloadLink.setHref(excelResource);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Notification.show("Error al generar el reporte de horas trabajadas en Excel.",
|
||||
3000, Notification.Position.MIDDLE);
|
||||
log.error("Error generating report", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package com.primefactorsolutions.views.util;
|
||||
|
||||
import com.primefactorsolutions.model.Employee;
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@UtilityClass
|
||||
public class AuthUtils {
|
||||
public static boolean isUser(final AuthenticationContext authenticationContext) {
|
||||
return authenticationContext.getAuthenticatedUser(UserDetails.class).isPresent();
|
||||
}
|
||||
|
||||
public static boolean isAdmin(final AuthenticationContext authenticationContext) {
|
||||
return authenticationContext.getAuthenticatedUser(UserDetails.class)
|
||||
.map(u ->
|
||||
u.getAuthorities().stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.anyMatch("ROLE_ADMIN"::equals)).orElse(false);
|
||||
}
|
||||
|
||||
public static Optional<UUID> getEmployeeId(final AuthenticationContext authenticationContext) {
|
||||
return authenticationContext.getAuthenticatedUser(UserDetails.class)
|
||||
.map(u -> u instanceof Employee
|
||||
? ((Employee) u).getId()
|
||||
: null);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package com.primefactorsolutions.views.util;
|
||||
|
||||
import com.vaadin.flow.component.HasSize;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class ComponentUtils {
|
||||
|
||||
public static <T extends HasSize> T withFullWidth(final T component) {
|
||||
component.setWidthFull();
|
||||
return component;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package com.primefactorsolutions.views.util;
|
||||
|
||||
import com.primefactorsolutions.model.HasLabel;
|
||||
import org.vaadin.firitin.components.combobox.VComboBox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityComboBox<T extends HasLabel> extends VComboBox<T> {
|
||||
public EntityComboBox(final String label) {
|
||||
super(label);
|
||||
this.setItemLabelGenerator(HasLabel::getLabel);
|
||||
}
|
||||
|
||||
public EntityComboBox(final String label, final List<T> items) {
|
||||
super(label, items);
|
||||
this.setItemLabelGenerator(HasLabel::getLabel);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user