diff --git a/src/main/bundles/prod.bundle b/src/main/bundles/prod.bundle index 6f55075..8cd2200 100644 Binary files a/src/main/bundles/prod.bundle and b/src/main/bundles/prod.bundle differ diff --git a/src/main/java/mx/gob/jumapacelaya/models/encuestas/MantenimientosSinEncuesta.java b/src/main/java/mx/gob/jumapacelaya/models/encuestas/MantenimientosSinEncuesta.java index e6158bc..52226e9 100644 --- a/src/main/java/mx/gob/jumapacelaya/models/encuestas/MantenimientosSinEncuesta.java +++ b/src/main/java/mx/gob/jumapacelaya/models/encuestas/MantenimientosSinEncuesta.java @@ -12,10 +12,11 @@ public class MantenimientosSinEncuesta { private String nomUsuario; private String email; private String encuesta; + private boolean tieneToken; public MantenimientosSinEncuesta(int mantenimientoId, int planId, LocalDate fecha, String periodo, String tipomant, String departamento, - String nomUsuario, String email, String encuesta) { + String nomUsuario, String email, String encuesta, boolean tieneToken) { this.mantenimientoId = mantenimientoId; this.planId = planId; @@ -26,6 +27,7 @@ public class MantenimientosSinEncuesta { this.nomUsuario = nomUsuario; this.email = email; this.encuesta = encuesta; + this.tieneToken = tieneToken; } public int getMantenimientoId() { @@ -99,4 +101,12 @@ public class MantenimientosSinEncuesta { public void setEncuesta(String encuesta) { this.encuesta = encuesta; } + + public boolean isTieneToken() { + return tieneToken; + } + + public void setTieneToken(boolean tieneToken) { + this.tieneToken = tieneToken; + } } diff --git a/src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java b/src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java index 068d185..b84dfb2 100644 --- a/src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java +++ b/src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java @@ -15,7 +15,10 @@ import org.springframework.stereotype.Service; import java.io.IOException; import java.io.InputStream; import java.sql.*; +import java.time.DayOfWeek; import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -319,7 +322,7 @@ public class DatabaseService { public List getEncuestPendientes(String mes, int anio) { List lista = new ArrayList<>(); String query = """ - SELECT + SELECT DISTINCT m.MANTENIMIENTOID MANTID, m.PLANANUALID PLANID, m.FECHA, @@ -328,7 +331,8 @@ public class DatabaseService { d.DESCRIPCION DEPTO, u.NOMBRE NOMUSUARIO, u.EMAIL, - m.ENCUESTA + m.ENCUESTA, + CASE WHEN e.TOKEN IS NOT NULL THEN 1 ELSE 0 END AS TIENE_TOKEN FROM MANTENIMIENTOS m JOIN TIPOMANT t ON t.TIPOMANTID = m.TIPOMANTID @@ -340,8 +344,10 @@ public class DatabaseService { ON p.PLANANUALID = m.PLANANUALID JOIN MESES ms ON ms.MESID = p.MESID - WHERE m.ENCUESTA = 'N' - AND m.TIPOMANTID = 1 + LEFT JOIN ENCUESTATOKENS e + ON e.MANTENIMIENTOID = m.MANTENIMIENTOID + WHERE + m.TIPOMANTID = 1 AND m.PLANANUALID IS NOT NULL AND UPPER(ms.NOMBRE) = UPPER(?) AND EXTRACT(YEAR FROM m.FECHA) = ? @@ -365,7 +371,8 @@ public class DatabaseService { rs.getString("DEPTO"), rs.getString("NOMUSUARIO"), rs.getString("EMAIL"), - rs.getString("ENCUESTA") + rs.getString("ENCUESTA"), + rs.getBoolean("TIENE_TOKEN") )); } } catch (SQLException e) { @@ -1037,21 +1044,39 @@ public class DatabaseService { public String crearTokenEncuesta(int mantenimientoid) { String token = UUID.randomUUID().toString(); - Timestamp expira = new Timestamp(System.currentTimeMillis() + (24 * 60 * 60 * 1000)); + ZoneId zonaMX = ZoneId.of("America/Mexico_City"); + ZonedDateTime ahora = ZonedDateTime.now(zonaMX); + + ZonedDateTime fechaExp = ahora; + int diasPorSumar = 3; + while (diasPorSumar > 0) { + fechaExp = fechaExp.plusDays(1); + if (!(fechaExp.getDayOfWeek() == DayOfWeek.SATURDAY || + fechaExp.getDayOfWeek() == DayOfWeek.SUNDAY)) { + diasPorSumar--; + } + } - String query = "INSERT INTO ENCUESTATOKENS (TOKEN, MANTENIMIENTOID, FECHAEXPIRACION) VALUES (?, ?, ?)"; + Timestamp expira = Timestamp.from(fechaExp.toInstant()); + LocalDate fechaEnvio = ahora.toLocalDate(); + + String query = "INSERT INTO ENCUESTATOKENS (TOKEN, MANTENIMIENTOID, FECHAENVIO, FECHAEXPIRACION) VALUES (?, ?, ?, ?)"; try (Connection conn = getMysqlConnection(); PreparedStatement stmt = conn.prepareStatement(query)) { + stmt.setString(1, token); stmt.setInt(2, mantenimientoid); - stmt.setTimestamp(3, expira); + stmt.setDate(3, java.sql.Date.valueOf(fechaEnvio)); + stmt.setTimestamp(4, expira); stmt.executeUpdate(); + return token; + } catch (SQLException e) { logger.error("Error al crear el token de encuesta: ", e); + throw new RuntimeException("Ya existe una encuesta para este mantenimiento o error en DB", e); } - return token; } public Integer validarTokenYObtenerId(String token) { @@ -1071,21 +1096,34 @@ public class DatabaseService { return null; } - public List getTotalEncuestas() { + public List getTotalEncuestas(Integer mesId, Integer anio) { List totalEncuestas = new ArrayList<>(); - String query = "SELECT * FROM VW_CONTEO_ENCUESTAS"; + String query = "{call sp_reporte_encuestas(?, ?)}"; try (Connection conn = getMysqlConnection(); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(query)) { + CallableStatement cstmt = conn.prepareCall(query)) { - while (rs.next()) { - ConteoEncuestas conteo = new ConteoEncuestas( - rs.getInt("TOTAL_ENVIADAS"), - rs.getInt("TOTAL_RESPONDIDAS"), - rs.getDouble("PORCENTAJE_TOTAL") - ); - totalEncuestas.add(conteo); + if (mesId != null) { + cstmt.setInt(1, mesId); + } else { + cstmt.setNull(1, Types.INTEGER); + } + + if (anio != null) { + cstmt.setInt(2, anio); + } else { + cstmt.setNull(2, Types.INTEGER); + } + + try (ResultSet rs = cstmt.executeQuery()) { + while (rs.next()) { + ConteoEncuestas conteo = new ConteoEncuestas( + rs.getInt("TOTAL_ENVIADAS"), + rs.getInt("TOTAL_RESPONDIDAS"), + rs.getDouble("PORCENTAJE_TOTAL") + ); + totalEncuestas.add(conteo); + } } } catch (SQLException e) { logger.error("Error al obtener el total de encuestas: ", e); @@ -1093,24 +1131,37 @@ public class DatabaseService { return totalEncuestas; } - public List getTotalRespuestas() { + public List getTotalRespuestas(Integer mesId, Integer anio) { List totalRespuestas = new ArrayList<>(); - String query = "SELECT * FROM VW_CONTEO_RESPUESTAS"; + String query = "{call sp_reporte_respuestas(?, ?)}"; try (Connection conn = getMysqlConnection(); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(query)) { + CallableStatement cstmt = conn.prepareCall(query)) { - while (rs.next()) { - ConteoRespuestas conteo = new ConteoRespuestas( - rs.getInt("PREGUNTAID"), - rs.getString("PREGUNTA"), - rs.getInt("TOTALREGISTROS"), - rs.getInt("TOTALSI"), - rs.getInt("TOTALNO"), - rs.getDouble("PORCENTAJE") - ); - totalRespuestas.add(conteo); + if (mesId != null) { + cstmt.setInt(1, mesId); + } else { + cstmt.setNull(1, Types.INTEGER); + } + + if (anio != null) { + cstmt.setInt(2, anio); + } else { + cstmt.setNull(2, Types.INTEGER); + } + + try (ResultSet rs = cstmt.executeQuery()) { + while (rs.next()) { + ConteoRespuestas conteo = new ConteoRespuestas( + rs.getInt("PREGUNTAID"), + rs.getString("PREGUNTA"), + rs.getInt("TOTALREGISTROS"), + rs.getInt("TOTALSI"), + rs.getInt("TOTALNO"), + rs.getDouble("PORCENTAJE") + ); + totalRespuestas.add(conteo); + } } } catch (SQLException e) { logger.error("Error al obtener el total de respuestas: ", e); diff --git a/src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java b/src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java index b0b7205..131e4fd 100644 --- a/src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java +++ b/src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java @@ -63,6 +63,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser private String baseUrl; private final H3 id; + private final TextField mantId; private final EmailService emailService; private HorizontalLayout fechasLayout; private TextField txtId; @@ -114,10 +115,12 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser id = new H3(); + mantId = new TextField(); HorizontalLayout headerLayout = new HorizontalLayout(); headerLayout.setWidthFull(); - headerLayout.add(id); + headerLayout.setAlignItems(Alignment.BASELINE); + headerLayout.add(id, mantId); cbTipo = new ComboBox<>("Tipo:"); cbTipo.setItems(service.getTiposDeMantenimientos()); @@ -536,25 +539,25 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser private void enviarCorreo() { Usuario usuarioDestino = cbUsuario.getValue(); if (usuarioDestino != null && usuarioDestino.getEmail() != null) { - String destinatario = usuarioDestino.getEmail(); - String asunto = "Encuesta de satisfacción -Mantenimiento #" + mantenimientoIdActual; + try { + String token = service.crearTokenEncuesta(mantenimientoIdActual); - String token = service.crearTokenEncuesta(mantenimientoIdActual); - String linkEncuesta = baseUrl + "/encuesta?token=" + token; + String destinatario = usuarioDestino.getEmail(); + String asunto = "Encuesta de satisfacción - Mantenimiento #" + mantenimientoIdActual; + String linkEncuesta = baseUrl + "/encuesta?token=" + token; + String cuerpo = ""; + String imagePath = "META-INF/resources/images/imgCorreo/imgEncuesta.png"; - String cuerpo = "" + - "" + - "" + - ""+ - "" + - "" + - ""; + emailService.enviarCorreo(destinatario, asunto, cuerpo, imagePath); - String imagePath = "META-INF/resources/images/imgCorreo/imgEncuesta.png"; + Notification.show("Encuesta enviada correctamente", 3000, Notification.Position.BOTTOM_END) + .addThemeVariants(NotificationVariant.LUMO_SUCCESS); - emailService.enviarCorreo(destinatario,asunto,cuerpo,imagePath); - Notification.show("Encuesta enviada correctamente para el mantenimiento No. " + mantenimientoIdActual, 3000, Notification.Position.BOTTOM_END) - .addThemeVariants(NotificationVariant.LUMO_SUCCESS); + } catch (Exception e) { + Notification.show("Error: No se pudo enviar. Es probable que ya exista una encuesta generada.", + 5000, Notification.Position.MIDDLE) + .addThemeVariants(NotificationVariant.LUMO_ERROR); + } } else { Notification.show("Por favor, seleccione un usuario destino", 3000, Notification.Position.MIDDLE); } @@ -612,6 +615,9 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser .filter(t -> t.getNombre().equals(detalle.getTipo())) .findFirst().orElse(null) ); + mantId.setValue(String.valueOf(mantenimientoIdActual)); + mantId.setReadOnly(true); + mantId.setWidth("100px"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); String fechaProg = detalle.getFechaprog() != null ? detalle.getFechaprog().format(formatter) : ""; diff --git a/src/main/java/mx/gob/jumapacelaya/ui/MainLayout.java b/src/main/java/mx/gob/jumapacelaya/ui/MainLayout.java index b6eb979..0663d18 100644 --- a/src/main/java/mx/gob/jumapacelaya/ui/MainLayout.java +++ b/src/main/java/mx/gob/jumapacelaya/ui/MainLayout.java @@ -93,7 +93,7 @@ public class MainLayout extends AppLayout implements BeforeEnterObserver { nav.addItem(new SideNavItem("Plan Anual", PlanAnualView.class, VaadinIcon.CALENDAR.create())); nav.addItem(new SideNavItem("Listado de Actividades", ActDiariaView.class, VaadinIcon.EDIT.create())); nav.addItem(new SideNavItem("Mantenimiento Correctivo", MantCorrectivoView.class, VaadinIcon.WRENCH.create())); - nav.addItem(new SideNavItem("Resultados de Encuestas", ResultEncuestasView.class, VaadinIcon.CLIPBOARD_TEXT.create())); + nav.addItem(new SideNavItem("Resultados de Encuestas", ResultEncuestasView.class, VaadinIcon.BAR_CHART_H.create())); nav.addItem(new SideNavItem("Preferencias del Sistema", ConfiguracionView.class, VaadinIcon.COG.create())); nav.getStyle().set("border-radius", "5px"); diff --git a/src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java b/src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java index 0667bec..4eda038 100644 --- a/src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java +++ b/src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java @@ -1095,8 +1095,11 @@ public class PlanAnualView extends VerticalLayout { grid.addColumn(createStatusRender()) .setHeader("Encuesta"); - grid.addColumn(MantenimientosSinEncuesta::getDepartamento) - .setHeader("Departamento"); + grid.addColumn(createTokenRender()) + .setHeader("Token"); + + /*grid.addColumn(MantenimientosSinEncuesta::getDepartamento) + .setHeader("Departamento");*/ grid.addColumn(MantenimientosSinEncuesta::getNomUsuario) .setHeader("Usuario") @@ -1114,6 +1117,11 @@ public class PlanAnualView extends VerticalLayout { btnEnviar.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS); + if (item.isTieneToken()) { + btnEnviar.setEnabled(false); + btnEnviar.setText("Enviado"); + } + btnEnviar.addClickListener(e -> { enviarEncuestaDesdeGrid(item); btnEnviar.setEnabled(false); @@ -1191,4 +1199,22 @@ public class PlanAnualView extends VerticalLayout { return span; }); } + + private ComponentRenderer createTokenRender() { + return new ComponentRenderer<>(token -> { + Span span = new Span(String.valueOf(token.isTieneToken())); + + if (token.isTieneToken()) { + span.setText("S"); + String theme2 = String.format("badge %s", "success"); + span.getElement().setAttribute("theme", theme2); + } else { + span.setText("N"); + String theme1 = String.format("badge %s", "error"); + span.getElement().setAttribute("theme", theme1); + } + + return span; + }); + } } diff --git a/src/main/java/mx/gob/jumapacelaya/ui/ResultEncuestasView.java b/src/main/java/mx/gob/jumapacelaya/ui/ResultEncuestasView.java index bf8e392..6eb5e6b 100644 --- a/src/main/java/mx/gob/jumapacelaya/ui/ResultEncuestasView.java +++ b/src/main/java/mx/gob/jumapacelaya/ui/ResultEncuestasView.java @@ -2,6 +2,7 @@ package mx.gob.jumapacelaya.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.dashboard.Dashboard; import com.vaadin.flow.component.dashboard.DashboardSection; import com.vaadin.flow.component.dashboard.DashboardWidget; @@ -23,7 +24,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.vaadin.lineawesome.LineAwesomeIcon; +import java.util.HashMap; import java.util.List; +import java.util.Map; @PermitAll @PageTitle("Resultados de Encuestas") @@ -41,6 +44,7 @@ public class ResultEncuestasView extends VerticalLayout { private DashboardWidget pregunta4; private DashboardWidget pregunta5; private DashboardWidget pregunta6; + private ComboBox cmbMes; public ResultEncuestasView(DatabaseService databaseService) { this.databaseService = databaseService; @@ -52,7 +56,32 @@ public class ResultEncuestasView extends VerticalLayout { Button btnExport = new Button("Exportar a PDF", LineAwesomeIcon.FILE_PDF.create()); btnExport.addThemeVariants(ButtonVariant.LUMO_SMALL); - HorizontalLayout toolbar = new HorizontalLayout(btnExport); + Map meses = new HashMap<>(); + meses.put(1, "Enero"); + meses.put(2, "Febrero"); + meses.put(3, "Marzo"); + meses.put(4, "Abril"); + meses.put(5, "Mayo"); + meses.put(6, "Junio"); + meses.put(7, "Julio"); + meses.put(8, "Agosto"); + meses.put(9, "Septiembre"); + meses.put(10, "Octubre"); + meses.put(11, "Noviembre"); + meses.put(12, "Diciembre"); + + + cmbMes = new ComboBox<>(); + cmbMes.setItems(meses.keySet()); + cmbMes.setItemLabelGenerator(meses::get); + cmbMes.setPlaceholder("Filtrar por mes"); + cmbMes.setClearButtonVisible(true); + cmbMes.addValueChangeListener(e -> { + Integer mesSeleccionado = e.getValue(); + actualizarDashboard(mesSeleccionado, 2026); + }); + + HorizontalLayout toolbar = new HorizontalLayout(btnExport, cmbMes); toolbar.setAlignItems(Alignment.BASELINE); toolbar.setWidthFull(); @@ -94,8 +123,7 @@ public class ResultEncuestasView extends VerticalLayout { preguntasSection.add(pregunta5); preguntasSection.add(pregunta6); - mostrarTotales(); - mostrarPreguntas(); + actualizarDashboard(null, null); add(dashboard); return dashboard; } @@ -160,8 +188,13 @@ public class ResultEncuestasView extends VerticalLayout { return vl; } - public void mostrarTotales() { - List datos = databaseService.getTotalEncuestas(); + private void actualizarDashboard(Integer mes, Integer anio) { + mostrarTotales(mes, anio); + mostrarPreguntas(mes, anio); + } + + public void mostrarTotales(Integer mes, Integer anio) { + List datos = databaseService.getTotalEncuestas(mes, anio); if (!datos.isEmpty()) { ConteoEncuestas totales = datos.get(0); @@ -180,15 +213,20 @@ public class ResultEncuestasView extends VerticalLayout { String.valueOf(totales.getPorcentajeRespondidas()), LineAwesomeIcon.PERCENT_SOLID, "#BC955B" )); + } else { + totalEncu.setContent(createDiv("0", LineAwesomeIcon.CLIPBOARD_LIST_SOLID, "#BC955B")); } } - public void mostrarPreguntas() { - List lista = databaseService.getTotalRespuestas(); + public void mostrarPreguntas(Integer mes, Integer anio) { + List lista = databaseService.getTotalRespuestas(mes, anio); DashboardWidget[] widgets = {pregunta1,pregunta2,pregunta3,pregunta4,pregunta5,pregunta6}; + for (DashboardWidget w : widgets) w.setContent(new Span("Sin datos")); + for (int i = 0; i < lista.size() && i < widgets.length; i++) { + ConteoRespuestas data = lista.get(i); DashboardWidget widget = widgets[i];