Compare commits

..

8 Commits

121 changed files with 2452 additions and 8658 deletions

View File

@ -1,14 +0,0 @@
name: PR Builder
run-name: ${{ gitea.actor }} building PR
on: [pull_request]
jobs:
Build-PR:
runs-on: ubuntu-22.04
steps:
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event on branch ${{ gitea.head_ref }} and ref is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Build PR
if: gitea.base_ref == 'main'
run: |
git clone --single-branch --branch "${{ gitea.head_ref }}" https://git.primefactorsolutions.com/PFS/pfs-intra.git && cd pfs-intra && ./mvnw clean package -Pproduction
- run: echo "This job's status is ${{ job.status }}."

View File

@ -1,16 +0,0 @@
name: Builder
run-name: ${{ gitea.actor }} building
on:
push:
branches:
- main
jobs:
Build-Project:
runs-on: ubuntu-22.04
steps:
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event on branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Build package
run: |
git clone --single-branch --branch main https://git.primefactorsolutions.com/PFS/pfs-intra.git && cd pfs-intra && ./mvnw clean package -Pproduction && unlink /home/ubuntu/pfs-intra/app.jar && cp target/*.jar /home/ubuntu/pfs-intra/app.jar && sudo systemctl restart pfs-intra
- run: echo "This job's status is ${{ job.status }}."

1
.gitignore vendored
View File

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

Binary file not shown.

2492
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,23 +5,21 @@
"dependencies": {
"@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"
@ -50,23 +48,21 @@
"dependencies": {
"@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 +87,7 @@
"workbox-core": "7.1.0",
"workbox-precaching": "7.1.0"
},
"hash": "1a0f17d48b329307b5862bc57499307d1b89f7d89260121c2b7189f76957c436"
"hash": "ff8035f5a0dd319a5c705d9cea5b3e1d6c1a0bb871c35af8dd8e4ede5c65fe76"
},
"overrides": {
"@vaadin/bundles": "$@vaadin/bundles",
@ -113,8 +109,6 @@
"@vaadin/vaadin-themable-mixin": "$@vaadin/vaadin-themable-mixin",
"@vaadin/vaadin-lumo-styles": "$@vaadin/vaadin-lumo-styles",
"@vaadin/vaadin-material-styles": "$@vaadin/vaadin-material-styles",
"@f0rce/ace-widget": "$@f0rce/ace-widget",
"print-js": "$print-js",
"@vaadin-component-factory/vcf-pdf-viewer": "$@vaadin-component-factory/vcf-pdf-viewer"
"@f0rce/ace-widget": "$@f0rce/ace-widget"
}
}

83
pom.xml
View File

@ -11,8 +11,7 @@
<properties>
<java.version>21</java.version>
<vaadin.version>24.5.1</vaadin.version>
<vaadin-maven-plugin.version>24.4.6</vaadin-maven-plugin.version>
<vaadin.version>24.4.6</vaadin.version>
</properties>
<parent>
@ -101,10 +100,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
@ -120,15 +115,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 +177,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>
@ -250,64 +221,16 @@
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>org.vaadin.addons.componentfactory</groupId>
<artifactId>vcf-pdf-viewer</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-pdfbox</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<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 +260,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 +321,7 @@
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin-maven-plugin.version}</version>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

@ -1,32 +1,20 @@
package com.primefactorsolutions.config;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.views.LoginView;
import com.vaadin.flow.spring.security.VaadinWebSecurity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.Collection;
@EnableWebSecurity
@Configuration
public class SecurityConfig extends VaadinWebSecurity {
@Value("${spring.ldap.url}")
private String ldapUrl;
@Override
protected void configure(final HttpSecurity http) throws Exception {
@ -49,28 +37,13 @@ public class SecurityConfig extends VaadinWebSecurity {
}
@Bean
public AuthenticationManager authenticationManager(final UserDetailsContextMapper userDetailsContextMapper) {
final DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
String.format("%s/dc=primefactorsolutions,dc=com", ldapUrl));
public AuthenticationManager authenticationManager() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://localhost:8389/dc=primefactorsolutions,dc=com");
contextSource.setCacheEnvironmentProperties(false);
final LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=users");
factory.setUserDetailsContextMapper(userDetailsContextMapper);
return factory.createAuthenticationManager();
}
@Bean
public UserDetailsContextMapper userDetailsContextMapper(final EmployeeService employeeService) {
return new LdapUserDetailsMapper() {
@Override
public UserDetails mapUserFromContext(final DirContextOperations ctx, final String username,
final Collection<? extends GrantedAuthority> authorities) {
final UserDetails details = super.mapUserFromContext(ctx, username, authorities);
final Employee employee = employeeService.getDetachedEmployeeByUsername(details.getUsername());
return employee == null ? details : employee;
}
};
}
}

View File

@ -15,33 +15,30 @@ import java.util.Optional;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Exam extends BaseEntity {
public class Assessment extends BaseEntity {
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@JoinTable(name = "EXAM_QUESTIONS", joinColumns = @JoinColumn(name = "exam_id"),
@JoinTable(name = "ASSESSMENT_QUESTIONS", joinColumns = @JoinColumn(name = "assessment_id"),
inverseJoinColumns = @JoinColumn(name = "question_id"))
private List<Question> questions = new ArrayList<>();
@OneToMany(fetch = FetchType.EAGER, mappedBy = "exam", cascade = {CascadeType.ALL})
@OneToMany(fetch = FetchType.EAGER, mappedBy = "assessment", cascade = {CascadeType.ALL})
private List<Submission> submissions = new ArrayList<>();
@ManyToOne
private Candidate candidate;
@ManyToOne
private Evaluation evaluation;
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@JoinColumn(name = "EXAM_ID")
private List<ExamEvent> examEvents = new ArrayList<>();
@JoinColumn(name = "ASSESSMENT_ID")
private List<AssessmentEvent> assessmentEvents = new ArrayList<>();
public Submission getCurrentSubmission() {
return submissions.getLast();
}
public Long getRemainingTimeSeconds() {
final Optional<Instant> started = examEvents.stream()
.filter(e -> e.getStatus() == ExamStatus.STARTED)
.map(ExamEvent::getTimestamp)
final Optional<Instant> started = assessmentEvents.stream()
.filter(e -> e.getStatus() == AssessmentStatus.STARTED)
.map(AssessmentEvent::getTimestamp)
.findFirst();
final Integer totalTimeMinutes = questions.stream()
@ -55,24 +52,24 @@ public class Exam extends BaseEntity {
}
public Instant getStartingTime() {
final Optional<Instant> started = examEvents.stream()
.filter(e -> e.getStatus() == ExamStatus.STARTED)
.map(ExamEvent::getTimestamp)
final Optional<Instant> started = assessmentEvents.stream()
.filter(e -> e.getStatus() == AssessmentStatus.STARTED)
.map(AssessmentEvent::getTimestamp)
.findFirst();
return started.orElse(null);
}
public boolean isCompleted() {
return examEvents.stream().filter(e -> e.getStatus() == ExamStatus.COMPLETED)
.map(ExamEvent::getTimestamp)
return assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.COMPLETED)
.map(AssessmentEvent::getTimestamp)
.findFirst()
.isPresent();
}
public boolean isStarted() {
return examEvents.stream().filter(e -> e.getStatus() == ExamStatus.STARTED)
.map(ExamEvent::getTimestamp)
return assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.STARTED)
.map(AssessmentEvent::getTimestamp)
.findFirst()
.isPresent();
}

View File

@ -13,7 +13,7 @@ import java.time.Instant;
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
public class ExamEvent extends BaseEntity {
public class AssessmentEvent extends BaseEntity {
private Instant timestamp;
private ExamStatus status;
private AssessmentStatus status;
}

View File

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

View File

@ -1,11 +1,11 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Version;
import java.time.Instant;
import java.util.UUID;
@MappedSuperclass
@ -13,18 +13,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 +48,4 @@ public abstract class BaseEntity {
}
return super.equals(that);
}
@PrePersist
public void updateCreated() {
this.created = Instant.now();
}
@PreUpdate
public void updateUpdated() {
this.updated = Instant.now();
}
}

View File

@ -6,17 +6,17 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Candidate extends BaseEntity implements HasLabel {
public class Candidate extends BaseEntity {
@Column(unique = true)
private String email;
@Override
public String getLabel() {
return email;
}
@OneToMany(fetch = FetchType.EAGER, mappedBy = "candidate")
private List<Assessment> assessments;
}

View File

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

View File

@ -1,23 +1,23 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import jakarta.annotation.Nullable;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.*;
@Data
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Document extends BaseEntity {
private String fileName;
@Enumerated(EnumType.STRING)
@Nullable
private String description;
private String location;
private DocumentType documentType;
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
@Lob
@Column(columnDefinition = "BLOB")
private byte[] fileData;
private String creator;
}

View File

@ -1,34 +1,7 @@
package com.primefactorsolutions.model;
public enum DocumentType {
CARNET_DE_IDENTIDAD,
RECIBOS_DE_PAGO,
CONTRATO_DE_TRABAJO,
CERTIFICADO_DE_TRABAJO,
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
ID_CARD,
PAY_STUB,
OTHER
}

View File

@ -1,193 +1,47 @@
package com.primefactorsolutions.model;
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
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.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity implements UserDetails, HasLabel {
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;
@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;
@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;
@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 LocalDate dateOfEntry;
private LocalDate dateOfExit;
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;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Lists.newArrayList(new SimpleGrantedAuthority("ROLE_" + this.role.name()));
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity {
private String firstName;
private String lastName;
private LocalDate birthday;
private String birthCity;
@Enumerated(EnumType.STRING)
private MaritalStatus maritalStatus;
private String residenceAddress;
private String phoneNumber;
private String personalEmail;
private String emergencyCName;
private String emergencyCAddress;
private String emergencyCPhone;
private String emergencyCEmail;
@Enumerated(EnumType.STRING)
private Status status;
public enum Status {
ACTIVE,
INACTIVE
}
public enum MaritalStatus {
SINGLE,
MARRIED,
WIDOWED,
DIVORCED
}
public Status getStatus() {
return status;
}
public void setStatus(final Status status) {
this.status = status;
}
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Enumerated(EnumType.STRING)
private MaritalStatus maritalStatus;
@Enumerated(EnumType.STRING)
private Gender gender;
@Enumerated(EnumType.STRING)
private ContractType contractType;
@Size(max = 255, message = "El detalle del contrato no debe exceder 255 caracteres")
private String otherContractDetail;
@Enumerated(EnumType.STRING)
private Role role = Role.USER;
@Override
public String getLabel() {
return String.format("%s %s", firstName, lastName);
}
public enum Status {
ACTIVE,
INACTIVE
}
public enum Gender {
MALE,
FEMALE
}
public enum MaritalStatus {
SINGLE,
MARRIED,
WIDOWED,
DIVORCED
}
public enum ContractType {
CONTRATO_LABORAL,
CONTRATO_CIVIL_O_SERVICIOS,
CONTRATO_PLAZO_FIJO,
CONSULTORIA_INTERNA,
CONSULTORIA_EXTERNA,
CONTRATO_MIXTO,
OTROS
}
}

View File

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

View File

@ -1,30 +0,0 @@
package com.primefactorsolutions.model;
import com.google.common.collect.Lists;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Type;
import java.util.List;
@Entity
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Evaluation extends BaseEntity {
@ManyToOne
private Candidate candidate;
private Integer points;
private EmployeePosition candidatePosition;
private Employee interviewer;
@Type(JsonType.class)
@Column(columnDefinition = "json")
@ColumnDefault("JSON_ARRAY()")
private List<SkillEvaluation> skillEvaluations = Lists.newArrayList();
@OneToMany(fetch = FetchType.EAGER, mappedBy = "evaluation")
private List<Exam> exams;
}

View File

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

View File

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

View File

@ -1,17 +0,0 @@
package com.primefactorsolutions.model;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = MinAgeValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MinAge {
String message() default "El empleado debe ser mayor de {value} años";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int value();
}

View File

@ -1,24 +0,0 @@
package com.primefactorsolutions.model;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class MinAgeValidator implements ConstraintValidator<MinAge, LocalDate> {
private int minAge;
@Override
public void initialize(final MinAge constraintAnnotation) {
this.minAge = constraintAnnotation.value();
}
@Override
public boolean isValid(final LocalDate birthday, final ConstraintValidatorContext context) {
if (birthday == null) {
return true;
}
return ChronoUnit.YEARS.between(birthday, LocalDate.now()) >= minAge;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,7 @@
package com.primefactorsolutions.model;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToOne;
@ -7,6 +9,9 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Type;
import java.util.Map;
@Entity
@Data
@ -18,12 +23,14 @@ public class Submission extends BaseEntity {
private Question question;
@Lob
private String text;
private String response;
private String output;
@Type(JsonType.class)
@Column(columnDefinition = "json")
private Map<String, Object> results;
private SubmissionStatus submissionStatus;
@ManyToOne
private Exam exam;
private Assessment assessment;
}

View File

@ -1,14 +0,0 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import lombok.*;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Team extends BaseEntity {
private String name;
}

View File

@ -1,32 +0,0 @@
package com.primefactorsolutions.model;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class TimeOff extends BaseEntity {
@Enumerated(EnumType.STRING)
private TimeOffRequestType category;
private LocalDate date;
private Double duration;
private Double expiration;
@Enumerated(EnumType.STRING)
private Type type;
public enum Type {
FIXED,
MOVABLE,
OTHER
}
}

View File

@ -1,30 +1,21 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.util.List;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class TimeOffRequest extends BaseEntity {
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
@Enumerated(EnumType.STRING)
private TimeOffRequestType category;
@Enumerated(EnumType.STRING)
private TimeOffRequestStatus state;
private Double availableDays;
private LocalDate expiration;
private LocalDate startDate;
private LocalDate endDate;
private Double daysToBeTake;
private Double daysBalance;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "request", cascade = {CascadeType.ALL})
private List<TimeOffRequestEntry> entries;
private TimeOffRequestType type;
}

View File

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

View File

@ -1,29 +1,7 @@
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,
VACATION,
MATERNITY,
OTHER
}

View File

@ -0,0 +1,21 @@
package com.primefactorsolutions.model;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import java.util.List;
@Entity
@Data
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Timesheet extends BaseEntity {
@Type(JsonType.class)
@Column(columnDefinition = "json")
private List<TimesheetEntry> entries;
}

View File

@ -1,30 +1,13 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDate;
import java.time.temporal.IsoFields;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class TimesheetEntry extends BaseEntity {
private String task;
private String details;
private LocalDate date;
private double hours;
@ManyToOne
@JoinColumn(name = "employee_id")
private Employee employee;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
public int getWeekNumber() {
return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
}
public class TimesheetEntry {
private TaskType taskType;
private int hours;
}

View File

@ -1,46 +0,0 @@
package com.primefactorsolutions.model;
import com.google.common.collect.Lists;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.IsoFields;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public record Week(LocalDate from, LocalDate to) {
@Override
public String toString() {
final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
return String.format("from %s to %s", formatter.format(from), formatter.format(to));
}
public static Week getCurrent() {
final int weekNumber = LocalDate.now().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
final LocalDate from = getFirstDayOfWeek(weekNumber);
return new Week(from, from.plusDays(6));
}
public static List<Week> getLastWeeks(final int numberOfWeeks) {
final int weekNumber = LocalDate.now().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
LocalDate from = getFirstDayOfWeek(weekNumber);
final ArrayList<Week> result = Lists.newArrayList();
for (int i = 0; i < numberOfWeeks; i++) {
result.add(new Week(from, from.plusDays(6)));
from = from.minusDays(7);
}
return result;
}
private static LocalDate getFirstDayOfWeek(final int weekNumber) {
return LocalDate
.now()
.with(WeekFields.of(Locale.US).getFirstDayOfWeek())
.with(WeekFields.of(Locale.US).weekOfWeekBasedYear(), weekNumber);
}
}

View File

@ -1,9 +1,9 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Exam;
import com.primefactorsolutions.model.Assessment;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface ExamRepository extends JpaRepository<Exam, UUID> {
public interface AssessmentRepository extends JpaRepository<Assessment, UUID> {
}

View File

@ -1,9 +0,0 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Document;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface DocumentRepository extends JpaRepository<Document, UUID> {
}

View File

@ -3,14 +3,7 @@ package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface EmployeeRepository extends JpaRepository<Employee, UUID> {
Optional<Employee> findByUsername(String username);
Optional<Employee> findByPersonalEmail(String personalEmail);
List<Employee> findAllByTeamId(UUID teamId);
//Optional<Employee> findByTeamIdAndLeadManagerTrue(UUID teamId);
List<Employee> findByTeamName(String teamName);
}
}

View File

@ -1,9 +0,0 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Evaluation;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface EvaluationRepository extends JpaRepository<Evaluation, UUID> {
}

View File

@ -1,9 +0,0 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Team;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface TeamRepository extends JpaRepository<Team, UUID> {
}

View File

@ -1,14 +0,0 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.TimeOff;
import com.primefactorsolutions.model.TimeOffRequestType;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
public interface TimeOffRepository extends JpaRepository<TimeOff, UUID> {
TimeOff findByCategory(TimeOffRequestType category);
List<TimeOff> findByDateBetween(LocalDate from, LocalDate to);
}

View File

@ -1,19 +0,0 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.TimeOffRequest;
import com.primefactorsolutions.model.TimeOffRequestStatus;
import com.primefactorsolutions.model.TimeOffRequestType;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface TimeOffRequestRepository extends JpaRepository<TimeOffRequest, UUID> {
List<TimeOffRequest> findByOrderByUpdatedDesc();
List<TimeOffRequest> findByEmployeeId(UUID idEmployee);
Optional<TimeOffRequest> findByEmployeeIdAndState(UUID employeeId, TimeOffRequestStatus state);
List<TimeOffRequest> findByEmployeeIdAndCategory(UUID employeeId, TimeOffRequestType category);
List<TimeOffRequest> findByState(TimeOffRequestStatus state);
void deleteByEmployeeIdAndCategory(UUID employeeId, TimeOffRequestType category);
}

View File

@ -1,12 +0,0 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.TimesheetEntry;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
public interface TimesheetEntryRepository extends JpaRepository<TimesheetEntry, UUID> {
List<TimesheetEntry> findByDateBetween(LocalDate from, LocalDate to);
}

View File

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

View File

@ -0,0 +1,199 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.repositories.AssessmentRepository;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import lombok.Data;
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
@Data
@Slf4j
public class AssessmentService {
private final AssessmentRepository assessmentRepository;
private final EntityManager entityManager;
private final JavaMailSender emailSender;
public Assessment createOrUpdate(final Assessment assessment) {
final Assessment saved = assessmentRepository.save(assessment);
return saved;
}
public void sendEmail(final Assessment assessment) {
try {
final String evaluationLink = String.format("https://careers.primefactorsolutions.com/evaluation/%s",
assessment.getId());
final SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("no-reply@primefactorsolutions.com");
message.setBcc("no-reply@primefactorsolutions.com");
message.setTo(assessment.getCandidate().getEmail());
message.setSubject("PFS - Evaluacion Tecnica");
message.setText(String.format("Estimado candidato,\n\nGracias por su candidatura. En esta etapa del "
+ "proceso de seleccion, usted debe completar "
+ "una evaluacion tecnica de programacion en JAVA. La prueba tiene una duracion de 30 minutos y "
+ "puede completarla cuando tenga una buena "
+ "conexion de internet.\n\n"
+ "Haga click aca: " + evaluationLink + "\n\n"
+ "Exito!"));
emailSender.send(message);
log.info("Sent email to {}", assessment.getCandidate().getEmail());
} catch (Exception e) {
log.error("Error sending email to {}", assessment.getCandidate().getEmail(), e);
throw e;
}
}
public List<Assessment> getAssessments() {
return assessmentRepository.findAll();
}
public Assessment getAssessment(final UUID id) {
return assessmentRepository.findById(id).orElse(null);
}
public Assessment startAssessment(final UUID id) {
final Assessment assessment = assessmentRepository.findById(id).get();
if (assessment.isStarted()) {
return assessment;
}
assessment.getAssessmentEvents().add(new AssessmentEvent(Instant.now(), AssessmentStatus.STARTED));
return assessmentRepository.save(assessment);
}
public Submission getNextSubmission(final UUID assessmentId, final UUID currSubmissionId) {
return getNextSubmission(assessmentId, currSubmissionId, true);
}
public Submission getNextSubmission(final UUID assessmentId, final UUID currSubmissionId,
final boolean checkCompleted) {
final Assessment assessment = assessmentRepository.findById(assessmentId).get();
Assessment saved;
if (currSubmissionId == null) {
if (checkCompleted && assessment.isCompleted()) {
return null;
}
final Question firstQuestion = assessment.getQuestions().stream().findFirst().get();
final Optional<Submission> submissionToReturn = assessment.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(firstQuestion))
.findFirst();
if (submissionToReturn.isEmpty()) {
final Submission result = new Submission(firstQuestion, firstQuestion.getContent(), Map.of(),
SubmissionStatus.FAIL, assessment);
assessment.getSubmissions().add(result);
}
saved = assessmentRepository.save(assessment);
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(firstQuestion))
.findFirst();
return submissionToReturn2.get();
}
final Submission currSubmission = assessment.getSubmissions().stream()
.filter(s -> s.getId().equals(currSubmissionId))
.findFirst().get();
final Question currQuestion = currSubmission.getQuestion();
int idx = assessment.getQuestions().indexOf(currQuestion);
if (idx == assessment.getQuestions().size() - 1) {
return null;
}
final Question nextQuestion = assessment.getQuestions().get(idx + 1);
final Optional<Submission> submissionToReturn = assessment.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(nextQuestion))
.findFirst();
if (submissionToReturn.isEmpty()) {
final Submission result = new Submission(nextQuestion, nextQuestion.getContent(), Map.of(),
SubmissionStatus.FAIL, assessment);
assessment.getSubmissions().add(result);
}
saved = assessmentRepository.save(assessment);
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(nextQuestion))
.findFirst();
return submissionToReturn2.get();
}
public Submission getPrevSubmission(final UUID assessmentId, final Submission currSubmission) {
if (currSubmission == null) {
return null;
}
final Question currQuestion = currSubmission.getQuestion();
Assessment assessment = assessmentRepository.findById(assessmentId).get();
int idx = assessment.getQuestions().indexOf(currQuestion);
if (idx == 0) {
return null;
}
final Question prevQuestion = assessment.getQuestions().get(idx - 1);
return assessment.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(prevQuestion))
.findFirst().orElseThrow(() -> new IllegalStateException("submission invalid"));
}
public Assessment completeAssessment(final UUID id) {
Assessment assessment = assessmentRepository.findById(id).get();
Optional<AssessmentEvent> completed = assessment.getAssessmentEvents().stream()
.filter(e -> e.getStatus() == AssessmentStatus.COMPLETED)
.findFirst();
if (completed.isPresent()) {
return assessment;
}
assessment.getAssessmentEvents().add(new AssessmentEvent(Instant.now(), AssessmentStatus.COMPLETED));
Assessment saved = assessmentRepository.save(assessment);
return saved;
}
public void saveSubmission(final UUID id, final Submission currSubmission) {
Assessment assessment = assessmentRepository.findById(id).get();
final Submission submission = assessment.getSubmissions().stream()
.filter(s -> s.getId().equals(currSubmission.getId()))
.findFirst().get();
submission.setResponse(currSubmission.getResponse());
Assessment saved = assessmentRepository.save(assessment);
}
@Transactional
public Assessment saveAssessment(final Assessment assessment) {
Candidate merged = entityManager.merge(assessment.getCandidate());
List<Question> mergedQuestions = assessment.getQuestions().stream().map(entityManager::merge)
.collect(Collectors.toList());
assessment.setCandidate(merged);
assessment.setQuestions(mergedQuestions);
return assessmentRepository.save(assessment);
}
}

View File

@ -2,19 +2,19 @@ package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Candidate;
import com.primefactorsolutions.repositories.CandidateRepository;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
@AllArgsConstructor
@Data
public class CandidateService {
private final CandidateRepository candidateRepository;
public Candidate createOrUpdate(final Candidate candidate) {
final Candidate saved = candidateRepository.save(candidate);
public Candidate createOrUpdate(final Candidate assessment) {
final Candidate saved = candidateRepository.save(assessment);
return saved;
}

View File

@ -1,78 +0,0 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Document;
import com.primefactorsolutions.model.DocumentType;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.repositories.DocumentRepository;
import lombok.AllArgsConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
public class DocumentService {
private final DocumentRepository documentRepository;
public void saveDocument(final Document newDocument) {
documentRepository.save(newDocument);
}
public void deleteDocument(final UUID id) {
documentRepository.deleteById(id);
}
public List<Document> getAllDocuments() {
return documentRepository.findAll();
}
public Document getDocument(final UUID id) {
Optional<Document> employee = documentRepository.findById(id);
return employee.orElse(null);
}
public List<Document> findDocuments(
final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<Document> documents = documentRepository.findAll();
int end = Math.min(start + pageSize, documents.size());
documents.sort(new BeanComparator<>(sortProperty));
if (!asc) {
Collections.reverse(documents);
}
return documents.subList(start, end);
}
public List<Document> findDocuments(final int start, final int pageSize) {
List<Document> employees = documentRepository.findAll();
int end = Math.min(start + pageSize, employees.size());
return employees.subList(start, end);
}
public List<Document> findDocumentBy(final DocumentType documentType,
final Employee employee,
final int start,
final int pageSize) {
List<Document> documents = documentRepository.findAll();
if (documentType != null) {
documents = documents.stream()
.filter(doc -> doc.getDocumentType().equals(documentType))
.collect(Collectors.toList());
}
if (employee != null) {
documents = documents.stream()
.filter(doc -> doc.getEmployee().equals(employee))
.collect(Collectors.toList());
}
int end = Math.min(start + pageSize, documents.size());
return documents.subList(start, end);
}
}

View File

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

View File

@ -1,72 +1,28 @@
package com.primefactorsolutions.service;
import com.google.common.base.Strings;
import com.primefactorsolutions.model.Employee;
import jakarta.persistence.EntityManager;
import lombok.AllArgsConstructor;
import org.apache.commons.beanutils.BeanComparator;
import com.primefactorsolutions.repositories.EmployeeRepository;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.support.LdapNameBuilder;
import lombok.Data;
import org.springframework.stereotype.Service;
import javax.naming.Name;
import javax.naming.directory.*;
import java.util.List;
import java.util.UUID;
import java.util.Optional;
import java.util.Collections;
@Service
@AllArgsConstructor
@Data
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;
protected Name buildDn(final Employee employee) {
return LdapNameBuilder.newInstance(BASE_DN)
.add("ou", "users")
.add("uid", employee.getUsername())
.build();
}
public List<Employee> getEmployees() {
return employeeRepository.findAll();
}
public Employee getDetachedEmployeeByUsername(final String username) {
final Employee employee = employeeRepository.findByUsername(username).orElse(null);
if (employee != null) {
entityManager.detach(employee);
return employee;
}
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 EmployeeService(final EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
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,62 +36,17 @@ 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);
// ldapClient.bind(dn).attributes(buildAttributes(employee)).execute();
ldapTemplate.bind(dn, null, buildAttributes(employee));
}
return employeeRepository.save(employee);
}
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);
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));
return attrs;
}
public void updatePassword(final Employee employee, final String newPassword) {
final Attribute attr = new BasicAttribute(USERPASSWORD, newPassword);
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);
}
}

View File

@ -1,27 +0,0 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Evaluation;
import com.primefactorsolutions.repositories.EvaluationRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
@AllArgsConstructor
public class EvaluationService {
private final EvaluationRepository evaluationRepository;
public Evaluation createOrUpdate(final Evaluation candidate) {
return evaluationRepository.save(candidate);
}
public List<Evaluation> getEvaluations() {
return evaluationRepository.findAll();
}
public Evaluation getEvaluation(final UUID id) {
return evaluationRepository.findById(id).orElse(null);
}
}

View File

@ -1,197 +0,0 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.repositories.ExamRepository;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
@Slf4j
public class ExamService {
private final ExamRepository examRepository;
private final EntityManager entityManager;
private final JavaMailSender emailSender;
public Exam createOrUpdate(final Exam exam) {
return examRepository.save(exam);
}
public void sendEmail(final Exam exam) {
try {
final String evaluationLink = String.format("https://careers.primefactorsolutions.com/candidate-exam/%s",
exam.getId());
final SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("no-reply@primefactorsolutions.com");
message.setBcc("no-reply@primefactorsolutions.com");
message.setTo(exam.getCandidate().getEmail());
message.setSubject("PFS - Evaluacion Tecnica");
message.setText(String.format("Estimado candidato,\n\nGracias por su candidatura. En esta etapa del "
+ "proceso de seleccion, usted debe completar "
+ "una evaluacion tecnica de programacion en JAVA. La prueba tiene una duracion de 30 minutos y "
+ "puede completarla cuando tenga una buena "
+ "conexion de internet.\n\n"
+ "Haga click aca: " + evaluationLink + "\n\n"
+ "Exito!"));
emailSender.send(message);
log.info("Sent email to {}", exam.getCandidate().getEmail());
} catch (Exception e) {
log.error("Error sending email to {}", exam.getCandidate().getEmail(), e);
throw e;
}
}
public List<Exam> getExams() {
return examRepository.findAll();
}
public Exam getExam(final UUID id) {
return examRepository.findById(id).orElse(null);
}
public Exam startExam(final UUID id) {
final Exam exam = examRepository.findById(id).get();
if (exam.isStarted()) {
return exam;
}
exam.getExamEvents().add(new ExamEvent(Instant.now(), ExamStatus.STARTED));
return examRepository.save(exam);
}
public Submission getNextSubmission(final UUID examId, final UUID currSubmissionId) {
return getNextSubmission(examId, currSubmissionId, true);
}
public Submission getNextSubmission(final UUID examId, final UUID currSubmissionId,
final boolean checkCompleted) {
final Exam exam = examRepository.findById(examId).get();
Exam saved;
if (currSubmissionId == null) {
if (checkCompleted && exam.isCompleted()) {
return null;
}
final Question firstQuestion = exam.getQuestions().stream().findFirst().get();
final Optional<Submission> submissionToReturn = exam.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(firstQuestion))
.findFirst();
if (submissionToReturn.isEmpty()) {
final Submission result = new Submission(firstQuestion, firstQuestion.getContent(), null,
SubmissionStatus.FAIL, exam);
exam.getSubmissions().add(result);
}
saved = examRepository.save(exam);
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(firstQuestion))
.findFirst();
return submissionToReturn2.get();
}
final Submission currSubmission = exam.getSubmissions().stream()
.filter(s -> s.getId().equals(currSubmissionId))
.findFirst().get();
final Question currQuestion = currSubmission.getQuestion();
int idx = exam.getQuestions().indexOf(currQuestion);
if (idx == exam.getQuestions().size() - 1) {
return null;
}
final Question nextQuestion = exam.getQuestions().get(idx + 1);
final Optional<Submission> submissionToReturn = exam.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(nextQuestion))
.findFirst();
if (submissionToReturn.isEmpty()) {
final Submission result = new Submission(nextQuestion, nextQuestion.getContent(), null,
SubmissionStatus.FAIL, exam);
exam.getSubmissions().add(result);
}
saved = examRepository.save(exam);
final Optional<Submission> submissionToReturn2 = saved.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(nextQuestion))
.findFirst();
return submissionToReturn2.get();
}
public Submission getPrevSubmission(final UUID examId, final Submission currSubmission) {
if (currSubmission == null) {
return null;
}
final Question currQuestion = currSubmission.getQuestion();
Exam exam = examRepository.findById(examId).get();
int idx = exam.getQuestions().indexOf(currQuestion);
if (idx == 0) {
return null;
}
final Question prevQuestion = exam.getQuestions().get(idx - 1);
return exam.getSubmissions().stream()
.filter(s -> s.getQuestion().equals(prevQuestion))
.findFirst().orElseThrow(() -> new IllegalStateException("submission invalid"));
}
public Exam completeExam(final UUID id) {
Exam exam = examRepository.findById(id).get();
Optional<ExamEvent> completed = exam.getExamEvents().stream()
.filter(e -> e.getStatus() == ExamStatus.COMPLETED)
.findFirst();
if (completed.isPresent()) {
return exam;
}
exam.getExamEvents().add(new ExamEvent(Instant.now(), ExamStatus.COMPLETED));
Exam saved = examRepository.save(exam);
return saved;
}
public void saveSubmission(final UUID id, final Submission currSubmission) {
Exam exam = examRepository.findById(id).get();
final Submission submission = exam.getSubmissions().stream()
.filter(s -> s.getId().equals(currSubmission.getId()))
.findFirst().get();
submission.setText(currSubmission.getText());
Exam saved = examRepository.save(exam);
}
@Transactional
public Exam saveExam(final Exam exam) {
Candidate merged = entityManager.merge(exam.getCandidate());
List<Question> mergedQuestions = exam.getQuestions().stream().map(entityManager::merge)
.collect(Collectors.toList());
exam.setCandidate(merged);
exam.setQuestions(mergedQuestions);
return examRepository.save(exam);
}
}

View File

@ -2,14 +2,14 @@ package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Question;
import com.primefactorsolutions.repositories.QuestionRepository;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
@AllArgsConstructor
@Data
public class QuestionService {
private final QuestionRepository questionRepository;

View File

@ -1,250 +0,0 @@
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;
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) {
try (var os = new ByteArrayOutputStream()) {
writeAsPdf(reportName, model, os);
return os.toByteArray();
}
}
@SneakyThrows
public void writeAsPdf(final String reportName, final Object model, final OutputStream out) {
var in = getTemplate(reportName);
final Configuration cfg = getConfiguration();
final Reader reader = new InputStreamReader(in);
final Template temp = new Template(reportName, reader, cfg);
var wrapper = new DefaultObjectWrapper(Configuration.VERSION_2_3_32);
ByteArrayOutputStream oo = new ByteArrayOutputStream();
Writer outTemplate = new OutputStreamWriter(oo);
temp.process(wrapper.wrap(model), outTemplate);
var builder = new PdfRendererBuilder();
builder.usePDDocument(new PDDocument(MemoryUsageSetting.setupMixed(1000000)));
builder.withHtmlContent(oo.toString(StandardCharsets.UTF_8), "/test");
builder.toStream(out);
try (PdfBoxRenderer pdfBoxRenderer = builder.buildPdfRenderer()) {
pdfBoxRenderer.layout();
pdfBoxRenderer.createPDF();
}
}
public static InputStream getTemplate(final String reportName) {
return ReportService.class.getResourceAsStream(String.format("/reports/%s.html", reportName));
}
@NotNull
private static Configuration getConfiguration() {
final Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
cfg.setFallbackOnNullLoopVariable(false);
cfg.setSQLDateAndTimeTimeZone(TimeZone.getDefault());
return cfg;
}
public byte[] generateExcelReport(final Employee employee) {
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("Empleado");
Row titleRow = sheet.createRow(0);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue("Información General del Empleado");
CellStyle titleStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 16);
titleStyle.setFont(titleFont);
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleCell.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 40));
Row header = sheet.createRow(2);
String[] headers = {
"Username", "Nombres", "Apellidos", "Estado", "Género", "Fecha de Nacimiento", "Edad",
"Ciudad y País de Nacimiento", "Dirección de Residencia", "Departamento y Provincia de Residencia",
"Estado Civil", "Número de Hijos", "CI", "Expedido en", "Teléfono", "E-mail Personal",
"Teléfono Laboral",
"E-mail Laboral", "Nombres y Apellidos de Contacto", "Dirección de Contacto",
"Teléfono de Contacto",
"Email de Contacto", "Código de Empleado", "Cargo", "Equipo", "Lead/Manager", "Fecha de Ingreso",
"Fecha de Retiro", "Tipo de Contrato", "Tipo de Contrato Personalizado", "Antigüedad",
"Salario Total",
"Salario Básico", "Bono de Antigüedad", "Bono Profesional", "Banco", "Número de Cuenta",
"Código Único de Asegurado (GPSS)", "Matrícula de Asegurado (SSS)", "Derechohabiente 1",
"Derechohabiente 2"
};
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
headerStyle.setAlignment(HorizontalAlignment.CENTER);
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
for (int i = 0; i < headers.length; i++) {
Cell cell = header.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
Row dataRow = sheet.createRow(3); // Fila 3
String[] employeeData = {
employee.getUsername(), employee.getFirstName(), employee.getLastName(),
employee.getStatus().toString(),
employee.getGender().toString(), employee.getBirthday().toString(),
String.valueOf(employee.getAge()),
employee.getBirthCity(), employee.getResidenceAddress(), employee.getLocalAddress(),
employee.getMaritalStatus().toString(), String.valueOf(employee.getNumberOfChildren()),
employee.getCi(),
employee.getIssuedIn(), employee.getPhoneNumber(), employee.getPersonalEmail(),
employee.getPhoneNumberProfessional(), employee.getProfessionalEmail(),
employee.getEmergencyCName(),
employee.getEmergencyCAddress(), employee.getEmergencyCPhone(), employee.getEmergencyCEmail(),
employee.getCod(), employee.getPosition(), employee.getTeam().getName(), employee.getLeadManager(),
employee.getDateOfEntry().toString(), employee.getDateOfExit() != null ? employee.getDateOfExit()
.toString() : "",
employee.getContractType().toString(), employee.getCustomContractType(),
employee.getSeniority(), employee.getSalaryTotal().toString(), employee.getSalaryBasic().toString(),
employee.getTenureBonus().toString(), employee.getProfessionalBonus().toString(),
employee.getBankName(), employee.getAccountNumber(), employee.getGpss(), employee.getSss(),
employee.getBeneficiarie1(), employee.getBeneficiarie2()
};
for (int i = 0; i < employeeData.length; i++) {
dataRow.createCell(i).setCellValue(employeeData[i] != null ? employeeData[i] : "");
}
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
workbook.write(outputStream);
return outputStream.toByteArray();
}
} catch (IOException e) {
throw new RuntimeException("Error al generar el reporte Excel", e);
}
}
}

View File

@ -1,27 +0,0 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.repositories.TeamRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
@AllArgsConstructor
public class TeamService {
private final TeamRepository teamRepository;
public void saveTeam(final Team newTeam) {
teamRepository.save(newTeam);
}
public void deleteTeam(final UUID id) {
teamRepository.deleteById(id);
}
public List<Team> findAllTeams() {
return teamRepository.findAll();
}
}

View File

@ -1,88 +0,0 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.*;
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;
@Service
@AllArgsConstructor
public class TimeOffRequestService {
private final TimeOffRequestRepository timeOffRequestRepository;
public void saveTimeOffRequest(final TimeOffRequest newTimeOffRequest) {
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();
}
public TimeOffRequest findTimeOffRequest(final UUID id) {
Optional<TimeOffRequest> timeOffRequest = timeOffRequestRepository.findById(id);
return timeOffRequest.orElse(null);
}
public List<TimeOffRequest> findRequestsByState(final TimeOffRequestStatus state) {
return timeOffRequestRepository.findByState(state);
}
public List<TimeOffRequest> findRequestsByEmployeeId(final UUID idEmployee) {
return timeOffRequestRepository.findByEmployeeId(idEmployee);
}
public Optional<TimeOffRequest> findByEmployeeAndState(final UUID employeeId, final TimeOffRequestStatus state) {
return timeOffRequestRepository.findByEmployeeIdAndState(employeeId, state);
}
public List<TimeOffRequest> findByEmployeeAndCategory(final UUID employeeId, final TimeOffRequestType category) {
return timeOffRequestRepository.findByEmployeeIdAndCategory(employeeId, category);
}
public void updateRequestStatuses() {
List<TimeOffRequest> requests = findAllTimeOffRequests();
LocalDate now = LocalDate.now();
LocalDate startOfYear = LocalDate.of(now.getYear(), 1, 1);
for (TimeOffRequest request : requests) {
if (request.getCategory() == TimeOffRequestType.VACACION_GESTION_ACTUAL && now.isEqual(startOfYear)) {
deleteTimeOffRequestByEmployeeAndCategory(
request.getEmployee().getId(),
TimeOffRequestType.VACACION_GESTION_ANTERIOR
);
request.setCategory(TimeOffRequestType.VACACION_GESTION_ANTERIOR);
}
if (request.getState() == TimeOffRequestStatus.APROBADO
|| request.getState() == TimeOffRequestStatus.EN_USO) {
LocalDate startDate = request.getStartDate();
LocalDate endDate = request.getEndDate();
if (now.isAfter(endDate)) {
request.setState(TimeOffRequestStatus.TOMADO);
} else if (now.isEqual(startDate) || (now.isAfter(startDate) && now.isBefore(endDate))) {
request.setState(TimeOffRequestStatus.EN_USO);
}
}
}
saveAll(requests);
}
}

View File

@ -1,39 +0,0 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.TimeOffRequestType;
import com.primefactorsolutions.model.TimeOff;
import com.primefactorsolutions.repositories.TimeOffRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
@Service
@AllArgsConstructor
public class TimeOffService {
private final TimeOffRepository timeOffRepository;
public TimeOff getTimeOff(final UUID id) {
return timeOffRepository.findById(id).orElse(null);
}
public void saveTimeOff(final TimeOff timeOff) {
timeOffRepository.save(timeOff);
}
public TimeOff findVacationByCategory(final TimeOffRequestType category) {
return timeOffRepository.findByCategory(category);
}
public List<TimeOff> findVacations() {
return timeOffRepository.findAll();
}
public List<TimeOff> findTimeOffs(final Integer year) {
final LocalDate from = LocalDate.of(year, 1, 1);
final LocalDate to = LocalDate.of(year, 12, 31);
return timeOffRepository.findByDateBetween(from, to);
}
}

View File

@ -1,37 +0,0 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.TimesheetEntry;
import com.primefactorsolutions.repositories.TimesheetEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.*;
@Service
public class TimesheetService {
private final TimesheetEntryRepository timesheetEntryRepository;
@Autowired
public TimesheetService(final TimesheetEntryRepository timesheetEntryRepository) {
this.timesheetEntryRepository = timesheetEntryRepository;
}
public List<TimesheetEntry> findAll() {
return timesheetEntryRepository.findAll();
}
public TimesheetEntry save(final TimesheetEntry timesheetEntry) {
return timesheetEntryRepository.save(timesheetEntry);
}
public TimesheetEntry getTimesheetEntry(final UUID id) {
final Optional<TimesheetEntry> hoursWorked = timesheetEntryRepository.findById(id);
return hoursWorked.orElse(null);
}
public List<TimesheetEntry> findListHoursWorkedEmployee(final LocalDate from,
final LocalDate to) {
return timesheetEntryRepository.findByDateBetween(from, to);
}
}

View File

@ -1,13 +1,11 @@
package com.primefactorsolutions.views.assessment;
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Candidate;
import com.primefactorsolutions.model.Exam;
import com.primefactorsolutions.model.Assessment;
import com.primefactorsolutions.model.Question;
import com.primefactorsolutions.service.ExamService;
import com.primefactorsolutions.service.AssessmentService;
import com.primefactorsolutions.service.QuestionService;
import com.primefactorsolutions.service.CandidateService;
import com.primefactorsolutions.views.BaseEntityForm;
import com.primefactorsolutions.views.MainLayout;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ItemLabelGenerator;
import com.vaadin.flow.component.combobox.ComboBox;
@ -17,34 +15,32 @@ import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import jakarta.annotation.security.PermitAll;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.fields.SubListSelector;
import org.vaadin.firitin.form.BeanValidationForm;
import java.util.List;
import java.util.UUID;
@SpringComponent
@RolesAllowed("ROLE_ADMIN")
@PermitAll
@Scope("prototype")
@PageTitle("Exams")
@Route(value = "/exams", layout = MainLayout.class)
@PageTitle("Assessments")
@Route(value = "/assessments", layout = MainLayout.class)
@Uses(ComboBox.class)
public class ExamView extends BaseEntityForm<Exam> implements HasUrlParameter<String> {
private final ExamService examService;
public class AssessmentView extends BeanValidationForm<Assessment> implements HasUrlParameter<String> {
private final AssessmentService assessmentService;
private final ComboBox<Candidate> candidate;
private final SubListSelector<Question> questions;
private ComboBox<Candidate> candidate = null;
private SubListSelector<Question> questions = null;
public ExamView(final AuthenticationContext authorizationContext,
final ExamService examService,
final QuestionService questionService,
final CandidateService candidateService) {
super(authorizationContext, Exam.class);
public AssessmentView(final AssessmentService assessmentService, final QuestionService questionService,
final CandidateService candidateService) {
super(Assessment.class);
this.examService = examService;
this.assessmentService = assessmentService;
candidate = new ComboBox<>("Candidate", candidateService.getCandidates());
candidate.setItemLabelGenerator((ItemLabelGenerator<Candidate>) Candidate::getEmail);
@ -55,20 +51,20 @@ public class ExamView extends BaseEntityForm<Exam> implements HasUrlParameter<St
questions.setReadOnly(false);
questions.setAvailableOptions(questionService.getQuestions());
setSavedHandler((SavedHandler<Exam>) exam -> {
this.examService.saveExam(exam);
goTo(ExamsListView.class);
setSavedHandler((SavedHandler<Assessment>) assessment -> {
final var saved = this.assessmentService.saveAssessment(assessment);
setEntityWithEnabledSave(saved);
});
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String s) {
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
final var exam = examService.getExam(UUID.fromString(s));
final var assessment = assessmentService.getAssessment(UUID.fromString(s));
setEntityWithEnabledSave(exam);
setEntityWithEnabledSave(assessment);
} else {
setEntityWithEnabledSave(new Exam());
setEntityWithEnabledSave(new Assessment());
}
}

View File

@ -0,0 +1,127 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Assessment;
import com.primefactorsolutions.service.AssessmentService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.DataProviderListener;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.addon.stefan.clipboard.ClientsideClipboard;
import org.vaadin.firitin.components.grid.VGrid;
import java.util.stream.Stream;
@SpringComponent
@Scope("prototype")
@PageTitle("Assessments")
@Route(value = "/assessments", layout = MainLayout.class)
@PermitAll
public class AssessmentsListView extends Main {
public AssessmentsListView(final AssessmentService assessmentService) {
final HorizontalLayout hl = new HorizontalLayout();
final Button addAssessment = new Button("Add Assessment");
addAssessment.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(AssessmentView.class, "new");
});
hl.add(addAssessment);
final VGrid<Assessment> grid = new VGrid<>(Assessment.class);
grid.setColumns("id", "candidate.email");
final Grid.Column<Assessment> statusColumn = grid.addColumn((ValueProvider<Assessment, Object>) assessment ->
assessment.getAssessmentEvents().isEmpty()
? "N/A"
: assessment.getAssessmentEvents().getLast().getStatus().name());
statusColumn.setHeader("Status");
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> {
var result = new Button("Result", event ->
this.getUI().get().navigate(SubmissionView.class, assessment.getId().toString())
);
result.setEnabled(assessment.isCompleted());
return result;
});
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> new Button("Copy Link", event ->
ClientsideClipboard.writeToClipboard(
String.format("email: %s link: https://careers.primefactorsolutions.com/evaluation/%s",
assessment.getCandidate().getEmail(),
assessment.getId()))
));
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment ->
new Button("Send Email", event -> {
ConfirmDialog dialog = new ConfirmDialog();
dialog.setHeader("Send Link Email");
dialog.setText(String.format("Enviar link por email al candidato %s?",
assessment.getCandidate().getEmail()));
dialog.setCancelable(true);
dialog.setConfirmText("Enviar");
dialog.setConfirmButtonTheme("primary");
dialog.addConfirmListener((ComponentEventListener<ConfirmDialog.ConfirmEvent>) confirmEvent -> {
try {
assessmentService.sendEmail(assessment);
} catch (Exception e) {
Notification.show("Error sending email: " + e.getMessage(), 10_000,
Notification.Position.TOP_CENTER);
}
});
dialog.open();
}));
grid.setDataProvider(new DataProvider<>() {
@Override
public boolean isInMemory() {
return false;
}
@Override
public int size(final Query<Assessment, Object> query) {
return assessmentService.getAssessments().size();
}
@Override
public Stream<Assessment> fetch(final Query<Assessment, Object> query) {
int limit = query.getLimit();
int pagerSize = query.getPageSize();
int page = query.getPage();
return assessmentService.getAssessments().stream();
}
@Override
public void refreshItem(final Assessment assessment) {
// no-op
}
@Override
public void refreshAll() {
}
@Override
public Registration addDataProviderListener(final DataProviderListener<Assessment> dataProviderListener) {
return null;
}
});
grid.setAllRowsVisible(true);
add(hl, grid);
}
}

View File

@ -1,76 +0,0 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.views.util.AuthUtils;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.spring.security.AuthenticationContext;
import org.vaadin.firitin.form.BeanValidationForm;
import java.io.Serializable;
import java.util.Optional;
import java.util.UUID;
@SuppressWarnings("deprecation")
public abstract class BaseEntityForm<T> extends BeanValidationForm<T> {
private final AuthenticationContext authenticationContext;
private Button editButton;
private EditHandler<T> editHandler;
public BaseEntityForm(final AuthenticationContext authenticationContext, final Class<T> entityType) {
super(entityType);
this.authenticationContext = authenticationContext;
}
@Override
public HorizontalLayout getToolbar() {
return new HorizontalLayout(getCancelButton(), getEditButton(), getSaveButton());
}
public Button getEditButton() {
if (editButton == null) {
editButton = new Button("Edit");
editButton.setEnabled(false);
editButton.addClickListener(__ -> {
if (editHandler != null) {
editHandler.onEdit(getEntity());
}
});
}
return editButton;
}
public void setEditHandler(final EditHandler<T> editHandler) {
this.editHandler = editHandler;
getEditButton().setEnabled(editHandler != null);
}
protected <V extends Component> void goTo(final Class<V> view) {
getUI().ifPresent(ui -> ui.navigate(view));
}
protected Button getCancelButton() {
final Button cancelButton = new Button("Cancel");
cancelButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
UI.getCurrent().getPage().getHistory().back());
return cancelButton;
}
protected boolean isRoleAdmin() {
return AuthUtils.isAdmin(this.authenticationContext);
}
protected Optional<UUID> getEmployeeId() {
return AuthUtils.getEmployeeId(this.authenticationContext);
}
public interface EditHandler<T> extends Serializable {
void onEdit(T entity);
}
}

View File

@ -1,31 +0,0 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.views.util.AuthUtils;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.spring.security.AuthenticationContext;
import lombok.Getter;
import java.util.Optional;
import java.util.UUID;
@Getter
public abstract class BaseView extends Main {
private final VerticalLayout currentPageLayout;
private final AuthenticationContext authenticationContext;
public BaseView(final AuthenticationContext authenticationContext) {
this.authenticationContext = authenticationContext;
currentPageLayout = new VerticalLayout();
add(currentPageLayout);
}
protected boolean isRoleAdmin() {
return AuthUtils.isAdmin(this.authenticationContext);
}
protected Optional<UUID> getEmployeeId() {
return AuthUtils.getEmployeeId(this.authenticationContext);
}
}

View File

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

View File

@ -1,9 +1,7 @@
package com.primefactorsolutions.views.assessment;
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Candidate;
import com.primefactorsolutions.service.CandidateService;
import com.primefactorsolutions.views.BaseEntityForm;
import com.primefactorsolutions.views.MainLayout;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.router.BeforeEvent;
@ -11,35 +9,34 @@ import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import jakarta.annotation.security.PermitAll;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.form.BeanValidationForm;
import java.util.List;
import java.util.UUID;
@SpringComponent
@Scope("prototype")
@PageTitle("Candidates")
@PageTitle("Assessments")
@Route(value = "/candidates", layout = MainLayout.class)
@RolesAllowed("ROLE_ADMIN")
public class CandidateView extends BaseEntityForm<Candidate> implements HasUrlParameter<String> {
@PermitAll
public class CandidateView extends BeanValidationForm<Candidate> implements HasUrlParameter<String> {
private final CandidateService candidateService;
private EmailField email = null;
public CandidateView(final AuthenticationContext authenticationContext,
final CandidateService candidateService) {
super(authenticationContext, Candidate.class);
public CandidateView(final CandidateService candidateService) {
super(Candidate.class);
this.candidateService = candidateService;
email = new EmailField();
email.setWidthFull();
email.setLabel("Email");
setSavedHandler((SavedHandler<Candidate>) candidate -> {
candidateService.createOrUpdate(candidate);
goTo(CandidatesListView.class);
final Candidate saved = candidateService.createOrUpdate(candidate);
setEntityWithEnabledSave(saved);
});
}

View File

@ -0,0 +1,90 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Candidate;
import com.primefactorsolutions.service.CandidateService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.DataProviderListener;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.VGrid;
import java.util.stream.Stream;
@SpringComponent
@Scope("prototype")
@PageTitle("Candidates")
@Route(value = "/candidates", layout = MainLayout.class)
@PermitAll
public class CandidatesListView extends Main {
private final CandidateService candidateService;
public CandidatesListView(final CandidateService candidateService) {
this.candidateService = candidateService;
final HorizontalLayout hl = new HorizontalLayout();
final Button addCandidate = new Button("Add Candidate");
addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(CandidateView.class, "new");
});
hl.add(addCandidate);
final VGrid<Candidate> grid = new VGrid<>(Candidate.class);
grid.setColumns("id", "email");
grid.setAllRowsVisible(true);
grid.addComponentColumn((ValueProvider<Candidate, Component>) candidate -> {
final Button edit = new Button("Edit");
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().get().navigate(CandidateView.class, candidate.getId().toString()));
return edit;
});
grid.setDataProvider(new DataProvider<>() {
@Override
public boolean isInMemory() {
return false;
}
@Override
public int size(final Query<Candidate, Object> query) {
return candidateService.getCandidates().size();
}
@Override
public Stream<Candidate> fetch(final Query<Candidate, Object> query) {
int limit = query.getLimit();
int pagerSize = query.getPageSize();
int page = query.getPage();
return candidateService.getCandidates().stream();
}
@Override
public void refreshItem(final Candidate candidate) {
// no-op
}
@Override
public void refreshAll() {
// no-op
}
@Override
public Registration addDataProviderListener(final DataProviderListener<Candidate> dataProviderListener) {
return null;
}
});
add(hl, grid);
}
}

View File

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

View File

@ -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("Documents")
@Route(value = "/documents/me", layout = MainLayout.class)
public class DocumentsView extends Main {
}

View File

@ -0,0 +1,214 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
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.H2;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
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.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.datepicker.VDatePicker;
import org.vaadin.firitin.form.BeanValidationForm;
import java.util.List;
import java.util.UUID;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Employee")
@Route(value = "/employees", layout = MainLayout.class)
public class EmployeeView extends BeanValidationForm<Employee> implements HasUrlParameter<String> {
private final EmployeeService employeeService;
private final TextField firstName;
private final TextField lastName;
private final ComboBox<String> status;
private final VDatePicker birthday;
private final TextField birthCity;
private final ComboBox<String> maritalStatus;
private final TextField residenceAddress;
private final TextField phoneNumber;
private final EmailField personalEmail;
private final TextField emergencyCName;
private final TextField emergencyCAddress;
private final TextField emergencyCPhone;
private final EmailField emergencyCEmail;
private final H2 mt;
private final H3 fs;
private final H3 ss;
public EmployeeView(final EmployeeService employeeService) {
super(Employee.class);
this.employeeService = employeeService;
mt = new H2("Información General del Empleado");
fs = new H3("Información Personal");
ss = new H3("Datos de Contacto de Emergencia");
final HorizontalLayout mainLayout = new HorizontalLayout();
final VerticalLayout sidebar = createSidebar();
final VerticalLayout contentLayout = createContentLayout();
final VerticalLayout contentLayout2 = createContentLayout();
firstName = new TextField("Nombres: ");
firstName.setWidthFull();
firstName.setMaxLength(30);
firstName.setRequired(true);
lastName = new TextField("Apellidos");
lastName.setWidthFull();
lastName.setMaxLength(30);
lastName.setRequired(true);
status = new ComboBox<>("Estado");
status.setItems(List.of("ACTIVE", "INACTIVE"));
lastName.setWidthFull();
lastName.setMaxLength(30);
status.setRequired(true);
birthday = new VDatePicker("Fecha de Nacimiento");
birthday.setWidthFull();
birthCity = new TextField("Ciudad y País de Nacimiento");
birthCity.setWidthFull();
birthCity.setMaxLength(20);
maritalStatus = new ComboBox<>("Estado Civil");
maritalStatus.setItems(List.of("Soltero", "Casado", "Viudo", "Divorciado"));
maritalStatus.setWidthFull();
residenceAddress = new TextField("Dirección de Residencia");
residenceAddress.setWidthFull();
residenceAddress.setMaxLength(50);
phoneNumber = new TextField("Teléfono");
phoneNumber.setWidthFull();
phoneNumber.setMaxLength(8);
phoneNumber.setValueChangeMode(ValueChangeMode.EAGER);
phoneNumber.addValueChangeListener(e -> {
if (!e.getValue().matches("\\d*")) {
phoneNumber.setErrorMessage("El teléfono debe contener solo números.");
}
});
personalEmail = new EmailField("E-mail");
personalEmail.setWidthFull();
personalEmail.setMaxLength(30);
emergencyCName = new TextField("Nombres y Apellidos de Contacto");
emergencyCName.setWidthFull();
emergencyCName.setMaxLength(50);
emergencyCAddress = new TextField("Dirección de Contacto");
emergencyCAddress.setWidthFull();
emergencyCAddress.setMaxLength(50);
emergencyCPhone = new TextField("Teléfono de Contacto");
emergencyCPhone.setWidthFull();
emergencyCPhone.setMaxLength(8);
emergencyCPhone.setValueChangeMode(ValueChangeMode.EAGER);
emergencyCPhone.addValueChangeListener(e -> {
if (!e.getValue().matches("\\d*")) {
emergencyCPhone.setErrorMessage("El teléfono debe contener solo números.");
}
});
emergencyCEmail = new EmailField("Email de Contacto");
emergencyCEmail.setWidthFull();
emergencyCEmail.setMaxLength(30);
contentLayout.add(
mt, fs, firstName, lastName, status, birthday, birthCity, maritalStatus,
residenceAddress, phoneNumber, personalEmail);
contentLayout2.add(
ss, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail);
setSavedHandler((SavedHandler<Employee>) employee -> {
if (validateForm()) {
final Employee saved = employeeService.createOrUpdate(employee);
Notification.show("Employee saved successfully.");
getUI().ifPresent(ui -> ui.navigate(EmployeesListView.class));
setEntityWithEnabledSave(saved);
} else {
Notification.show("Please complete the required fields correctly.", 3000, Notification.Position.MIDDLE);
}
});
mainLayout.add(sidebar, contentLayout, contentLayout2);
addClassName("main-layout");
}
private boolean validateForm() {
return !firstName.isEmpty() && !lastName.isEmpty() && status.getValue() != null;
}
private VerticalLayout createSidebar() {
VerticalLayout sidebar = new VerticalLayout();
sidebar.setWidth("250px");
sidebar.add(new Button("Información General", e -> navigateToSection("Información General")));
sidebar.add(new Button("Detalles Profesionales", e -> navigateToSection("Detalles Profesionales")));
return sidebar;
}
private void navigateToSection(final String section) {
Notification.show("Navigating to " + section);
}
private VerticalLayout createContentLayout() {
VerticalLayout contentLayout = new VerticalLayout();
contentLayout.setWidth("100%");
return contentLayout;
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String s) {
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
var employee = employeeService.getEmployee(UUID.fromString(s));
setEntityWithEnabledSave(employee);
} else {
setEntityWithEnabledSave(new Employee());
}
}
@Override
protected List<Component> getFormComponents() {
return List.of(
mt,
fs,
firstName,
lastName,
status,
birthday,
birthCity,
maritalStatus,
residenceAddress,
phoneNumber,
personalEmail,
ss,
emergencyCName,
emergencyCAddress,
emergencyCPhone,
emergencyCEmail
);
}
}

View File

@ -0,0 +1,109 @@
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("Edit", this::navigateToEditView);
setupPagingGrid();
}
private void updateEmployeeStatus(final Employee employee, final boolean isActive) {
employee.setStatus(isActive ? Employee.Status.ACTIVE : Employee.Status.INACTIVE);
employeeService.createOrUpdate(employee);
refreshGrid();
}
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()));
}
private void navigateToAddEmployeeView() {
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, "new"));
}
private void setupPagingGrid() {
table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
table.setPageSize(5);
}
private void refreshGrid() {
table.setPagingDataProvider((page, pageSize) -> fetchEmployees((int) page, pageSize));
}
private List<Employee> fetchEmployees(final int page, final int pageSize) {
int start = page * pageSize;
if (hasSortOrder()) {
return fetchSortedEmployees(start, pageSize);
}
return employeeService.findEmployees(start, pageSize);
}
private boolean hasSortOrder() {
return !table.getSortOrder().isEmpty();
}
private List<Employee> fetchSortedEmployees(final int start, final int pageSize) {
GridSortOrder<Employee> sortOrder = table.getSortOrder().getFirst();
return employeeService.findEmployees(start, pageSize,
sortOrder.getSorted().getKey(),
sortOrder.getDirection() == SortDirection.ASCENDING);
}
@FunctionalInterface
private interface ButtonClickHandler {
void handle(Employee employee);
}
}

View File

@ -5,7 +5,7 @@ import com.hilerio.ace.AceEditor;
import com.hilerio.ace.AceMode;
import com.hilerio.ace.AceTheme;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.ExamService;
import com.primefactorsolutions.service.AssessmentService;
import com.primefactorsolutions.service.CompilerService;
import com.vaadin.flow.component.*;
import com.vaadin.flow.component.button.Button;
@ -27,15 +27,19 @@ import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.theme.lumo.LumoUtility.Background;
import com.vaadin.flow.theme.lumo.LumoUtility.BoxSizing;
import com.vaadin.flow.theme.lumo.LumoUtility.Display;
import com.vaadin.flow.theme.lumo.LumoUtility.Flex;
import com.vaadin.flow.theme.lumo.LumoUtility.FlexDirection;
import com.vaadin.flow.theme.lumo.LumoUtility.FontSize;
import com.vaadin.flow.theme.lumo.LumoUtility.FontWeight;
import com.vaadin.flow.theme.lumo.LumoUtility.Gap;
import com.vaadin.flow.theme.lumo.LumoUtility.Height;
import com.vaadin.flow.theme.lumo.LumoUtility.Margin;
import com.vaadin.flow.theme.lumo.LumoUtility.Overflow;
import com.vaadin.flow.theme.lumo.LumoUtility.Padding;
import com.vaadin.flow.theme.lumo.LumoUtility.TextColor;
import io.overcoded.vaadin.panel.Panel;
import io.overcoded.vaadin.panel.PanelConfig;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Scope;
@ -45,23 +49,24 @@ import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@PageTitle("Evaluacion")
@SpringComponent
@Scope("prototype")
@Route(value = "/candidate-exam", layout = MainLayout.class)
@Route(value = "/evaluation", layout = MainLayout.class)
@AnonymousAllowed
@Slf4j
public class CandidateExamView extends Main implements HasUrlParameter<String> {
public class EvaluationView extends Main implements HasUrlParameter<String> {
private final CompilerService compilerService;
private final ExamService examService;
private final AssessmentService assessmentService;
private AceEditor questionEditor = null;
private Dialog dialog = null;
private Dialog completeDialog = null;
private AceEditor result = null;
private Exam exam = null;
private Assessment assessment = null;
private Submission currSubmission = null;
private Boolean isCompleted = false;
@ -69,48 +74,33 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
private MenuItem prev = null;
private MenuItem next = null;
private MenuItem reset = null;
private MenuItem finish = null;
private Panel candidatePanel = null;
private Section sidebar = null;
private SimpleTimer timer = null;
private DescriptionList dl = null;
private Section editorSection = null;
private Section startSection = null;
private Section completedSection = null;
private H3 questionTitle = null;
private Text questionDescription = null;
public CandidateExamView(final CompilerService compilerService, final ExamService examService) {
public EvaluationView(final CompilerService compilerService, final AssessmentService assessmentService) {
this.compilerService = compilerService;
this.examService = examService;
this.assessmentService = assessmentService;
addClassNames(Display.FLEX, Flex.GROW, Height.FULL);
initStartSection();
initCompletedSection();
initEditorSection();
initResultDialog();
initCompleteDialog();
initTimer(examService);
initCandidatePanel();
initSidebar();
VerticalLayout vl = new VerticalLayout();
vl.add(completedSection, candidatePanel, startSection, editorSection, dialog);
add(vl);
add(completedSection, startSection, editorSection, sidebar, dialog);
updateUI();
}
private void initTimer(final ExamService examService) {
timer = new SimpleTimer(0);
timer.setMinutes(true);
timer.addTimerEndEvent((ComponentEventListener<SimpleTimer.TimerEndedEvent>) timerEndedEvent -> {
Notification.show("Tiempo completado.", 5_000, Notification.Position.TOP_CENTER);
this.currSubmission.setText(this.questionEditor.getValue());
this.examService.saveSubmission(exam.getId(), this.currSubmission);
this.exam = examService.completeExam(exam.getId());
goToCompleted();
updateUI();
});
timer.setFractions(false);
}
private void initResultDialog() {
dialog = new Dialog();
dialog.setHeaderTitle("Resultados");
@ -138,8 +128,8 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
dialog.add(dialogLayout);
final Button saveButton = new Button("Guardar y Siguiente", e -> {
this.currSubmission.setText(this.questionEditor.getValue());
this.examService.saveSubmission(exam.getId(), this.currSubmission);
this.currSubmission.setResponse(this.questionEditor.getValue());
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
dialog.close();
goToNext();
});
@ -166,9 +156,9 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
final Button completeButton = new Button("Terminar", e -> {
completeDialog.close();
this.currSubmission.setText(this.questionEditor.getValue());
this.examService.saveSubmission(exam.getId(), this.currSubmission);
this.exam = examService.completeExam(exam.getId());
this.currSubmission.setResponse(this.questionEditor.getValue());
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
this.assessment = assessmentService.completeAssessment(assessment.getId());
goToCompleted();
updateUI();
});
@ -205,26 +195,24 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
final MenuBar navMenuBar = new MenuBar();
prev = navMenuBar.addItem("Anterior pregunta",
menuItemClickEvent -> {
this.currSubmission.setText(this.questionEditor.getValue());
this.examService.saveSubmission(exam.getId(), this.currSubmission);
this.currSubmission = this.examService.getPrevSubmission(exam.getId(), this.currSubmission);
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
log.info(">>> prev");
this.currSubmission.setResponse(this.questionEditor.getValue());
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
this.currSubmission = this.assessmentService.getPrevSubmission(assessment.getId(), this.currSubmission);
updateUI();
});
next = navMenuBar.addItem("Siguiente pregunta",
menuItemClickEvent -> {
this.currSubmission.setText(this.questionEditor.getValue());
this.examService.saveSubmission(exam.getId(), this.currSubmission);
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
this.currSubmission.setResponse(this.questionEditor.getValue());
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
goToNext();
});
reset = navMenuBar.addItem("Reiniciar pregunta (deshacer todos los cambios)",
menuItemClickEvent -> {
this.currSubmission.setText(this.currSubmission.getQuestion().getContent());
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
this.currSubmission.setResponse(this.currSubmission.getQuestion().getContent());
this.questionEditor.setValue(this.currSubmission.getQuestion().getContent());
});
finish = navMenuBar.addItem("Terminar evaluacion", menuItemClickEvent -> {
this.completeDialog.open();
});
final Div menuBar = new Div();
menuBar.add(runMenuBar, navMenuBar);
@ -265,9 +253,10 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
start = new Button("Empezar");
start.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
start.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.exam = this.examService.startExam(this.exam.getId());
log.info(">>> start");
this.assessment = this.assessmentService.startAssessment(this.assessment.getId());
if (tf.getValue().trim().equalsIgnoreCase(this.exam.getCandidate().getEmail())) {
if (tf.getValue().trim().equalsIgnoreCase(this.assessment.getCandidate().getEmail())) {
this.getUI().get().getPage().reload();
} else {
Notification notification = new Notification();
@ -329,7 +318,8 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
}
private void goToNext() {
Submission found = this.examService.getNextSubmission(exam.getId(),
log.info(">>> next");
Submission found = this.assessmentService.getNextSubmission(assessment.getId(),
this.currSubmission.getId());
if (found == null) {
@ -340,44 +330,78 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
}
}
private void initCandidatePanel() {
final PanelConfig config = PanelConfig.builder()
.closeable(false)
.collapsable(false)
.build();
private void initSidebar() {
sidebar = new Section();
sidebar.addClassNames(Background.CONTRAST_5, BoxSizing.BORDER, Display.FLEX, FlexDirection.COLUMN,
Flex.SHRINK_NONE, Overflow.AUTO, Padding.LARGE);
sidebar.setWidth("256px");
candidatePanel = new Panel(config, "", new HorizontalLayout());
candidatePanel.setVisible(false);
dl = new DescriptionList();
dl.addClassNames(Display.FLEX, FlexDirection.COLUMN, Gap.LARGE, Margin.Bottom.SMALL, Margin.Top.NONE,
FontSize.SMALL);
final Text text = new Text("Tiempo restante:");
timer = new SimpleTimer(0);
timer.setMinutes(true);
timer.addTimerEndEvent((ComponentEventListener<SimpleTimer.TimerEndedEvent>) timerEndedEvent -> {
Notification.show("Tiempo completado.", 5_000, Notification.Position.TOP_CENTER);
this.currSubmission.setResponse(this.questionEditor.getValue());
this.assessmentService.saveSubmission(assessment.getId(), this.currSubmission);
this.assessment = assessmentService.completeAssessment(assessment.getId());
goToCompleted();
updateUI();
});
timer.setFractions(false);
final Button completeButton = new Button("Terminar evaluacion");
completeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
completeButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.completeDialog.open();
});
// Add it all together
sidebar.add(dl, text, timer, completeButton);
}
private void updateUI() {
if (exam == null || !exam.isStarted()) {
if (assessment == null || !assessment.isStarted()) {
editorSection.setVisible(false);
startSection.setVisible(true);
sidebar.setVisible(false);
} else {
if (currSubmission != null) {
questionEditor.setValue(this.currSubmission.getText());
questionEditor.setValue(this.currSubmission.getResponse());
questionTitle.setText(this.currSubmission.getQuestion().getTitle());
questionDescription.setText(this.currSubmission.getQuestion().getDescription());
}
editorSection.setVisible(true);
startSection.setVisible(false);
sidebar.setVisible(true);
updateCandidatePanel();
prev.setEnabled(currSubmission != null && !assessment.isFirst(currSubmission));
next.setEnabled(currSubmission == null || !assessment.isLast(currSubmission));
prev.setEnabled(currSubmission != null && !exam.isFirst(currSubmission));
next.setEnabled(currSubmission == null || !exam.isLast(currSubmission));
if (this.exam.isCompleted()) {
if (this.assessment.isCompleted()) {
goToCompleted();
this.editorSection.setVisible(false);
this.candidatePanel.setVisible(false);
this.sidebar.setVisible(false);
this.startSection.setVisible(false);
this.completedSection.setVisible(true);
}
final Long remainingTime = this.exam.getRemainingTimeSeconds();
if (dl.getChildren().collect(Collectors.toList()).isEmpty()) {
dl.add(
createItem("Candidato:", assessment.getCandidate().getEmail()),
createItem("Hora de inicio:", Optional.ofNullable(assessment.getStartingTime())
.map(t -> ZonedDateTime.ofInstant(t,
ZoneId.of("GMT-4")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
.orElse("N/A"))
);
}
final Long remainingTime = this.assessment.getRemainingTimeSeconds();
timer.pause();
timer.setStartTime(remainingTime > 0 ? remainingTime : 3);
timer.setMinutes(true);
@ -386,19 +410,6 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
}
}
private void updateCandidatePanel() {
final Text candidateName = new Text("Candidato: " + exam.getCandidate().getEmail() + ", ");
final Text startTime = new Text("Hora de inicio: " + Optional.ofNullable(exam.getStartingTime())
.map(t -> ZonedDateTime.ofInstant(t,
ZoneId.of("GMT-4")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
.orElse("N/A") + ", ");
final Text text = new Text("Tiempo restante:");
final HorizontalLayout layout = new HorizontalLayout(candidateName, startTime, text, timer);
layout.setWidthFull();
candidatePanel.setContent(layout);
candidatePanel.setVisible(true);
}
private Div createItem(final String label, final String value) {
return new Div(createTerm(label), createDescription(value));
}
@ -426,18 +437,18 @@ public class CandidateExamView extends Main implements HasUrlParameter<String> {
@Override
public void setParameter(final BeforeEvent beforeEvent, final String s) {
this.exam = this.examService.getExam(UUID.fromString(s));
this.assessment = this.assessmentService.getAssessment(UUID.fromString(s));
if (this.exam == null) {
if (this.assessment == null) {
throw new NotFoundException();
}
if (this.exam.isCompleted()) {
if (this.assessment.isCompleted()) {
goToCompleted();
}
this.currSubmission = this.exam.isStarted()
? this.examService.getNextSubmission(exam.getId(), null)
this.currSubmission = this.assessment.isStarted()
? this.assessmentService.getNextSubmission(assessment.getId(), null)
: null;
updateUI();

View File

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

View File

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

View File

@ -1,46 +1,20 @@
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.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
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.Scroller;
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;
import java.util.UUID;
/**
* The main view is a top-level placeholder for other views.
*/
@ -49,158 +23,79 @@ 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";
if (user instanceof Employee) {
final UUID uuid = ((Employee) user).getId();
if (uuid != null) {
employeeId = uuid.toString();
}
}
final Avatar loggedUser = new Avatar(user.getUsername());
loggedUser.getStyle().set("display", "block");
loggedUser.getElement().setAttribute("tabindex", "-1");
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(menuBar);
hl.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
return hl;
}).orElseGet(HorizontalLayout::new);
header.setAlignItems(FlexComponent.Alignment.STRETCH);
header.setWidthFull();
addToNavbar(true, toggle, viewTitle, header);
addToNavbar(true, toggle, viewTitle);
}
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("Requests", RequestsListView.class,
LineAwesomeIcon.TASKS_SOLID.create()));
admin.addItem(new SideNavItem("Timesheets", TimesheestReportView.class,
LineAwesomeIcon.HOURGLASS_END_SOLID.create()));
admin.addItem(new SideNavItem("Employees", EmployeesListView.class,
LineAwesomeIcon.USER_EDIT_SOLID.create()));
profile.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());
SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class,
LineAwesomeIcon.HOURGLASS_START_SOLID.create());
SideNavItem profile = new SideNavItem("My Profile", ProfileView.class,
LineAwesomeIcon.USER_EDIT_SOLID.create());
SideNavItem documents = new SideNavItem("My Documents", DocumentsView.class,
LineAwesomeIcon.SUITCASE_SOLID.create());
nav.addItem(new SideNavItem("Home", MainView.class, LineAwesomeIcon.HOME_SOLID.create()));
nav.addItem(admin);
nav.addItem(recruiting);
nav.addItem(profile);
nav.addItem(timesheet);
nav.addItem(timeOff);
}
});
return nav;
}
private Footer createFooter(final String commitId) {
return new Footer(new Text(String.format("v.%s", commitId)));
private Footer createFooter() {
Footer layout = new Footer();
return layout;
}
@Override

View File

@ -2,7 +2,6 @@ package com.primefactorsolutions.views;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import jakarta.annotation.security.PermitAll;
@ -11,8 +10,7 @@ import jakarta.annotation.security.PermitAll;
@Route(value = "", layout = MainLayout.class)
@PermitAll
public class MainView extends Main {
public MainView() {
add(new VerticalLayout(new Text("Welcome to PFS!")));
add(new Text("welcome"));
}
}

View File

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

View File

@ -0,0 +1,17 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Profile")
@Route(value = "/profiles", layout = MainLayout.class)
public class ProfileView extends Main {
}

View File

@ -1,29 +1,27 @@
package com.primefactorsolutions.views.assessment;
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Question;
import com.primefactorsolutions.service.QuestionService;
import com.primefactorsolutions.views.BaseEntityForm;
import com.primefactorsolutions.views.MainLayout;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.*;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import jakarta.annotation.security.PermitAll;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.form.BeanValidationForm;
import java.util.List;
import java.util.UUID;
@SpringComponent
@Scope("prototype")
@PageTitle("Questions")
@PageTitle("Assessments")
@Route(value = "/questions", layout = MainLayout.class)
@RolesAllowed("ROLE_ADMIN")
public class QuestionView extends BaseEntityForm<Question> implements HasUrlParameter<String> {
@PermitAll
public class QuestionView extends BeanValidationForm<Question> implements HasUrlParameter<String> {
private final QuestionService questionService;
private TextField title = null;
@ -31,9 +29,8 @@ public class QuestionView extends BaseEntityForm<Question> implements HasUrlPara
private TextArea content = null;
private IntegerField timeMinutes = null;
public QuestionView(final AuthenticationContext authenticationContext,
final QuestionService questionService) {
super(authenticationContext, Question.class);
public QuestionView(final QuestionService questionService) {
super(Question.class);
this.questionService = questionService;
title = new TextField();
title.setWidthFull();
@ -51,8 +48,8 @@ public class QuestionView extends BaseEntityForm<Question> implements HasUrlPara
content.setLabel("Content");
setSavedHandler((SavedHandler<Question>) question -> {
questionService.createOrUpdate(question);
goTo(QuestionsListView.class);
final Question saved = questionService.createOrUpdate(question);
setEntityWithEnabledSave(saved);
});
}

View File

@ -0,0 +1,89 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Question;
import com.primefactorsolutions.service.QuestionService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.DataProviderListener;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.VGrid;
import java.util.stream.Stream;
@SpringComponent
@Scope("prototype")
@PageTitle("Questions")
@Route(value = "/questions", layout = MainLayout.class)
@PermitAll
public class QuestionsListView extends Main {
private final QuestionService questionService;
public QuestionsListView(final QuestionService questionService) {
this.questionService = questionService;
final HorizontalLayout hl = new HorizontalLayout();
final Button addQuestion = new Button("Add Question");
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(QuestionView.class, "new");
});
hl.add(addQuestion);
final VGrid<Question> grid = new VGrid<>(Question.class);
grid.setColumns("id", "title");
grid.addComponentColumn((ValueProvider<Question, Component>) question -> {
final Button edit = new Button("Edit");
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().get().navigate(QuestionView.class, question.getId().toString()));
return edit;
});
grid.setDataProvider(new DataProvider<>() {
@Override
public boolean isInMemory() {
return false;
}
@Override
public int size(final Query<Question, Object> query) {
return questionService.getQuestions().size();
}
@Override
public Stream<Question> fetch(final Query<Question, Object> query) {
int limit = query.getLimit();
int pagerSize = query.getPageSize();
int page = query.getPage();
return questionService.getQuestions().stream();
}
@Override
public void refreshItem(final Question question) {
// no-op
}
@Override
public void refreshAll() {
// no-op
}
@Override
public Registration addDataProviderListener(final DataProviderListener<Question> dataProviderListener) {
return null;
}
});
grid.setAllRowsVisible(true);
add(hl, grid);
}
}

View File

@ -0,0 +1,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("Requests")
@Route(value = "/requests", layout = MainLayout.class)
@PermitAll
public class RequestsListView extends Main {
}

View File

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

View File

@ -3,9 +3,9 @@ package com.primefactorsolutions.views;
import com.hilerio.ace.AceEditor;
import com.hilerio.ace.AceMode;
import com.hilerio.ace.AceTheme;
import com.primefactorsolutions.model.Exam;
import com.primefactorsolutions.model.Assessment;
import com.primefactorsolutions.model.Submission;
import com.primefactorsolutions.service.ExamService;
import com.primefactorsolutions.service.AssessmentService;
import com.primefactorsolutions.service.CompilerService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
@ -20,9 +20,9 @@ import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.theme.lumo.LumoUtility.*;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Scope;
@ -37,17 +37,18 @@ import java.util.stream.Collectors;
@PageTitle("Evaluacion")
@SpringComponent
@Scope("prototype")
@RolesAllowed("ROLE_ADMIN")
@Route(value = "/submissions", layout = MainLayout.class)
@Route(value = "/submission", layout = MainLayout.class)
@AnonymousAllowed
@Slf4j
public class SubmissionView extends Main implements HasUrlParameter<String> {
private final CompilerService compilerService;
private final ExamService examService;
private final AssessmentService assessmentService;
private AceEditor questionEditor = null;
private AceEditor result = null;
private Dialog dialog = null;
private Exam exam = null;
private Assessment assessment = null;
private Submission currSubmission = null;
private MenuItem prev = null;
private MenuItem next = null;
@ -56,9 +57,9 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
private Section editorSection = null;
private H3 questionTitle = null;
public SubmissionView(final CompilerService compilerService, final ExamService examService) {
public SubmissionView(final CompilerService compilerService, final AssessmentService assessmentService) {
this.compilerService = compilerService;
this.examService = examService;
this.assessmentService = assessmentService;
addClassNames(Display.FLEX, Flex.GROW, Height.FULL);
@ -130,13 +131,13 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
prev = navMenuBar.addItem("Anterior pregunta",
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
log.info(">>> prev");
this.currSubmission = this.examService.getPrevSubmission(exam.getId(),
this.currSubmission = this.assessmentService.getPrevSubmission(assessment.getId(),
this.currSubmission);
updateUI();
});
next = navMenuBar.addItem("Siguiente pregunta",
(ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
this.currSubmission.setText(this.questionEditor.getValue());
this.currSubmission.setResponse(this.questionEditor.getValue());
goToNext();
});
@ -185,7 +186,7 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
private void goToNext() {
log.info(">>> next");
Submission found = this.examService.getNextSubmission(exam.getId(),
Submission found = this.assessmentService.getNextSubmission(assessment.getId(),
this.currSubmission.getId(), false);
if (found != null) {
@ -208,25 +209,25 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
}
private void updateUI() {
if (exam == null || !exam.isStarted()) {
if (assessment == null || !assessment.isStarted()) {
editorSection.setVisible(false);
sidebar.setVisible(false);
} else {
if (currSubmission != null) {
questionEditor.setValue(this.currSubmission.getText());
questionEditor.setValue(this.currSubmission.getResponse());
questionTitle.setText(this.currSubmission.getQuestion().getTitle());
}
editorSection.setVisible(true);
sidebar.setVisible(true);
prev.setEnabled(currSubmission != null && !exam.isFirst(currSubmission));
next.setEnabled(currSubmission == null || !exam.isLast(currSubmission));
prev.setEnabled(currSubmission != null && !assessment.isFirst(currSubmission));
next.setEnabled(currSubmission == null || !assessment.isLast(currSubmission));
if (dl.getChildren().collect(Collectors.toList()).isEmpty()) {
dl.add(
createItem("Candidato:", exam.getCandidate().getEmail()),
createItem("Hora de inicio:", Optional.ofNullable(exam.getStartingTime())
createItem("Candidato:", assessment.getCandidate().getEmail()),
createItem("Hora de inicio:", Optional.ofNullable(assessment.getStartingTime())
.map(t -> ZonedDateTime.ofInstant(t,
ZoneId.of("GMT-4")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
.orElse("N/A"))
@ -258,14 +259,14 @@ public class SubmissionView extends Main implements HasUrlParameter<String> {
@Override
public void setParameter(final BeforeEvent beforeEvent, final String s) {
this.exam = this.examService.getExam(UUID.fromString(s));
this.assessment = this.assessmentService.getAssessment(UUID.fromString(s));
if (this.exam == null) {
if (this.assessment == null) {
throw new NotFoundException();
}
this.currSubmission = this.exam.isStarted()
? this.examService.getNextSubmission(exam.getId(), null, false)
this.currSubmission = this.assessment.isStarted()
? this.assessmentService.getNextSubmission(assessment.getId(), null, false)
: null;
updateUI();

View File

@ -0,0 +1,17 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Timeoff")
@Route(value = "/timeoffs/me", layout = MainLayout.class)
public class TimeoffView extends Main {
}

View File

@ -0,0 +1,16 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@Scope("prototype")
@PageTitle("Timesheets")
@Route(value = "/timesheets", layout = MainLayout.class)
@PermitAll
public class TimesheestReportView extends Main {
}

View File

@ -0,0 +1,17 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Timesheet")
@Route(value = "/timesheets/me", layout = MainLayout.class)
public class TimesheetView extends Main {
}

View File

@ -1,135 +0,0 @@
package com.primefactorsolutions.views.admin;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.TimeOffService;
import com.primefactorsolutions.views.BaseView;
import com.primefactorsolutions.views.MainLayout;
import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.VGrid;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import java.util.stream.IntStream;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Registro de Vacaciones")
@Route(value = "/time-off/list", layout = MainLayout.class)
public class TimeOffListView extends BaseView {
private final TimeOffService timeOffService;
private final VGrid<TimeOff> timeOffGrid = new VGrid<>();
private ComboBox<Integer> yearFilter;
public TimeOffListView(final AuthenticationContext authenticationContext,
final TimeOffService timeOffService) {
super(authenticationContext);
this.timeOffService = timeOffService;
initializeView();
}
private void refreshGridListHoursWorked(final Integer year) {
final List<TimeOff> timeOffs = timeOffService.findTimeOffs(year);
timeOffGrid.setDataProvider(new ListDataProvider<>(timeOffs));
timeOffGrid.getDataProvider().refreshAll();
}
private void initializeView() {
getCurrentPageLayout().add(createAddTimeOff());
setupFilters();
setupListHoursWorkedGrid();
getCurrentPageLayout().add(timeOffGrid);
refreshGridListHoursWorked(yearFilter.getValue());
}
private void setupFilters() {
final HorizontalLayout hl = new HorizontalLayout();
hl.add(createYearFilter());
getCurrentPageLayout().add(hl);
}
private ComboBox<Integer> createYearFilter() {
yearFilter = new ComboBox<>("Year");
final int nowYear = LocalDate.now().getYear();
final List<Integer> years = IntStream.range(0, 2).mapToObj(y -> nowYear - y).toList();
yearFilter.setItems(years);
yearFilter.setValue(years.getFirst());
yearFilter.addValueChangeListener(event ->
refreshGridListHoursWorked(event.getValue())
);
return yearFilter;
}
private void setupListHoursWorkedGrid() {
timeOffGrid.addColumn(e -> e.getCategory().name())
.setHeader("Categoria");
timeOffGrid.addColumn(e -> e.getType().name())
.setHeader("Tipo");
timeOffGrid.addColumn(TimeOff::getDate)
.setHeader("Fecha");
timeOffGrid.addColumn(TimeOff::getDuration).setHeader("Duracion");
timeOffGrid.addColumn(TimeOff::getExpiration).setHeader("Expiracion");
timeOffGrid.addComponentColumn((ValueProvider<TimeOff, Component>) timeOff -> {
final MenuBar menuBar = new MenuBar();
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.EYE, "Ver");
viewItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
navigateToTimeOffView(timeOff.getId(), "view");
});
final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Editar");
editItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
navigateToTimeOffView(timeOff.getId(), "edit");
});
return menuBar;
});
}
private void navigateToTimeOffView(final UUID idRecord, final String action) {
getUI().ifPresent(ui -> ui.navigate(TimeOffView.class, idRecord.toString() + "/" + action));
}
private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
final Button button = new Button(label);
if (isPrimary) {
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
}
button.addClickListener(event -> onClickAction.run());
return button;
}
private Button createAddTimeOff() {
return createButton("Agregar Vacacion", this::navigateToTimeOff, true);
}
private void navigateToTimeOff() {
getUI().ifPresent(ui -> ui.navigate(TimeOffView.class, "new"));
}
}

View File

@ -1,109 +0,0 @@
package com.primefactorsolutions.views.admin;
import com.primefactorsolutions.model.TimeOff;
import com.primefactorsolutions.model.TimeOffRequestType;
import com.primefactorsolutions.service.TimeOffService;
import com.primefactorsolutions.views.BaseEntityForm;
import com.primefactorsolutions.views.MainLayout;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.router.*;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.datepicker.VDatePicker;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.List;
import java.util.UUID;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Vacaciones")
@Route(value = "/time-off/:hours-workedId?/:action?", layout = MainLayout.class)
public class TimeOffView extends BaseEntityForm<TimeOff> implements HasUrlParameter<String> {
private final ComboBox<TimeOffRequestType> category = new ComboBox<>("Categoria");
private final ComboBox<TimeOff.Type> type = new ComboBox<>("Tipo");
private final VDatePicker date = new VDatePicker("Fecha");
private final NumberField duration = new NumberField("Duracion");
private final NumberField expiration = new NumberField("Expiracion");
private final TimeOffService timeOffService;
public TimeOffView(final AuthenticationContext authenticationContext,
final TimeOffService timeOffService) {
super(authenticationContext, TimeOff.class);
this.timeOffService = timeOffService;
initializeDateField();
category.setItems(TimeOffRequestType.values());
type.setItems(TimeOff.Type.values());
this.setSavedHandler(this::saveTimeOff);
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
final String s = params.get("hours-workedId").orElse(null);
if ("new".equals(action) || s == null) {
setEntityWithEnabledSave(new TimeOff());
} else {
final UUID timeOffId = UUID.fromString(s);
final TimeOff timeOff = timeOffService.getTimeOff(timeOffId);
if ("edit".equals(action) && !s.isEmpty()) {
setEntityWithEnabledSave(timeOff);
} else if ("view".equals(action) && !s.isEmpty()) {
setEntity(timeOff);
duration.setReadOnly(true);
expiration.setReadOnly(true);
category.setReadOnly(true);
type.setReadOnly(true);
date.setReadOnly(true);
}
}
}
@Override
protected List<Component> getFormComponents() {
return List.of(
category,
type,
date,
duration,
expiration
);
}
private void initializeDateField() {
final LocalDate today = LocalDate.now();
final YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth());
final LocalDate startOfMonth = currentMonth.atDay(1);
date.setWidthFull();
date.setMin(startOfMonth);
date.setMax(today);
date.setValue(today);
}
private void saveTimeOff(final TimeOff timeOff) {
if (isFormValid()) {
timeOffService.saveTimeOff(timeOff);
closeForm();
}
}
private void closeForm() {
getUI().ifPresent(ui -> ui.navigate(TimeOffListView.class));
}
private boolean isFormValid() {
return date.getValue() != null;
}
}

View File

@ -1,54 +0,0 @@
package com.primefactorsolutions.views.assessment;
import com.primefactorsolutions.model.Candidate;
import com.primefactorsolutions.service.CandidateService;
import com.primefactorsolutions.views.BaseView;
import com.primefactorsolutions.views.MainLayout;
import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.VGrid;
import java.util.Map;
@SpringComponent
@Scope("prototype")
@PageTitle("Candidates")
@Route(value = "/candidates", layout = MainLayout.class)
@RolesAllowed("ROLE_ADMIN")
public class CandidatesListView extends BaseView {
public CandidatesListView(final AuthenticationContext authenticationContext,
final CandidateService candidateService) {
super(authenticationContext);
final HorizontalLayout hl = new HorizontalLayout();
final Button addCandidate = new Button("Add Candidate");
addCandidate.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, "new"));
});
hl.add(addCandidate);
final VGrid<Candidate> grid = new VGrid<>(Candidate.class);
grid.setColumns("email");
grid.setAllRowsVisible(true);
grid.addComponentColumn(candidate ->
MenuBarUtils.menuBar(Map.of(Pair.of("Edit", VaadinIcon.PENCIL), menuItemClickEvent ->
getUI().flatMap(ui -> ui.navigate(CandidateView.class, candidate.getId().toString())))));
grid.setDataProvider(new ListDataProvider<>(candidateService.getCandidates()));
getCurrentPageLayout().add(hl, grid);
}
}

View File

@ -1,88 +0,0 @@
package com.primefactorsolutions.views.assessment;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.CandidateService;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.EvaluationService;
import com.primefactorsolutions.views.BaseEntityForm;
import com.primefactorsolutions.views.MainLayout;
import com.primefactorsolutions.views.util.EntityComboBox;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.fields.ElementCollectionField;
import org.vaadin.firitin.fields.EnumSelect;
import java.util.List;
import java.util.UUID;
@SpringComponent
@Scope("prototype")
@PageTitle("Evaluations")
@Route(value = "/evaluations", layout = MainLayout.class)
@RolesAllowed("ROLE_ADMIN")
public class EvaluationView extends BaseEntityForm<Evaluation> implements HasUrlParameter<String> {
private final CandidateService candidateService;
private final EvaluationService evaluationService;
private final EmployeeService employeeService;
private final EntityComboBox<Candidate> candidate = new EntityComboBox<>("Candidate");
private final EntityComboBox<Employee> interviewer = new EntityComboBox<>("Interviewer");
private final EnumSelect<EmployeePosition> candidatePosition =
new EnumSelect<>("Position", EmployeePosition.class);
private final IntegerField points = new IntegerField("Points");
private final Text skillLabel = new Text("Skills");
private final ElementCollectionField<SkillEvaluation> skillEvaluations =
new ElementCollectionField<>(SkillEvaluation.class);
public EvaluationView(final AuthenticationContext authenticationContext,
final CandidateService candidateService,
final EmployeeService employeeService,
final EvaluationService evaluationService) {
super(authenticationContext, Evaluation.class);
this.employeeService = employeeService;
this.evaluationService = evaluationService;
this.candidateService = candidateService;
this.candidate.setWidthFull();
this.interviewer.setWidthFull();
this.points.setWidthFull();
this.skillEvaluations.setWidthFull();
this.candidatePosition.setWidthFull();
this.candidate.setItems(this.candidateService.getCandidates());
this.interviewer.setItems(this.employeeService.getEmployees());
setSavedHandler(evaluation -> goTo(EvaluationsListView.class));
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String s) {
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
var evaluation = evaluationService.getEvaluation(UUID.fromString(s));
setEntityWithEnabledSave(evaluation);
} else {
setEntityWithEnabledSave(new Evaluation());
}
}
@Override
protected List<Component> getFormComponents() {
return List.of(
candidate,
points,
candidatePosition,
skillLabel,
skillEvaluations,
interviewer
);
}
}

View File

@ -1,52 +0,0 @@
package com.primefactorsolutions.views.assessment;
import com.primefactorsolutions.model.Evaluation;
import com.primefactorsolutions.service.EvaluationService;
import com.primefactorsolutions.views.BaseView;
import com.primefactorsolutions.views.MainLayout;
import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.VGrid;
import java.util.Map;
@SpringComponent
@Scope("prototype")
@PageTitle("Evaluations")
@Route(value = "/evaluations", layout = MainLayout.class)
@RolesAllowed("ROLE_ADMIN")
public class EvaluationsListView extends BaseView {
public EvaluationsListView(final AuthenticationContext authenticationContext,
final EvaluationService evaluationService) {
super(authenticationContext);
final HorizontalLayout hl = new HorizontalLayout();
final Button addEvaluation = new Button("Add Evaluation");
addEvaluation.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
addEvaluation.addClickListener(buttonClickEvent -> {
this.getUI().flatMap(ui -> ui.navigate(EvaluationView.class, "new"));
});
hl.add(addEvaluation);
final VGrid<Evaluation> grid = new VGrid<>(Evaluation.class);
grid.setColumns("candidate.email");
grid.setAllRowsVisible(true);
grid.addComponentColumn(evaluation ->
MenuBarUtils.menuBar(Map.of(Pair.of("Edit", VaadinIcon.PENCIL), menuItemClickEvent ->
getUI().flatMap(ui -> ui.navigate(EvaluationView.class, evaluation.getId().toString())))));
grid.setDataProvider(new ListDataProvider<>(evaluationService.getEvaluations()));
getCurrentPageLayout().add(hl, grid);
}
}

View File

@ -1,86 +0,0 @@
package com.primefactorsolutions.views.assessment;
import com.primefactorsolutions.model.Exam;
import com.primefactorsolutions.service.ExamService;
import com.primefactorsolutions.views.BaseView;
import com.primefactorsolutions.views.MainLayout;
import com.primefactorsolutions.views.SubmissionView;
import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.context.annotation.Scope;
import org.vaadin.addon.stefan.clipboard.ClientsideClipboard;
import org.vaadin.firitin.components.grid.VGrid;
@SpringComponent
@Scope("prototype")
@PageTitle("Exams")
@Route(value = "/exams", layout = MainLayout.class)
@RolesAllowed("ROLE_ADMIN")
public class ExamsListView extends BaseView {
public ExamsListView(final AuthenticationContext authenticationContext,
final ExamService examService) {
super(authenticationContext);
final HorizontalLayout hl = new HorizontalLayout();
final Button addExam = new Button("Add Exam");
addExam.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
addExam.addClickListener(buttonClickEvent ->
getUI().flatMap(ui -> ui.navigate(ExamView.class, "new")));
hl.add(addExam);
final VGrid<Exam> grid = new VGrid<>(Exam.class);
grid.setColumns("id", "candidate.email");
final Grid.Column<Exam> statusColumn = grid.addColumn(exam ->
exam.getExamEvents().isEmpty()
? "N/A"
: exam.getExamEvents().getLast().getStatus().name());
statusColumn.setHeader("Status");
grid.addComponentColumn(exam -> MenuBarUtils.menuBar(
Pair.of("View", __ ->
getUI().flatMap(ui -> ui.navigate(SubmissionView.class, exam.getId().toString()))),
Pair.of("Copy", __ ->
ClientsideClipboard.writeToClipboard(
String.format("email: %s link: "
+ "https://intra.primefactorsolutions.com/candidate-exam/%s",
exam.getCandidate().getEmail(),
exam.getId()))),
Pair.of("Email", __ -> {
ConfirmDialog dialog = new ConfirmDialog();
dialog.setHeader("Send Link Email");
dialog.setText(String.format("Enviar link por email al candidato %s?",
exam.getCandidate().getEmail()));
dialog.setCancelable(true);
dialog.setConfirmText("Enviar");
dialog.setConfirmButtonTheme("primary");
dialog.addConfirmListener(confirmEvent -> {
try {
examService.sendEmail(exam);
} catch (Exception e) {
Notification.show("Error sending email: " + e.getMessage(), 10_000,
Notification.Position.TOP_CENTER);
}
});
dialog.open();
})
));
grid.setDataProvider(new ListDataProvider<>(examService.getExams()));
grid.setAllRowsVisible(true);
getCurrentPageLayout().add(hl, grid);
}
}

View File

@ -1,50 +0,0 @@
package com.primefactorsolutions.views.assessment;
import com.primefactorsolutions.model.Question;
import com.primefactorsolutions.service.QuestionService;
import com.primefactorsolutions.views.BaseView;
import com.primefactorsolutions.views.MainLayout;
import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.VGrid;
@SpringComponent
@Scope("prototype")
@PageTitle("Questions")
@Route(value = "/questions", layout = MainLayout.class)
@RolesAllowed("ROLE_ADMIN")
public class QuestionsListView extends BaseView {
public QuestionsListView(final AuthenticationContext authenticationContext, final QuestionService questionService) {
super(authenticationContext);
final HorizontalLayout hl = new HorizontalLayout();
final Button addQuestion = new Button("Add Question");
addQuestion.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, "new")));
hl.add(addQuestion);
final VGrid<Question> grid = new VGrid<>(Question.class);
grid.setColumns("title", "description", "timeMinutes");
grid.addComponentColumn(question ->
MenuBarUtils.menuBar(Pair.of("Edit", __ ->
getUI().flatMap(ui -> ui.navigate(QuestionView.class, question.getId().toString())))));
grid.setDataProvider(new ListDataProvider<>(questionService.getQuestions()));
grid.setAllRowsVisible(true);
getCurrentPageLayout().add(hl, grid);
}
}

View File

@ -1,229 +0,0 @@
package com.primefactorsolutions.views.employee;
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;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent;
import elemental.json.Json;
import elemental.json.JsonArray;
import elemental.json.JsonObject;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.userdetails.UserDetails;
import com.vaadin.flow.spring.security.AuthenticationContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
import java.io.InputStream;
@SpringComponent
@PermitAll
@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.");
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 viewDocumentButton;
public DocumentView(final AuthenticationContext authenticationContext,
final DocumentService documentService,
final EmployeeService employeeService,
final AuthenticationContext authContext) {
super(authenticationContext, Document.class);
this.documentService = documentService;
this.employeeService = employeeService;
this.authContext = authContext;
initializeView();
this.setSavedHandler(this::saveDocument);
}
private void initializeView() {
configureComponents();
configureUploadButton();
}
protected Button createViewDocumentButton() {
viewDocumentButton = new Button("Ver documento");
viewDocumentButton.setEnabled(false);
viewDocumentButton.addClickListener(event -> viewDocument());
return viewDocumentButton;
}
private void setFileNameProperties() {
fileName.setWidthFull();
}
private void setDocumentTypeProperties() {
documentType.setItems(DocumentType.values());
documentType.setWidthFull();
}
private void setEmployeeComboBoxProperties() {
List<Employee> employees = employeeService.findAllEmployees();
employeeComboBox.setItems(employees);
employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
employeeComboBox.setWidthFull();
}
private void setDocumentCreator(final Document document) {
authContext.getAuthenticatedUser(UserDetails.class).ifPresent(user -> {
document.setCreator(user.getUsername());
});
}
private void setFieldsReadOnly(final boolean option) {
fileName.setReadOnly(option);
documentType.setReadOnly(option);
employeeComboBox.setReadOnly(option);
}
private void viewDocument() {
StreamResource resource;
try {
InputStream inputStream = buffer.getInputStream();
if (inputStream != null && inputStream.available() > 0) {
resource = new StreamResource(fileName.getValue(), () -> new ByteArrayInputStream(readFileData()));
} else {
byte[] fileData = getEntity().getFileData();
resource = new StreamResource(fileName.getValue(), () -> new ByteArrayInputStream(fileData));
}
resource.setContentType("application/pdf");
getUI().ifPresent(ui -> {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString());
});
} catch (IOException e) {
Notification.show("Error al leer el archivo.");
}
}
private void saveDocument(final Document document) {
if (isFormValid()) {
document.setFileName(fileName.getValue());
document.setDocumentType(documentType.getValue());
document.setEmployee(employeeComboBox.getValue());
document.setFileData(readFileData());
setDocumentCreator(document);
documentService.saveDocument(document);
getUI().ifPresent(ui -> ui.navigate(DocumentsListView.class));
} else {
Notification.show("Error al guardar: Por favor, complete todos los campos y cargue un archivo.");
}
}
private boolean isFormValid() {
return !fileName.isEmpty()
&& documentType.getValue() != null
&& employeeComboBox.getValue() != null
&& fileUploaded;
}
private byte[] readFileData() {
try {
return buffer.getInputStream().readAllBytes();
} catch (IOException e) {
Notification.show("Error al leer los datos del archivo.");
return new byte[0];
}
}
private void preLoadFile(final Document document) {
JsonArray jsonArray = Json.createArray();
JsonObject jsonObject = Json.createObject();
jsonObject.put("name", document.getFileName());
jsonObject.put("progress", 100);
jsonObject.put("complete", true);
jsonObject.put("fileData", Base64.getEncoder().encodeToString(document.getFileData()));
jsonArray.set(0, jsonObject);
uploadButton.getElement().setPropertyJson("files", jsonArray);
fileUploaded = true;
}
private void configureComponents() {
setFileNameProperties();
setDocumentTypeProperties();
setEmployeeComboBoxProperties();
}
private void configureUploadButton() {
uploadButton.setMaxFiles(1);
uploadButton.setAcceptedFileTypes(".pdf");
uploadButton.addSucceededListener(event -> {
fileUploaded = true;
Notification.show("Archivo cargado correctamente.");
viewDocumentButton.setEnabled(true);
});
uploadButton.getElement().addEventListener("file-remove", event -> {
fileUploaded = false;
Notification.show("Archivo eliminado.");
viewDocumentButton.setEnabled(false);
});
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
final String documentIdString = params.get("documentId").orElse(null);
if ("new".equals(action)) {
setEntityWithEnabledSave(new Document());
} else {
assert documentIdString != null;
UUID documentId = UUID.fromString(documentIdString);
Document document = documentService.getDocument(documentId);
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);
}
}
}
@Override
protected List<Component> getFormComponents() {
final HorizontalLayout buttonLayout = new HorizontalLayout();
buttonLayout.add(uploadButton, createViewDocumentButton());
buttonLayout.setSpacing(true);
return List.of(documentType, fileName, employeeComboBox, pdfOnlyMessage, buttonLayout);
}
}

View File

@ -1,245 +0,0 @@
package com.primefactorsolutions.views.employee;
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.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;
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 {
private final DocumentService documentService;
private final EmployeeService employeeService;
private final PagingGrid<Document> documentGrid = new PagingGrid<>(Document.class);
private ComboBox<Employee> employeeFilter;
private ComboBox<DocumentType> documentTypeFilter;
public DocumentsListView(final AuthenticationContext authenticationContext,
final DocumentService documentService,
final EmployeeService employeeService) {
super(authenticationContext);
this.documentService = documentService;
this.employeeService = employeeService;
initializeView();
updateDocumentGrid(null, null);
}
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);
}
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");
addActionColumns();
configurePagination();
}
private Span createEmployeeSpan(final Document document) {
Employee employee = document.getEmployee();
String employeeName = employee.getFirstName() + " " + employee.getLastName();
return new Span(employeeName);
}
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;
});
}
private Button createActionButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
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.setItems(DocumentType.values());
documentTypeFilter.addValueChangeListener(event -> {
updateDocumentGrid(event.getValue(), employeeFilter.getValue());
});
return documentTypeFilter;
}
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.setItemLabelGenerator(this::getEmployeeLabel);
employeeFilter.addValueChangeListener(event -> {
updateDocumentGrid(documentTypeFilter.getValue(), event.getValue());
});
return employeeFilter;
}
private String getEmployeeLabel(final Employee employee) {
return employee.getFirstName() + " " + employee.getLastName();
}
private void navigateToEditDocumentView(final Document document) {
navigateToDocumentView(document, "edit");
}
private void navigateToDocumentView(final Document document) {
navigateToDocumentView(document, "view");
}
private void navigateToDocumentView(final Document document, final String action) {
getUI().ifPresent(ui -> ui.navigate(DocumentView.class, document.getId().toString() + "/" + action));
}
private void navigateToAddDocumentView() {
getUI().ifPresent(ui -> ui.navigate(DocumentView.class, "new"));
}
private void configurePagination() {
documentGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
documentGrid.setPageSize(PAGE_SIZE);
}
private void updateDocumentGrid(final DocumentType documentType, final Employee employee) {
DocumentType finalDocumentType = isValidDocumentType(documentType) ? documentType : null;
Employee finalEmployee = isValidEmployee(employee) ? employee : null;
documentGrid.setPagingDataProvider((page, pageSize) ->
(finalDocumentType == null && finalEmployee == null)
? fetchDocuments((int) page, pageSize)
: fetchFilteredDocuments((int) page, pageSize, finalDocumentType, finalEmployee)
);
documentGrid.getDataProvider().refreshAll();
}
private boolean isValidDocumentType(final DocumentType documentType) {
return documentType != null && !"All".equals(documentType.name());
}
private boolean isValidEmployee(final Employee employee) {
return employee != null && !"All".equals(employee.getFirstName());
}
private List<Document> fetchFilteredDocuments(final int page,
final int pageSize,
final DocumentType documentType,
final Employee employee) {
return documentService.findDocumentBy(documentType, employee, page, pageSize);
}
private List<Document> fetchDocuments(final int page, final int size) {
int startIndex = page * size;
return isSortOrderPresent()
? fetchSortedDocuments(startIndex, size)
: documentService.findDocuments(startIndex, size);
}
private boolean isSortOrderPresent() {
return !documentGrid.getSortOrder().isEmpty();
}
private List<Document> fetchSortedDocuments(final int start, final int pageSize) {
GridSortOrder<Document> sortOrder = documentGrid.getSortOrder().getFirst();
return documentService.findDocuments(start, pageSize,
sortOrder.getSorted().getKey(),
sortOrder.getDirection() == SortDirection.ASCENDING);
}
private void downloadDocument(final Document document) {
StreamResource resource = createDocumentStreamResource(document);
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
}
private StreamResource createDocumentStreamResource(final Document document) {
StreamResource resource = new StreamResource(document.getFileName(),
() -> new ByteArrayInputStream(document.getFileData()));
resource.setContentType("application/pdf");
resource.setHeader("Content-Disposition", "attachment; filename=\"" + document.getFileName() + ".pdf\"");
return resource;
}
private void openDocumentStream(final StreamResource resource, final UI ui) {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString());
}
}

View File

@ -1,60 +0,0 @@
package com.primefactorsolutions.views.employee;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.ReportService;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.StreamResource;
import jakarta.annotation.security.PermitAll;
import java.io.ByteArrayInputStream;
import java.util.UUID;
@PermitAll
@PageTitle("Reporte excel")
@Route("employee-report")
public class EmployeeReportView extends VerticalLayout implements HasUrlParameter<String> {
private final EmployeeService employeeService;
private final ReportService reportService;
public EmployeeReportView(final EmployeeService employeeService, final ReportService reportService) {
this.employeeService = employeeService;
this.reportService = reportService;
Button backButton = new Button("Volver al Reporte de Empleados", event ->
UI.getCurrent().navigate(EmployeesListView.class));
backButton.addClassName("back-button");
add(backButton);
}
@Override
public void setParameter(final BeforeEvent event, @OptionalParameter final String employeeId) {
if (employeeId != null) {
UUID id = UUID.fromString(employeeId);
Employee employee = employeeService.getEmployee(id);
if (employee != null) {
generateExcelReport(employee);
} else {
Notification.show("Empleado no encontrado", 3000, Notification.Position.MIDDLE);
}
}
}
private void generateExcelReport(final Employee employee) {
try {
byte[] excelContent = reportService.generateExcelReport(employee);
StreamResource resource = new StreamResource(
employee.getFirstName() + "_" + employee.getLastName() + "_report.xlsx",
() -> new ByteArrayInputStream(excelContent)
);
Anchor downloadLink = new Anchor(resource, "Descargar Reporte Excel");
downloadLink.getElement().setAttribute("download", true);
add(downloadLink);
} catch (Exception e) {
Notification.show("Error al generar el reporte Excel", 3000, Notification.Position.MIDDLE);
}
}
}

View File

@ -1,714 +0,0 @@
package com.primefactorsolutions.views.employee;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.ReportService;
import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.views.BaseEntityForm;
import com.primefactorsolutions.views.MainLayout;
import com.vaadin.componentfactory.pdfviewer.PdfViewer;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.tabs.TabSheet;
import com.vaadin.flow.component.textfield.BigDecimalField;
import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.PermitAll;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.datepicker.VDatePicker;
import org.vaadin.firitin.fields.ElementCollectionField;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Employee")
@Route(value = "/employees/:employeeId?/:action?", layout = MainLayout.class)
@Slf4j
public class EmployeeView extends BaseEntityForm<Employee> implements HasUrlParameter<String> {
private final EmployeeService employeeService;
private final ReportService reportService;
private final TeamService teamService;
private final TabSheet tabSheet = new TabSheet();
// TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente.
private final TextField username = createTextField("Username", 30, true);
private final TextField firstName = createTextField("Nombres", 30, true);
private final TextField lastName = createTextField("Apellidos", 30, true);
private final ComboBox<Employee.Status> status = createStatusComboBox();
private final ComboBox<Employee.Gender> gender = createGenderComboBox();
private final VDatePicker birthday = new VDatePicker("Fecha de Nacimiento");
private final TextField age = createTextField("Edad", 3, false);
private final TextField birthCity = createTextField("Ciudad y País de Nacimiento", 30, false);
private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false);
private final TextField localAddress = createTextField("Departamento y Provincia de Residencia", 30, false);
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
private final TextField numberOfChildren = createTextField("Numero de Hijos", 1, false);
private final TextField ci = createTextField("CI", 10, false);
private final TextField issuedIn = createTextField("Lugar de Expedicion", 10, false);
private final TextField phoneNumber = createTextField("Teléfono", 8, false);
private final EmailField personalEmail = createEmailField("E-mail");
private final TextField phoneNumberProfessional = createTextField("Teléfono Laboral", 8, false);
private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false);
private final TextField emergencyCAddress = createTextField("Dirección de Contacto", 50, false);
private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false);
private final EmailField emergencyCEmail = createEmailField("Email de Contacto");
private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload upload = new Upload(buffer);
private final Image profileImagePreview = new Image();
private final ElementCollectionField<Certification> educationTitles =
new ElementCollectionField<>(Certification.class);
private final ElementCollectionField<Certification> certifications =
new ElementCollectionField<>(Certification.class);
private final TextField recognition = createTextField("Reconocimientos", 30, false);
private final TextField achievements = createTextField("Logros Profesionales", 30, false);
private final ElementCollectionField<Language> languages = new ElementCollectionField<>(Language.class);
private final TextField cod = createTextField("Codigo de Empleado", 20, false);
private final TextField position = createTextField("Cargo", 30, false);
private final ComboBox<Team> team = new ComboBox<>("Equipo");
private final TextField leadManager = createTextField("Lead/Manager", 30, false);
private final VDatePicker dateOfEntry = new VDatePicker("Fecha de Ingreso");
private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro");
private final ComboBox<Employee.ContractType> contractType = createContractTypeComboBox();
private final TextField customContractType = createCustomContractTypeField();
private final TextField seniority = createTextField("Antiguedad", 30, false);
private final BigDecimalField salaryTotal = createBigDecimalField("Salario Total", false);
private final BigDecimalField salaryBasic = createBigDecimalField("Salario Basico", false);
private final BigDecimalField tenureBonus = createBigDecimalField("Bono de Antiguedad", false);
private final BigDecimalField professionalBonus = createBigDecimalField("Bono Profesional", false);
private final TextField bankName = createTextField("Banco", 30, false);
private final TextField accountNumber = createTextField("Nro. de Cuenta", 30, false);
private final TextField gpss = createTextField("Código Único de Asegurado (GPSS)", 30, false);
private final TextField sss = createTextField("Matricula de Asegurado (SSS)", 30, false);
private final TextField beneficiarie1 = createTextField("Derechohabiente 1", 30, false);
private final TextField beneficiarie2 = createTextField("Derechohabiente 2", 30, false);
private static final String NOTIFICATION_SAVE_SUCCESS = "Employee saved successfully.";
private static final String NOTIFICATION_VALIDATE_ERROR = "Please complete the required fields correctly.";
private static final String PHONE_NUMBER_ERROR_MESSAGE = "El teléfono debe contener solo números.";
private final Button reportButton = new Button("Generar Ficha de Contratación");
private final Dialog dialog = new Dialog();
private final PdfViewer pdfViewer = new PdfViewer();
private final Button excelReportButton = new Button("Información General del Empleado Excel",
VaadinIcon.FILE_TABLE.create());
private final H3 generalInfo = new H3("Información General");
private final H5 imagenSub = new H5("Insertar una imagen .jpg:");
private final H3 contEmerg = new H3("Contacto de Emergencia");
private final H3 titulos = new H3("Titulos Profesionales y Estudios Realizados");
private final H3 certif = new H3("Certificaciones Profesionales");
private final H3 logros = new H3("Otros Logros y Reconocimientos");
private final H3 idioma = new H3("Dominio de Idiomas");
private final H2 titleAdminInfo = new H2("Información Administrativa");
private final H3 infoCont = new H3("Información de Contratación");
private final H3 datBanc = new H3("Datos Bancarios");
private final H3 datGest = new H3("Datos Gestora Pública y Seguro Social");
public EmployeeView(final AuthenticationContext authenticationContext,
final EmployeeService employeeService,
final ReportService reportService,
final TeamService teamService) {
super(authenticationContext, Employee.class);
this.employeeService = employeeService;
this.reportService = reportService;
this.teamService = teamService;
excelReportButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
configureComponents();
addClassName("main-layout");
excelReportButton.addClickListener(e ->
getUI().ifPresent(ui ->
ui.navigate(EmployeeReportView.class, getEntity().getId().toString())
)
);
setSavedHandler(this::saveEmployee);
}
private void makeUpperCase(final TextField textField) {
textField.addValueChangeListener(event -> {
String value = event.getValue();
if (value != null) {
textField.setValue(value.toUpperCase());
}
});
}
private void configureComponents() {
tabSheet.setWidthFull();
phoneNumber.setValueChangeMode(ValueChangeMode.EAGER);
phoneNumber.addValueChangeListener(e -> validatePhoneNumber(phoneNumber, e.getValue()));
emergencyCPhone.setValueChangeMode(ValueChangeMode.EAGER);
emergencyCPhone.addValueChangeListener(e -> validatePhoneNumber(emergencyCPhone, e.getValue()));
firstName.setValueChangeMode(ValueChangeMode.EAGER);
firstName.addValueChangeListener(e -> validateNameField(firstName, e.getValue()));
lastName.setValueChangeMode(ValueChangeMode.EAGER);
lastName.addValueChangeListener(e -> validateNameField(lastName, e.getValue()));
createTeamComboBox();
configureUpload();
reportButton.setVisible(true);
birthday.addValueChangeListener(event -> calculateAge());
birthday.setMin(LocalDate.now().minusYears(100));
birthday.setMax(LocalDate.now().minusYears(18));
birthday.addValueChangeListener(event -> {
LocalDate selectedDate = event.getValue();
if (selectedDate != null && ChronoUnit.YEARS.between(selectedDate, LocalDate.now()) < 18) {
birthday.setInvalid(true);
birthday.setErrorMessage("El empleado debe ser mayor o tener 18 años");
} else {
birthday.setInvalid(false);
}
});
salaryTotal.addValueChangeListener(event -> calculateSalaryTotal());
dateOfEntry.addValueChangeListener(event -> calculateSeniority());
dateOfExit.addValueChangeListener(event -> {
if (event.getValue() != null) {
status.setValue(Employee.Status.INACTIVE);
} else {
status.setValue(Employee.Status.ACTIVE);
}
});
reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
var employee = getEntity();
byte[] pdfContent = reportService.writeAsPdf("ficha", employee);
var resource = new StreamResource("ficha.pdf", () -> new ByteArrayInputStream(pdfContent));
pdfViewer.setSrc(resource);
dialog.open();
});
makeUpperCase(firstName);
makeUpperCase(lastName);
makeUpperCase(birthCity);
makeUpperCase(residenceAddress);
makeUpperCase(localAddress);
makeUpperCase(position);
makeUpperCase(emergencyCName);
makeUpperCase(emergencyCAddress);
makeUpperCase(ci);
makeUpperCase(issuedIn);
makeUpperCase(recognition);
makeUpperCase(achievements);
makeUpperCase(cod);
makeUpperCase(leadManager);
makeUpperCase(seniority);
makeUpperCase(bankName);
makeUpperCase(accountNumber);
makeUpperCase(gpss);
makeUpperCase(sss);
makeUpperCase(beneficiarie1);
makeUpperCase(beneficiarie2);
initDialog();
}
private void validateNameField(final TextField textField, final String value) {
if (!value.matches("^[a-zA-ZáéíóúÁÉÍÓÚñÑ\\s]*$")) {
textField.setInvalid(true);
textField.setErrorMessage("Este campo solo debe contener letras.");
} else {
textField.setInvalid(false);
}
}
private void calculateAge() {
if (birthday.getValue() != null) {
int currentYear = java.time.LocalDate.now().getYear();
int birthYear = birthday.getValue().getYear();
int ages = currentYear - birthYear;
age.setValue(String.valueOf(ages));
birthday.setInvalid(ages < 18);
}
}
private void calculateSeniority() {
LocalDate entryDate = dateOfEntry.getValue();
LocalDate exitDate = dateOfExit.getValue() != null ? dateOfExit.getValue() : LocalDate.now();
if (entryDate != null) {
long yearsOfService = ChronoUnit.YEARS.between(entryDate, exitDate);
String seniorityValue = yearsOfService + " años ";
seniority.setValue(seniorityValue);
} else {
seniority.setValue("No disponible");
}
}
private void calculateSalaryTotal() {
if (contractType.getValue() == Employee.ContractType.CONTRATO_LABORAL) {
salaryBasic.setVisible(true);
professionalBonus.setVisible(true);
tenureBonus.setVisible(true);
salaryTotal.setVisible(true);
salaryBasic.addValueChangeListener(event -> updateTotalSalary());
professionalBonus.addValueChangeListener(event -> updateTotalSalary());
tenureBonus.addValueChangeListener(event -> updateTotalSalary());
} else {
salaryBasic.setVisible(false);
professionalBonus.setVisible(false);
tenureBonus.setVisible(false);
salaryTotal.setVisible(true);
}
salaryTotal.getValue();
}
private void updateTotalSalary() {
try {
final BigDecimal basic = salaryBasic.getValue();
final BigDecimal bonus = professionalBonus.getValue();
final BigDecimal seniorityBonus = tenureBonus.getValue();
final BigDecimal totalSalary = basic.add(bonus).add(seniorityBonus);
salaryTotal.setValue(totalSalary);
} catch (Exception e) {
salaryTotal.setValue(BigDecimal.valueOf(0.0));
}
}
private double parseDoubleValue(final String value) {
try {
return value != null && !value.isEmpty() ? Double.parseDouble(value) : 0.0;
} catch (NumberFormatException e) {
return 0.0;
}
}
private void configureUpload() {
upload.setAcceptedFileTypes("image/jpeg", "image/png");
upload.setMaxFileSize(1024 * 1024);
upload.addSucceededListener(event -> {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
buffer.getInputStream().transferTo(outputStream);
byte[] imageBytes = outputStream.toByteArray();
String base64Image = Base64.getEncoder().encodeToString(imageBytes);
getEntity().setProfileImage(base64Image);
profileImagePreview.setSrc("data:image/png;base64," + base64Image);
profileImagePreview.setMaxWidth("150px");
profileImagePreview.setMaxHeight("150px");
} catch (IOException e) {
Notification.show("Error al subir la imagen: " + e.getMessage());
log.error("Error uploading image", e);
} catch (Exception e) {
Notification.show("Error en el servidor al procesar la imagen.");
log.error("Error uploading image", e);
}
});
}
private void validatePhoneNumber(final TextField textField, final String value) {
if (!value.matches("\\d*")) {
textField.setErrorMessage(PHONE_NUMBER_ERROR_MESSAGE);
}
}
private void initDialog() {
pdfViewer.setSizeFull();
H2 headline = new H2("Ficha Empleado");
headline.getStyle().set("margin", "var(--lumo-space-m) 0 0 0")
.set("font-size", "1.5em").set("font-weight", "bold");
final Button cancelDialogButton = new Button("Close", e -> dialog.close());
final HorizontalLayout buttonLayout = new HorizontalLayout(cancelDialogButton);
buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
final VerticalLayout dialogLayout = new VerticalLayout(headline, pdfViewer, buttonLayout);
dialogLayout.getStyle().set("height", "100%");
dialogLayout.getStyle().set("overflow", "hidden");
dialogLayout.getStyle().set("display", "flex");
dialogLayout.getStyle().set("flex-direction", "column");
dialogLayout.setPadding(false);
dialogLayout.setAlignItems(FlexComponent.Alignment.STRETCH);
dialogLayout.getStyle().set("width", "700px").set("max-width", "100%");
dialogLayout.getStyle().set("height", "800px").set("max-height", "100%");
dialog.add(dialogLayout);
}
private ComboBox<Employee.MaritalStatus> createMaritalStatusComboBox() {
ComboBox<Employee.MaritalStatus> comboBox = new ComboBox<>("Estado Civil");
comboBox.setItems(Employee.MaritalStatus.values());
comboBox.setItemLabelGenerator(Employee.MaritalStatus::name);
return comboBox;
}
private ComboBox<Employee.Status> createStatusComboBox() {
ComboBox<Employee.Status> comboBox = new ComboBox<>("Estado");
comboBox.setItems(Employee.Status.values());
comboBox.setItemLabelGenerator(Employee.Status::name);
comboBox.setRequiredIndicatorVisible(true);
return comboBox;
}
private ComboBox<Employee.ContractType> createContractTypeComboBox() {
ComboBox<Employee.ContractType> comboBox = new ComboBox<>("Tipo de Contrato");
comboBox.setItems(Employee.ContractType.values());
comboBox.setItemLabelGenerator(Employee.ContractType::name);
comboBox.setRequiredIndicatorVisible(true);
comboBox.setWidth("300px");
comboBox.addValueChangeListener(event -> handleContractTypeChange(event.getValue()));
return comboBox;
}
private TextField createCustomContractTypeField() {
TextField textField = new TextField("Especificar Tipo de Contrato");
textField.setPlaceholder("Ingrese el tipo de contrato...");
textField.setVisible(false);
textField.setWidth("300px");
return textField;
}
private void handleContractTypeChange(final Employee.ContractType selectedType) {
if (selectedType == Employee.ContractType.OTROS) {
customContractType.setVisible(true);
customContractType.setRequired(true);
} else {
customContractType.setVisible(false);
customContractType.clear();
customContractType.setRequired(false);
}
}
private VerticalLayout createContentLayout() {
VerticalLayout contentLayout = new VerticalLayout();
contentLayout.setWidth("100%");
return contentLayout;
}
private TextField createTextField(final String label, final int maxLength, final boolean required) {
TextField textField = new TextField(label);
textField.setWidthFull();
textField.setMaxLength(maxLength);
textField.setRequired(required);
return textField;
}
private BigDecimalField createBigDecimalField(final String label, final boolean required) {
BigDecimalField textField = new BigDecimalField(label);
textField.setWidthFull();
textField.setRequired(required);
return textField;
}
private EmailField createEmailField(final String label) {
EmailField emailField = new EmailField(label);
emailField.setWidthFull();
emailField.setMaxLength(50);
return emailField;
}
private void createTeamComboBox() {
List<Team> teams = teamService.findAllTeams();
team.setItems(teams);
team.setItemLabelGenerator(Team::getName);
team.setWidthFull();
}
private ComboBox<Employee.Gender> createGenderComboBox() {
ComboBox<Employee.Gender> comboBox = new ComboBox<>("Genero");
comboBox.setItems(Employee.Gender.values());
comboBox.setItemLabelGenerator(Employee.Gender::name);
comboBox.setRequiredIndicatorVisible(true);
return comboBox;
}
private boolean validateForm() {
return !firstName.isEmpty() && !lastName.isEmpty() && status.getValue() != null;
}
private void setVacationDuration(
final Employee employee,
final TimeOffRequest request,
final LocalDate referenceDate) {
double yearsOfService = ChronoUnit.YEARS.between(employee.getDateOfEntry(), referenceDate);
request.setAvailableDays(calculateAvailableDays(yearsOfService));
}
private double calculateAvailableDays(final double yearsOfService) {
if (yearsOfService > 10) {
return 30.0;
} else if (yearsOfService > 5) {
return 20.0;
} else if (yearsOfService > 1) {
return 15.0;
} else {
return 0.0;
}
}
private void saveEmployee(final Employee employee) {
if (validateForm()) {
employee.setStatus(status.getValue());
employee.setAge(age.getValue());
employee.setSalaryBasic(salaryBasic.getValue());
employee.setProfessionalBonus(professionalBonus.getValue());
employee.setTenureBonus(tenureBonus.getValue());
employee.setSalaryTotal((salaryTotal.getValue()));
employee.setContractType(contractType.getValue());
if (contractType.getValue() == Employee.ContractType.OTROS) {
employee.setCustomContractType(customContractType.getValue());
} else {
employee.setCustomContractType(null);
}
employeeService.createOrUpdate(employee);
Notification.show(NOTIFICATION_SAVE_SUCCESS);
getUI().ifPresent(ui -> ui.navigate(EmployeesListView.class));
} else {
Notification.show(NOTIFICATION_VALIDATE_ERROR, 3000, Notification.Position.MIDDLE);
}
}
private void enableEditMode() {
setFieldsEditable();
}
@Override
public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters();
final String s = params.get("employeeId").orElse(null);
if ("new".equals(action)) {
setEntityWithEnabledSave(new Employee());
setFieldsEditable();
upload.setVisible(true);
profileImagePreview.setVisible(true);
salaryTotal.setValue(BigDecimal.valueOf(0.0));
} else {
final Employee employee;
if (s != null) {
final UUID employeeId = UUID.fromString(s);
employee = employeeService.getEmployee(employeeId);
} else {
employee = employeeService.getEmployee(getEmployeeId().get());
}
if ("edit".equals(action)) {
setEntityWithEnabledSave(employee);
setEditHandler(null);
status.setValue(employee.getStatus());
setFieldsEditable();
upload.setVisible(true);
displayProfileImage(employee);
profileImagePreview.setVisible(true);
salaryTotal.setValue(employee.getSalaryTotal());
} else if ("view".equals(action) || "me".equals(action)) {
setEntity(employee);
setEditHandler(__ -> getUI().ifPresent(ui -> ui.navigate("/employees/" + employee.getId() + "/edit")));
setFieldsReadOnly();
displayProfileImage(employee);
salaryTotal.setValue(employee.getSalaryTotal());
}
}
}
private void displayProfileImage(final Employee employee) {
if (employee.getProfileImage() != null && !employee.getProfileImage().isEmpty()) {
profileImagePreview.setSrc("data:image/jpeg;base64," + employee.getProfileImage());
profileImagePreview.setVisible(true);
profileImagePreview.setMaxWidth("250px");
profileImagePreview.setMaxHeight("250px");
upload.setVisible(true);
} else {
profileImagePreview.setVisible(true);
upload.setVisible(true);
}
}
private void setFieldsReadOnly() {
username.setReadOnly(true);
firstName.setReadOnly(true);
lastName.setReadOnly(true);
status.setReadOnly(true);
birthday.setReadOnly(true);
birthCity.setReadOnly(true);
residenceAddress.setReadOnly(true);
localAddress.setReadOnly(true);
maritalStatus.setReadOnly(true);
numberOfChildren.setReadOnly(true);
phoneNumber.setReadOnly(true);
personalEmail.setReadOnly(true);
phoneNumberProfessional.setReadOnly(true);
position.setReadOnly(true);
team.setReadOnly(true);
emergencyCName.setReadOnly(true);
emergencyCAddress.setReadOnly(true);
emergencyCPhone.setReadOnly(true);
emergencyCEmail.setReadOnly(true);
profileImagePreview.setVisible(true);
age.setReadOnly(true);
gender.setReadOnly(true);
status.setReadOnly(true);
ci.setReadOnly(true);
issuedIn.setReadOnly(true);
educationTitles.setReadOnly(true);
certifications.setReadOnly(true);
recognition.setReadOnly(true);
achievements.setReadOnly(true);
languages.setReadOnly(true);
cod.setReadOnly(true);
leadManager.setReadOnly(true);
dateOfEntry.setReadOnly(true);
dateOfExit.setReadOnly(true);
contractType.setReadOnly(true);
customContractType.setReadOnly(true);
seniority.setReadOnly(true);
salaryTotal.setReadOnly(true);
salaryBasic.setReadOnly(true);
professionalBonus.setReadOnly(true);
tenureBonus.setReadOnly(true);
bankName.setReadOnly(true);
accountNumber.setReadOnly(true);
gpss.setReadOnly(true);
sss.setReadOnly(true);
beneficiarie1.setReadOnly(true);
beneficiarie2.setReadOnly(true);
}
private void setFieldsEditable() {
username.setReadOnly(false);
firstName.setReadOnly(false);
lastName.setReadOnly(false);
status.setReadOnly(false);
birthday.setReadOnly(false);
birthCity.setReadOnly(false);
residenceAddress.setReadOnly(false);
localAddress.setReadOnly(false);
maritalStatus.setReadOnly(false);
numberOfChildren.setReadOnly(false);
phoneNumber.setReadOnly(false);
personalEmail.setReadOnly(false);
phoneNumberProfessional.setReadOnly(false);
position.setReadOnly(false);
team.setReadOnly(false);
emergencyCName.setReadOnly(false);
emergencyCAddress.setReadOnly(false);
emergencyCPhone.setReadOnly(false);
emergencyCEmail.setReadOnly(false);
profileImagePreview.setVisible(true);
age.setReadOnly(false);
gender.setReadOnly(false);
status.setReadOnly(false);
ci.setReadOnly(false);
issuedIn.setReadOnly(false);
educationTitles.setReadOnly(false);
certifications.setReadOnly(false);
recognition.setReadOnly(false);
achievements.setReadOnly(false);
languages.setReadOnly(false);
cod.setReadOnly(false);
leadManager.setReadOnly(false);
dateOfEntry.setReadOnly(false);
dateOfExit.setReadOnly(false);
contractType.setReadOnly(false);
customContractType.setReadOnly(false);
seniority.setReadOnly(false);
salaryTotal.setReadOnly(false);
salaryBasic.setReadOnly(false);
professionalBonus.setReadOnly(false);
tenureBonus.setReadOnly(false);
bankName.setReadOnly(false);
accountNumber.setReadOnly(false);
gpss.setReadOnly(false);
sss.setReadOnly(false);
beneficiarie1.setReadOnly(false);
beneficiarie2.setReadOnly(false);
}
@Override
protected List<Component> getFormComponents() {
tabSheet.add("Info Personal", new VerticalLayout(
generalInfo,
imagenSub,
upload,
profileImagePreview,
firstName,
lastName,
gender,
status,
birthday,
age,
birthCity,
residenceAddress,
localAddress,
maritalStatus,
ci,
issuedIn,
numberOfChildren,
phoneNumber,
personalEmail,
phoneNumberProfessional,
contEmerg,
emergencyCName,
emergencyCAddress,
emergencyCPhone,
emergencyCEmail
));
tabSheet.add("Info Profesional", new VerticalLayout(
titulos,
educationTitles,
certif,
certifications,
logros,
recognition,
achievements,
idioma,
languages
));
if (isRoleAdmin()) {
tabSheet.add("Info Administrativa", new VerticalLayout(
cod,
position,
team,
leadManager,
infoCont,
dateOfEntry,
dateOfExit,
seniority,
contractType,
customContractType,
salaryBasic,
professionalBonus,
tenureBonus,
salaryTotal,
datBanc,
bankName,
accountNumber,
datGest,
gpss,
sss,
beneficiarie1,
beneficiarie2
));
}
return List.of(
new HorizontalLayout(reportButton, excelReportButton),
username,
tabSheet,
dialog
);
}
}

View File

@ -1,206 +0,0 @@
package com.primefactorsolutions.views.employee;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.views.BaseView;
import com.primefactorsolutions.views.Constants;
import com.primefactorsolutions.views.MainLayout;
import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.security.AuthenticationContext;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.vaadin.firitin.components.grid.PagingGrid;
import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.data.provider.SortDirection;
import org.springframework.context.annotation.Scope;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
@SpringComponent
@Scope("prototype")
@PageTitle("Employees")
@Route(value = "/employees", layout = MainLayout.class)
@RolesAllowed("ROLE_ADMIN")
@Slf4j
public class EmployeesListView extends BaseView {
private final EmployeeService employeeService;
private final PagingGrid<Employee> employeePagingGrid = new PagingGrid<>(Employee.class);
public EmployeesListView(final AuthenticationContext authenticationContext, final EmployeeService employeeService) {
super(authenticationContext);
this.employeeService = employeeService;
setupView();
refreshGrid();
}
private void setupView() {
configureTable();
final HorizontalLayout hl = new HorizontalLayout(createAddEmployeeButton(), createExportButton());
getCurrentPageLayout().add(hl);
getCurrentPageLayout().add(employeePagingGrid);
}
private Button createExportButton() {
final StreamResource excelResource = new StreamResource("employees.xlsx", this::generateExcel);
final Anchor downloadLink = new Anchor(excelResource, "Export Employees");
downloadLink.getElement().setAttribute("download", true); // Forzar descarga
return new Button("Exportar como Excel", e -> getCurrentPageLayout().add(downloadLink));
}
private ByteArrayInputStream generateExcel() {
final List<Employee> employees = employeeService.findAllEmployees();
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet("Employees");
Row headerRow = sheet.createRow(0);
String[] headers = {
"ID", "Nombres", "Apellidos", "Status", "Genero", "Fecha de Nacimiento", "Edad",
"Ciudad y Pais de Nacimiento", "Dirección de Residencia", "Departamento y Provincia de Residencia",
"Marital Status",
"Numero de Hijos", "CI", "Expedido en", "Teléfono", "E-mail",
"Teléfono Laboral", "E-mail Laboral", "Codigo de Empleado", "Cargo",
"Equipo", "Lead/Manager", "Fecha de Ingreso", "Fecha de Retiro", "Tipo de Contrato",
"Otro Tipo de Contrato", "Antiguedad", "Salario Total"
};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
style.setFont(font);
cell.setCellStyle(style);
}
int rowIndex = 1;
for (Employee employee : employees) {
Row row = sheet.createRow(rowIndex++);
row.createCell(0).setCellValue(employee.getId().toString());
row.createCell(1).setCellValue(employee.getFirstName());
row.createCell(2).setCellValue(employee.getLastName());
row.createCell(3).setCellValue(employee.getStatus().toString());
row.createCell(4).setCellValue(employee.getGender() != null ? employee.getGender().toString() : "");
row.createCell(5).setCellValue(employee.getBirthday() != null ? employee.getBirthday()
.toString() : "");
row.createCell(6).setCellValue(employee.getAge() != null ? employee.getAge() : "");
row.createCell(7).setCellValue(employee.getBirthCity() != null ? employee.getBirthCity() : "");
row.createCell(8).setCellValue(employee.getResidenceAddress() != null ? employee
.getResidenceAddress() : "");
row.createCell(9).setCellValue(employee.getLocalAddress() != null ? employee.getLocalAddress() : "");
row.createCell(10).setCellValue(employee.getMaritalStatus() != null ? employee.getMaritalStatus()
.toString() : "");
row.createCell(11).setCellValue(employee.getNumberOfChildren() != null ? employee
.getNumberOfChildren() : "");
row.createCell(12).setCellValue(employee.getCi() != null ? employee.getCi() : "");
row.createCell(13).setCellValue(employee.getIssuedIn() != null ? employee.getIssuedIn() : "");
row.createCell(14).setCellValue(employee.getPhoneNumber() != null ? employee.getPhoneNumber() : "");
row.createCell(15).setCellValue(employee.getPersonalEmail() != null ? employee
.getPersonalEmail() : "");
row.createCell(16).setCellValue(employee.getPhoneNumberProfessional() != null ? employee
.getPhoneNumberProfessional() : "");
row.createCell(17).setCellValue(employee.getProfessionalEmail() != null ? employee
.getProfessionalEmail() : "");
row.createCell(18).setCellValue(employee.getCod() != null ? employee.getCod() : "");
row.createCell(19).setCellValue(employee.getPosition() != null ? employee.getPosition() : "");
row.createCell(20).setCellValue(employee.getTeam() != null ? employee.getTeam().getName() : "");
row.createCell(21).setCellValue(employee.getLeadManager() != null ? employee.getLeadManager() : "");
row.createCell(22).setCellValue(employee.getDateOfEntry() != null ? employee.getDateOfEntry()
.toString() : "");
row.createCell(23).setCellValue(employee.getDateOfExit() != null ? employee.getDateOfExit()
.toString() : "");
row.createCell(24).setCellValue(employee.getContractType() != null ? employee.getContractType()
.toString() : "");
row.createCell(25).setCellValue(employee.getCustomContractType() != null
? employee.getCustomContractType()
: "");
row.createCell(26).setCellValue(employee.getSeniority() != null ? employee.getSeniority() : "");
row.createCell(27).setCellValue(employee.getSalaryTotal() != null
? employee.getSalaryTotal().toString()
: "");
}
workbook.write(out);
return new ByteArrayInputStream(out.toByteArray());
} catch (IOException e) {
log.error("Error generating excel", e);
return null;
}
}
private void configureTable() {
employeePagingGrid.setColumns("username", "firstName", "lastName", "status");
employeePagingGrid.addComponentColumn(employee -> MenuBarUtils.menuBar(
Pair.of("View", __ -> navigateToEmployeeView(employee)),
Pair.of("Edit", __ -> navigateToEditView(employee))));
setupPagingGrid();
}
private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) {
final Button button = new Button(label);
if (isPrimary) {
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
}
button.addClickListener(event -> onClickAction.run());
return button;
}
private Button createAddEmployeeButton() {
return createButton("Add Employee", this::navigateToAddEmployeeView, true);
}
private void navigateToEditView(final Employee employee) {
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/edit"));
}
private void navigateToEmployeeView(final Employee employee) {
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString() + "/view"));
}
private void navigateToAddEmployeeView() {
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, "new"));
}
private void setupPagingGrid() {
employeePagingGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
employeePagingGrid.setPageSize(Constants.PAGE_SIZE);
}
private void refreshGrid() {
employeePagingGrid.setPagingDataProvider((page, pageSize) -> fetchEmployees((int) page, pageSize));
}
private List<Employee> fetchEmployees(final int page, final int pageSize) {
int start = page * pageSize;
if (hasSortOrder()) {
return fetchSortedEmployees(start, pageSize);
}
return employeeService.findEmployees(start, pageSize);
}
private boolean hasSortOrder() {
return !employeePagingGrid.getSortOrder().isEmpty();
}
private List<Employee> fetchSortedEmployees(final int start, final int pageSize) {
final GridSortOrder<Employee> sortOrder = employeePagingGrid.getSortOrder().getFirst();
return employeeService.findEmployees(start, pageSize,
sortOrder.getSorted().getKey(),
sortOrder.getDirection() == SortDirection.ASCENDING);
}
}

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