Compare commits

...

9 Commits

Author SHA1 Message Date
Melina Gutierrez
c5484de9a0 #46 Perfil de Empleado - Registro Semanal mas reporte, falta mejorar el reporte semanal
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m31s
2024-10-29 14:55:57 -04:00
Melina Gutierrez
4ad745145f #46 Perfil de Empleado - Registro Semanal 2024-10-22 15:04:40 -04:00
Melina Gutierrez
e3d672c00b #46 Perfil de Empleado - Registro Semanal Y Mensual falta bd 2024-10-21 13:07:42 -04:00
Melina Gutierrez
6c1c0e5ea7 #46 Perfil de Empleado - Registro Semanal Y Mensual falta bd 2024-10-18 15:01:37 -04:00
Melina Gutierrez
2aec8ff05a #46 Perfil de Empleado - Registro Semanal Y Mensual falta bd 2024-10-17 15:44:28 -04:00
Melina Gutierrez
b19496ca15 #46 Perfil de Empleado - Registro Mensual 2024-10-16 15:26:31 -04:00
Melina Gutierrez
b4826f1720 #46 Perfil de Empleado - Registro Semanal de Horas trabajadas con el numero de semanas pero sin la bd 2024-10-16 15:08:08 -04:00
Melina Gutierrez
b5396ae373 #46 Perfil de Empleado - Registro Semanal de Horas trabajadas con el numero de semanas 2024-10-15 10:11:14 -04:00
Melina Gutierrez
3e57c50378 #46 Perfil de Empleado - Registro Semanal de Horas trabajadas 2024-10-14 12:56:02 -04:00
13 changed files with 789 additions and 253 deletions

View File

@ -1,16 +1,14 @@
name: Builder name: PR Builder
run-name: ${{ gitea.actor }} building run-name: ${{ gitea.actor }} building PR
on: on: [pull_request]
push:
branches:
- main
jobs: jobs:
Build-Project: Build-PR:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: 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 }}." - 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 package - name: Build PR
if: gitea.base_ref == 'main'
run: | 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 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 }}." - run: echo "This job's status is ${{ job.status }}."

35
pom.xml
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<!-- Project from https://start.vaadin.com/project/6f77deb3-8326-4444-8c05-0398f79d7fee --> <!-- Project from https://start.vaadin.com/project/6f77deb3-8326-4444-8c05-0398f79d7fee -->
<groupId>com.primefactorsolutions</groupId> <groupId>com.primefactorsolutions</groupId>
@ -181,6 +181,21 @@
<artifactId>viritin</artifactId> <artifactId>viritin</artifactId>
<version>2.8.22</version> <version>2.8.22</version>
</dependency> </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>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency> <dependency>
<groupId>com.flowingcode.addons</groupId> <groupId>com.flowingcode.addons</groupId>
<artifactId>simple-timer</artifactId> <artifactId>simple-timer</artifactId>
@ -380,22 +395,8 @@
</plugin> </plugin>
<!-- Runs the integration tests (*IT) after the server is started --> <!-- Runs the integration tests (*IT) after the server is started -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<trimStackTrace>false</trimStackTrace>
<enableAssertions>true</enableAssertions>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</profile> </profile>

View File

@ -0,0 +1,111 @@
package com.primefactorsolutions.model;
public final class Actividad {
private String nombre;
private double lunes;
private double martes;
private double miercoles;
private double jueves;
private double viernes;
private double sabado;
private double domingo;
public Actividad(final Builder builder) {
this.nombre = builder.nombre;
this.lunes = builder.lunes;
this.martes = builder.martes;
this.miercoles = builder.miercoles;
this.jueves = builder.jueves;
this.viernes = builder.viernes;
this.sabado = builder.sabado;
this.domingo = builder.domingo;
}
public String getNombre() {
return nombre;
}
public double getLunes() {
return lunes;
}
public double getMartes() {
return martes;
}
public double getMiercoles() {
return miercoles;
}
public double getJueves() {
return jueves;
}
public double getViernes() {
return viernes;
}
public double getSabado() {
return sabado;
}
public double getDomingo() {
return domingo;
}
// Builder para crear instancias de Actividad
public static class Builder {
private String nombre;
private double lunes;
private double martes;
private double miercoles;
private double jueves;
private double viernes;
private double sabado;
private double domingo;
public Builder nombre(final String nombre) {
this.nombre = nombre;
return this;
}
public Builder lunes(final double horas) {
this.lunes = horas;
return this;
}
public Builder martes(final double horas) {
this.martes = horas;
return this;
}
public Builder miercoles(final double horas) {
this.miercoles = horas;
return this;
}
public Builder jueves(final double horas) {
this.jueves = horas;
return this;
}
public Builder viernes(final double horas) {
this.viernes = horas;
return this;
}
public Builder sabado(final double horas) {
this.sabado = horas;
return this;
}
public Builder domingo(final double horas) {
this.domingo = horas;
return this;
}
public Actividad build() {
return new Actividad(this);
}
}
}

View File

@ -0,0 +1,56 @@
package com.primefactorsolutions.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import java.util.UUID;
@Entity
public class HoursWorked extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@ManyToOne
private Employee employee;
private int weekNumber;
private double totalHours;
public HoursWorked() { }
public UUID getId() {
return id;
}
public void setId(final UUID id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(final Employee value) {
this.employee = value;
}
public int getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(final int weekNumber) {
this.weekNumber = weekNumber;
}
public double getTotalHours() {
return totalHours;
}
public void setTotalHours(final double totalHours) {
this.totalHours = totalHours;
}
}

View File

@ -2,10 +2,12 @@ package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, UUID> { public interface EmployeeRepository extends JpaRepository<Employee, UUID> {
Optional<Employee> findByUsername(String username); Optional<Employee> findByUsername(String username);
} }

View File

@ -0,0 +1,10 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.HoursWorked;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface HoursWorkedRepository extends JpaRepository<HoursWorked, Long> {
// Puedes definir consultas personalizadas aquí si es necesario.
}

View File

@ -0,0 +1,35 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.repositories.HoursWorkedRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class HoursWorkedService {
private final HoursWorkedRepository hoursWorkedRepository;
@Autowired
public HoursWorkedService(final HoursWorkedRepository hoursWorkedRepository) {
this.hoursWorkedRepository = hoursWorkedRepository;
}
public List<HoursWorked> findAll() {
return hoursWorkedRepository.findAll();
}
public HoursWorked saveHoursWorked(final HoursWorked hoursWorked) {
return hoursWorkedRepository.save(hoursWorked);
}
public HoursWorked save(final HoursWorked hoursWorked) {
return hoursWorkedRepository.save(hoursWorked);
}
public void deleteHoursWorked(final Long id) {
hoursWorkedRepository.deleteById(id);
}
}

View File

@ -1,72 +1,76 @@
package com.primefactorsolutions.service; package com.primefactorsolutions.service;
import com.openhtmltopdf.pdfboxout.PdfBoxRenderer; import com.primefactorsolutions.repositories.HoursWorkedRepository;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import freemarker.template.Configuration; import org.apache.poi.ss.usermodel.*;
import freemarker.template.DefaultObjectWrapper; import org.springframework.beans.factory.annotation.Autowired;
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.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.*; import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets; import java.io.IOException;
import java.util.TimeZone; import java.util.List;
import java.util.Map;
@Service @Service
public class ReportService { public class ReportService {
@SneakyThrows private final HoursWorkedRepository hoursWorkedRepository;
public byte[] writeAsPdf(final String reportName, final Object model) {
try (var os = new ByteArrayOutputStream()) {
writeAsPdf(reportName, model, os);
@Autowired
public ReportService(final HoursWorkedRepository hoursWorkedRepository) {
this.hoursWorkedRepository = hoursWorkedRepository;
}
// Este método ahora solo crea el archivo Excel a partir de los datos que recibe.
public byte[] writeAsExcel(final String reportName, final List<String> headers,
final List<Map<String, Object>> data)
throws IOException {
return createExcelFile(reportName, headers, data);
}
private byte[] createExcelFile(final String reportName, final List<String> headers,
final List<Map<String, Object>> data)
throws IOException {
try (Workbook workbook = new XSSFWorkbook();
ByteArrayOutputStream os = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet(reportName);
// Crear encabezados
Row headerRow = sheet.createRow(0);
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
for (int i = 0; i < data.size(); i++) {
Row dataRow = sheet.createRow(i + 1);
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 instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
} else if (value == null) {
cell.setCellValue(""); // Manejo de valores nulos
}
}
}
workbook.write(os);
return os.toByteArray(); 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 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;
}
} }

View File

@ -4,9 +4,7 @@ import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.ReportService; import com.primefactorsolutions.service.ReportService;
import com.vaadin.componentfactory.pdfviewer.PdfViewer; import com.vaadin.componentfactory.pdfviewer.PdfViewer;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
@ -24,14 +22,12 @@ import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer; import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.data.value.ValueChangeMode; import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.*; import com.vaadin.flow.router.*;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.datepicker.VDatePicker; import org.vaadin.firitin.components.datepicker.VDatePicker;
import org.vaadin.firitin.form.BeanValidationForm; import org.vaadin.firitin.form.BeanValidationForm;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Base64; import java.util.Base64;
@ -156,13 +152,13 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
reportButton.setVisible(true); reportButton.setVisible(true);
birthday.addValueChangeListener(event -> calculateAge()); birthday.addValueChangeListener(event -> calculateAge());
reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> { // reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
var employee = getEntity(); // var employee = getEntity();
byte[] pdfContent = reportService.writeAsPdf("ficha", employee); // byte[] pdfContent = reportService.writeAsPdf("ficha", employee);
var resource = new StreamResource("ficha.pdf", () -> new ByteArrayInputStream(pdfContent)); // var resource = new StreamResource("ficha.pdf", () -> new ByteArrayInputStream(pdfContent));
pdfViewer.setSrc(resource); // pdfViewer.setSrc(resource);
dialog.open(); // dialog.open();
}); // });
initDialog(); initDialog();
} }

View File

@ -0,0 +1,161 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.Actividad;
import com.primefactorsolutions.service.EmployeeService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import java.time.LocalDate;
import java.util.List;
@SpringComponent
@PermitAll
@Scope("prototype")
@PageTitle("Hours Worked Month")
@Route(value = "/hours-worked-month/me", layout = MainLayout.class)
public class HoursWorkedMonthView extends VerticalLayout {
private final EmployeeService employeeService;
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
private final ComboBox<String> equipoDropdown = new ComboBox<>("Equipo");
private final Grid<Actividad> grid = new Grid<>(Actividad.class);
private final Label totalCompletadoLabel = new Label();
private final Label horasPendientesLabel = new Label();
private final Label totalAcumuladasLabel = new Label();
private final Label horasAdeudadasLabel = new Label();
private final Button actualizarButton = new Button("Actualizar");
private final Button guardarButton = new Button("Guardar");
private final Button cerrarButton = new Button("Cerrar");
private LocalDate selectedMonth;
@Autowired
public HoursWorkedMonthView(final EmployeeService employeeService) {
this.employeeService = employeeService;
configurarVista();
}
private void configurarVista() {
DatePicker monthPicker = new DatePicker("Selecciona un mes");
monthPicker.setValue(LocalDate.now());
monthPicker.addValueChangeListener(event -> {
selectedMonth = event.getValue().withDayOfMonth(1);
//cargarDatosMes(selectedMonth);
});
equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3");
equipoDropdown.setWidth("250px");
setEmployeeComboBoxProperties();
configurarGrid();
actualizarButton.addClickListener(event -> actualizarDatos());
guardarButton.addClickListener(event -> guardarActividades());
cerrarButton.addClickListener(event -> closeView());
HorizontalLayout headerLayout = new HorizontalLayout(monthPicker, equipoDropdown, employeeComboBox);
add(
headerLayout, grid, totalCompletadoLabel,
horasPendientesLabel, totalAcumuladasLabel,
horasAdeudadasLabel, actualizarButton,
guardarButton, cerrarButton);
}
private void setEmployeeComboBoxProperties() {
employeeComboBox.setWidth("250px");
employeeComboBox.setPlaceholder("Buscar empleado...");
employeeComboBox.setItems(employeeService.findAllEmployees());
employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
}
private void configurarGrid() {
grid.removeAllColumns();
grid.addColumn(Actividad::getLunes).setHeader("Lunes");
grid.addColumn(Actividad::getMartes).setHeader("Martes");
grid.addColumn(Actividad::getMiercoles).setHeader("Miércoles");
grid.addColumn(Actividad::getJueves).setHeader("Jueves");
grid.addColumn(Actividad::getViernes).setHeader("Viernes");
grid.addColumn(Actividad::getSabado).setHeader("Sábado");
grid.addColumn(Actividad::getDomingo).setHeader("Domingo");
grid.addColumn(this::calcularTotalPorDia).setHeader("Total Semanal").setKey("totalSemanal");
}
// private void cargarDatosMes(final LocalDate month) {
// List<Actividad> actividadesDelMes = obtenerActividadesDelMes(month);
// grid.setItems(actividadesDelMes);
//
// double totalCompletado = calcularTotalCompletado(actividadesDelMes);
// double horasPendientes = calcularHorasPendientes(totalCompletado);
// double totalAcumuladas = 166;
// double horasAdeudadas = 2;
//
// totalCompletadoLabel.setText("Prom. Hrs/Semana Completadas: " + totalCompletado);
// horasPendientesLabel.setText("Prom. Hrs/Semana Pendientes: " + horasPendientes);
// totalAcumuladasLabel.setText("Total Hrs./Mes Acumuladas: " + totalAcumuladas);
// horasAdeudadasLabel.setText("Total Hrs./Mes Adeudadas: " + horasAdeudadas);
// }
// private List<Actividad> obtenerActividadesDelMes(final LocalDate month) {
// LocalDate startOfMonth = month.with(TemporalAdjusters.firstDayOfMonth());
// LocalDate endOfMonth = month.with(TemporalAdjusters.lastDayOfMonth());
//
// List<Actividad> actividadesDelMes = new ArrayList<>();
//
// for (LocalDate date = startOfMonth; date.isBefore(endOfMonth.plusDays(1)); date = date.plusDays(1)) {
// Actividad actividad = new Actividad.Builder()
// .lunes(0)
// .martes(0)
// .miercoles(0)
// .jueves(0)
// .viernes(0)
// .sabado(0)
// .domingo(0)
// .build();
// actividadesDelMes.add(actividad);
// }
//
// return actividadesDelMes;
// }
private double calcularTotalCompletado(final List<Actividad> actividades) {
return actividades.stream()
.mapToDouble(this::calcularTotalPorDia)
.sum();
}
private double calcularTotalPorDia(final Actividad actividad) {
return actividad.getLunes() + actividad.getMartes() + actividad.getMiercoles()
+ actividad.getJueves() + actividad.getViernes() + actividad.getSabado()
+ actividad.getDomingo();
}
private double calcularHorasPendientes(final double totalCompletado) {
return 40 - totalCompletado;
}
private void actualizarDatos() {
Notification.show("Datos actualizados.");
}
private void guardarActividades() {
Notification.show("Actividades guardadas correctamente.");
}
private void closeView() {
getUI().ifPresent(ui -> ui.navigate(""));
}
}

View File

@ -1,19 +1,33 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Actividad;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.HoursWorkedService;
import com.vaadin.flow.component.datepicker.DatePicker; import com.vaadin.flow.component.datepicker.DatePicker;
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.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import com.vaadin.flow.component.html.Label;
import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.temporal.IsoFields;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@SpringComponent @SpringComponent
@PermitAll @PermitAll
@ -21,20 +35,107 @@ import java.time.LocalDate;
@PageTitle("Hours Worked") @PageTitle("Hours Worked")
@Route(value = "/hours-worked/me", layout = MainLayout.class) @Route(value = "/hours-worked/me", layout = MainLayout.class)
public class HoursWorkedView extends VerticalLayout { public class HoursWorkedView extends VerticalLayout {
public HoursWorkedView() { private final List<Actividad> actividades = new ArrayList<>();
H2 title = new H2("Registro de Horas Trabajadas"); private final Grid<Actividad> grid = new Grid<>(Actividad.class);
DatePicker datePicker = new DatePicker("Selecciona una fecha"); private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Employee");
datePicker.setValue(LocalDate.now()); private LocalDate selectedStartOfWeek;
private int weekNumber;
@Autowired
private final EmployeeService employeeService;
private final Label fechasLabel = new Label("Selecciona una semana para ver las fechas.");
private final Label totalCompletadoLabel = new Label();
private final Label horasPendientesLabel = new Label();
@Autowired
private final HoursWorkedService hoursWorkedService;
public HoursWorkedView(final EmployeeService employeeService, final HoursWorkedService hoursWorkedService) {
this.employeeService = employeeService;
this.hoursWorkedService = hoursWorkedService;
configurarVista();
cargarDatos();
}
private void cargarDatos() {
List<HoursWorked> listaDeHorasTrabajadas = obtenerDatos(); // Obtenemos la lista aquí
grid.setItems(actividades);
double totalHoras = calcularTotalHoras(listaDeHorasTrabajadas); // Pasa la lista aquí
}
private void setEmployeeComboBoxProperties() {
employeeComboBox.setWidth("250px");
employeeComboBox.setPlaceholder("Buscar empleado...");
employeeComboBox.setItems(employeeService.findAllEmployees());
employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName());
employeeComboBox.setAllowCustomValue(false);
employeeComboBox.addCustomValueSetListener(event ->
Notification.show("Selecciona un empleado válido de la lista.")
);
employeeComboBox.addValueChangeListener(event -> {
Employee selectedEmployee = event.getValue();
if (selectedEmployee != null) {
Notification.show("Empleado seleccionado: "
+ selectedEmployee.getFirstName() + " "
+ selectedEmployee.getLastName());
}
});
}
private int getWeekOfYear(final LocalDate date) {
return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
}
private void configurarVista() {
DatePicker fechaPicker = new DatePicker("Selecciona una fecha");
fechaPicker.addValueChangeListener(event -> {
LocalDate selectedDate = event.getValue();
if (selectedDate != null) {
selectedStartOfWeek = getStartOfWeek(selectedDate);
LocalDate endOfWeek = selectedStartOfWeek.plusDays(6);
fechasLabel.setText("Semana del " + selectedStartOfWeek + " al " + endOfWeek);
weekNumber = getWeekOfYear(selectedDate);
}
});
Button verMesButton = new Button("Ver Mes", event -> {
getUI().ifPresent(ui -> ui.navigate(HoursWorkedMonthView.class));
});
ComboBox<String> equipoDropdown = new ComboBox<>("Equipo"); ComboBox<String> equipoDropdown = new ComboBox<>("Equipo");
equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3"); // Ejemplo de datos equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3");
equipoDropdown.setWidth("250px");
TextField empleadoSearch = new TextField("Empleado (Search)"); setEmployeeComboBoxProperties();
HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, empleadoSearch); HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, employeeComboBox);
filtersLayout.setSpacing(true);
Grid<Actividad> grid = new Grid<>(Actividad.class, false); configurarGrid();
HorizontalLayout actividadFormLayout = configurarFormularioActividades();
Button actualizarButton = new Button("Actualizar Totales", event -> actualizarTotales());
Button guardarButton = new Button("Guardar", event -> guardarActividades());
Button cerrarButton = new Button("Cerrar", event -> this.closeView());
HorizontalLayout buttonsLayout = new HorizontalLayout(actualizarButton, guardarButton,
cerrarButton, verMesButton);
VerticalLayout totalesLayout = new VerticalLayout(totalCompletadoLabel, horasPendientesLabel);
totalesLayout.setSpacing(true);
totalesLayout.setPadding(true);
add(fechaPicker, fechasLabel, filtersLayout, grid, actividadFormLayout, buttonsLayout, totalesLayout);
}
private void configurarGrid() {
grid.removeAllColumns();
grid.setItems(actividades);
grid.addColumn(Actividad::getNombre).setHeader("Actividad"); grid.addColumn(Actividad::getNombre).setHeader("Actividad");
grid.addColumn(Actividad::getLunes).setHeader("Lunes"); grid.addColumn(Actividad::getLunes).setHeader("Lunes");
grid.addColumn(Actividad::getMartes).setHeader("Martes"); grid.addColumn(Actividad::getMartes).setHeader("Martes");
@ -43,155 +144,127 @@ public class HoursWorkedView extends VerticalLayout {
grid.addColumn(Actividad::getViernes).setHeader("Viernes"); grid.addColumn(Actividad::getViernes).setHeader("Viernes");
grid.addColumn(Actividad::getSabado).setHeader("Sábado"); grid.addColumn(Actividad::getSabado).setHeader("Sábado");
grid.addColumn(Actividad::getDomingo).setHeader("Domingo"); grid.addColumn(Actividad::getDomingo).setHeader("Domingo");
grid.addColumn(this::calcularTotalPorDia).setHeader("Total Día").setKey("totalDia");
grid.setItems(
new Actividad.Builder()
.nombre("Actividad 1")
.lunes(3)
.martes(3)
.miercoles(3)
.jueves(3)
.viernes(3)
.sabado(1)
.domingo(2)
.build(),
new Actividad.Builder()
.nombre("Actividad 2")
.lunes(2)
.martes(2)
.miercoles(2)
.jueves(2)
.viernes(2)
.sabado(0)
.domingo(1)
.build(),
new Actividad.Builder()
.nombre("Meeting 1")
.lunes(0)
.martes(0.5)
.miercoles(0.5)
.jueves(0)
.viernes(0)
.sabado(0.5)
.domingo(0)
.build()
);
Button actualizarButton = new Button("Actualizar");
Button guardarButton = new Button("Guardar");
Button cerrarButton = new Button("Cerrar");
HorizontalLayout buttonsLayout = new HorizontalLayout(actualizarButton, guardarButton, cerrarButton);
add(title, datePicker, filtersLayout, grid, buttonsLayout);
} }
public static final class Actividad { private HorizontalLayout configurarFormularioActividades() {
private final String nombre; TextField actividadNombre = new TextField("Actividad");
private final double lunes; actividadNombre.setWidth("200px");
private final double martes;
private final double miercoles;
private final double jueves;
private final double viernes;
private final double sabado;
private final double domingo;
private Actividad(final Builder builder) { TextField lunesHoras = crearCampoHora("Lunes");
this.nombre = builder.nombre; TextField martesHoras = crearCampoHora("Martes");
this.lunes = builder.lunes; TextField miercolesHoras = crearCampoHora("Miércoles");
this.martes = builder.martes; TextField juevesHoras = crearCampoHora("Jueves");
this.miercoles = builder.miercoles; TextField viernesHoras = crearCampoHora("Viernes");
this.jueves = builder.jueves; TextField sabadoHoras = crearCampoHora("Sábado");
this.viernes = builder.viernes; TextField domingoHoras = crearCampoHora("Domingo");
this.sabado = builder.sabado;
this.domingo = builder.domingo;
}
public static class Builder { Button agregarActividadButton = new Button("Agregar Actividad", e -> {
private String nombre; try {
private double lunes; Actividad nuevaActividad = new Actividad.Builder()
private double martes; .nombre(actividadNombre.getValue())
private double miercoles; .lunes(parseHoras(lunesHoras.getValue()))
private double jueves; .martes(parseHoras(martesHoras.getValue()))
private double viernes; .miercoles(parseHoras(miercolesHoras.getValue()))
private double sabado; .jueves(parseHoras(juevesHoras.getValue()))
private double domingo; .viernes(parseHoras(viernesHoras.getValue()))
.sabado(parseHoras(sabadoHoras.getValue()))
.domingo(parseHoras(domingoHoras.getValue()))
.build();
public Builder nombre(final String nombre) { actividades.add(nuevaActividad);
this.nombre = nombre; grid.setItems(actividades);
return this; actualizarTotales();
Notification.show("Actividad agregada correctamente");
// Limpiar los campos de entrada
actividadNombre.clear();
lunesHoras.clear();
martesHoras.clear();
miercolesHoras.clear();
juevesHoras.clear();
viernesHoras.clear();
sabadoHoras.clear();
domingoHoras.clear();
} catch (NumberFormatException ex) {
Notification.show("Error: Por favor ingresa números válidos para las horas.");
} }
});
public Builder lunes(final double lunes) { return new HorizontalLayout(
this.lunes = lunes; actividadNombre, lunesHoras, martesHoras, miercolesHoras,
return this; juevesHoras, viernesHoras, sabadoHoras, domingoHoras, agregarActividadButton);
} }
public Builder martes(final double martes) { private TextField crearCampoHora(final String placeholder) {
this.martes = martes; TextField field = new TextField(placeholder);
return this; field.setWidth("80px");
} field.setPlaceholder("0.0");
return field;
}
public Builder miercoles(final double miercoles) { private double parseHoras(final String value) {
this.miercoles = miercoles; if (value == null || value.trim().isEmpty()) {
return this; return 0.0;
} }
return Double.parseDouble(value);
}
public Builder jueves(final double jueves) { private LocalDate getStartOfWeek(final LocalDate date) {
this.jueves = jueves; WeekFields weekFields = WeekFields.of(Locale.getDefault());
return this; return date.with(weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue());
} }
public Builder viernes(final double viernes) { private double calcularTotalPorDia(final Actividad actividad) {
this.viernes = viernes; return actividad.getLunes() + actividad.getMartes() + actividad.getMiercoles()
return this; + actividad.getJueves() + actividad.getViernes() + actividad.getSabado() + actividad.getDomingo();
} }
public Builder sabado(final double sabado) { private void actualizarTotales() {
this.sabado = sabado; double totalSemanaCompletada = actividades.stream()
return this; .mapToDouble(this::calcularTotalPorDia)
} .sum();
double horasPendientes = 40 - totalSemanaCompletada;
public Builder domingo(final double domingo) { totalCompletadoLabel.setText("Total Hrs/Semana Completadas: " + totalSemanaCompletada);
this.domingo = domingo; horasPendientesLabel.setText("Horas Pendientes: " + horasPendientes);
return this; }
}
public Actividad build() { private void guardarActividades() {
return new Actividad(this); Employee selectedEmployee = employeeComboBox.getValue();
}
if (selectedEmployee == null) {
Notification.show("Por favor, selecciona un empleado antes de guardar.");
return;
} }
public String getNombre() { double totalHorasSemana = actividades.stream()
return nombre; .mapToDouble(this::calcularTotalPorDia)
} .sum();
public double getLunes() { HoursWorked hoursWorked = new HoursWorked();
return lunes; hoursWorked.setEmployee(selectedEmployee);
} hoursWorked.setWeekNumber(weekNumber);
hoursWorked.setTotalHours(totalHorasSemana);
public double getMartes() { try {
return martes; hoursWorkedService.saveHoursWorked(hoursWorked); // Usa saveHoursWorked directamente
Notification.show("Actividades guardadas correctamente.");
} catch (Exception e) {
Notification.show("Error al guardar actividades: " + e.getMessage());
} }
}
public double getMiercoles() { private double calcularTotalHoras(final List<HoursWorked> listaDeHorasTrabajadas) {
return miercoles; return listaDeHorasTrabajadas.stream()
} .mapToDouble(HoursWorked::getTotalHours)
.sum();
}
public double getJueves() { private List<HoursWorked> obtenerDatos() {
return jueves; return new ArrayList<>();
} }
public double getViernes() { private void closeView() {
return viernes; getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class));
}
public double getSabado() {
return sabado;
}
public double getDomingo() {
return domingo;
}
} }
} }

View File

@ -148,6 +148,8 @@ public class MainLayout extends AppLayout {
LineAwesomeIcon.HOURGLASS_START_SOLID.create()); LineAwesomeIcon.HOURGLASS_START_SOLID.create());
timesheet.addItem(new SideNavItem("Hours Worked", HoursWorkedView.class, timesheet.addItem(new SideNavItem("Hours Worked", HoursWorkedView.class,
LineAwesomeIcon.ID_CARD_SOLID.create())); LineAwesomeIcon.ID_CARD_SOLID.create()));
// timesheet.addItem(new SideNavItem("Reporte", ReportHoursWorkedView.class,
// LineAwesomeIcon.CAR_ALT_SOLID.create()));
SideNavItem profile = new SideNavItem("My Profile", ProfileView.class, SideNavItem profile = new SideNavItem("My Profile", ProfileView.class,
LineAwesomeIcon.USER_EDIT_SOLID.create()); LineAwesomeIcon.USER_EDIT_SOLID.create());

View File

@ -0,0 +1,87 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.service.HoursWorkedService;
import com.primefactorsolutions.service.ReportService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
import jakarta.annotation.security.PermitAll;
import org.springframework.beans.factory.annotation.Autowired;
import com.vaadin.flow.component.notification.Notification;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@PermitAll
@Route(value = "/reportes", layout = MainLayout.class)
@PageTitle("Reporte de Horas Trabajadas")
public class ReporteView extends VerticalLayout {
private final HoursWorkedService hoursWorkedService;
private final ReportService reportService;
@Autowired
public ReporteView(final HoursWorkedService hoursWorkedService, final ReportService reportService) {
this.hoursWorkedService = hoursWorkedService;
this.reportService = reportService;
H2 title = new H2("Reporte de Horas Trabajadas");
add(title);
Button reportButton = new Button("Generar Reporte de Horas Trabajadas", event -> generateHoursWorkedReport());
add(reportButton);
}
private void generateHoursWorkedReport() {
List<HoursWorked> hoursWorkedList = hoursWorkedService.findAll(); // Obtener la lista de HoursWorked
if (hoursWorkedList.isEmpty()) {
Notification.show("No hay horas trabajadas disponibles para generar el reporte.",
3000, Notification.Position.MIDDLE);
return;
}
try {
List<String> headers = List.of("ID", "Employee ID", "Week Number", "Total Hours");
List<Map<String, Object>> data = hoursWorkedList.stream()
.map(hoursWorked -> {
Map<String, Object> map = new HashMap<>();
map.put("ID", hoursWorked.getId().toString());
map.put("Employee ID", hoursWorked.getEmployee().getId().toString());
map.put("Week Number", hoursWorked.getWeekNumber());
map.put("Total Hours", hoursWorked.getTotalHours());
return map;
})
.collect(Collectors.toList());
byte[] excelBytes = reportService.writeAsExcel("hours_worked_report", headers, data);
StreamResource excelResource = new StreamResource("hours_worked_report.xlsx",
() -> new ByteArrayInputStream(excelBytes));
excelResource.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
excelResource.setCacheTime(0);
Anchor downloadLink = new Anchor(excelResource, "Descargar Reporte de Horas Trabajadas");
downloadLink.getElement().setAttribute("download", true);
add(downloadLink);
Notification.show("Reporte de horas trabajadas generado exitosamente.",
3000, Notification.Position.MIDDLE);
} catch (Exception e) {
Notification.show("Error al generar el reporte de horas trabajadas. Inténtalo de nuevo.",
3000, Notification.Position.MIDDLE);
e.printStackTrace();
}
}
}