Browse Source

Fusion

master
mramirezg 2 weeks ago
parent
commit
5eb73d6786
31 changed files with 5055 additions and 11446 deletions
  1. +3594
    -10844
      package-lock.json
  2. +50
    -48
      package.json
  3. +3
    -3
      pom.xml
  4. BIN
      src/main/bundles/dev.bundle
  5. +20
    -180
      src/main/frontend/themes/sistema-mantenimiento/styles.css
  6. +5
    -3
      src/main/java/mx/gob/jumapacelaya/Application.java
  7. +48
    -66
      src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java
  8. +0
    -1
      src/main/java/mx/gob/jumapacelaya/api/SecurityService.java
  9. +16
    -33
      src/main/java/mx/gob/jumapacelaya/controller/SecurityConfiguration.java
  10. +0
    -1
      src/main/java/mx/gob/jumapacelaya/datasource/MysqlDataSource.java
  11. +0
    -1
      src/main/java/mx/gob/jumapacelaya/datasource/OracleDataSource.java
  12. +38
    -0
      src/main/java/mx/gob/jumapacelaya/models/encuestas/ConteoEncuestas.java
  13. +68
    -0
      src/main/java/mx/gob/jumapacelaya/models/encuestas/ConteoRespuestas.java
  14. +102
    -0
      src/main/java/mx/gob/jumapacelaya/models/encuestas/MantenimientosSinEncuesta.java
  15. +147
    -69
      src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java
  16. +8
    -12
      src/main/java/mx/gob/jumapacelaya/services/EmailService.java
  17. +1
    -1
      src/main/java/mx/gob/jumapacelaya/services/LdapService.java
  18. +261
    -113
      src/main/java/mx/gob/jumapacelaya/ui/ActDiariaView.java
  19. +203
    -0
      src/main/java/mx/gob/jumapacelaya/ui/ConfiguracionView.java
  20. +16
    -5
      src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java
  21. +14
    -5
      src/main/java/mx/gob/jumapacelaya/ui/EncuestaView.java
  22. +28
    -7
      src/main/java/mx/gob/jumapacelaya/ui/MainLayout.java
  23. +6
    -8
      src/main/java/mx/gob/jumapacelaya/ui/MantCorrectivoView.java
  24. +7
    -12
      src/main/java/mx/gob/jumapacelaya/ui/MantenimientoView.java
  25. +214
    -28
      src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java
  26. +200
    -0
      src/main/java/mx/gob/jumapacelaya/ui/ResultEncuestasView.java
  27. +0
    -3
      src/main/java/mx/gob/jumapacelaya/ui/login/LoginView.java
  28. BIN
      src/main/resources/META-INF/resources/images/LOGO_admon2.png
  29. BIN
      src/main/resources/META-INF/resources/images/imgCorreo/imgEncuesta.png
  30. +5
    -2
      src/main/resources/application.properties
  31. +1
    -1
      tsconfig.json

+ 3594
- 10844
package-lock.json
File diff suppressed because it is too large
View File


+ 50
- 48
package.json View File

@ -4,42 +4,43 @@
"type": "module",
"dependencies": {
"@polymer/polymer": "3.5.2",
"@vaadin/bundles": "24.5.5",
"@vaadin/bundles": "24.8.0",
"@vaadin/common-frontend": "0.0.19",
"@vaadin/polymer-legacy-adapter": "24.5.5",
"@vaadin/react-components": "24.5.5",
"@vaadin/react-components-pro": "24.5.5",
"@vaadin/polymer-legacy-adapter": "24.8.0",
"@vaadin/react-components": "24.8.0",
"@vaadin/react-components-pro": "24.8.0",
"@vaadin/vaadin-development-mode-detector": "2.0.7",
"@vaadin/vaadin-lumo-styles": "24.5.5",
"@vaadin/vaadin-material-styles": "24.5.5",
"@vaadin/vaadin-themable-mixin": "24.5.5",
"@vaadin/vaadin-lumo-styles": "24.8.0",
"@vaadin/vaadin-material-styles": "24.8.0",
"@vaadin/vaadin-themable-mixin": "24.8.0",
"@vaadin/vaadin-usage-statistics": "2.1.3",
"construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3",
"lit": "3.2.1",
"proj4": "2.12.1",
"lit": "3.3.0",
"proj4": "2.15.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.28.0",
"react-router": "7.6.1",
"signature_pad": "4.1.5"
},
"devDependencies": {
"@babel/preset-react": "7.26.3",
"@preact/signals-react-transform": "0.4.0",
"@rollup/plugin-replace": "6.0.1",
"@rollup/pluginutils": "5.1.3",
"@types/react": "18.3.13",
"@types/react-dom": "18.3.1",
"@vitejs/plugin-react": "4.3.3",
"@babel/preset-react": "7.27.1",
"@preact/signals-react-transform": "0.5.1",
"@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.1.4",
"@types/react": "18.3.23",
"@types/react-dom": "18.3.7",
"@vitejs/plugin-react": "4.5.0",
"async": "3.2.6",
"glob": "10.4.5",
"glob": "11.0.2",
"magic-string": "0.30.17",
"rollup-plugin-brotli": "3.1.0",
"rollup-plugin-visualizer": "5.12.0",
"rollup-plugin-visualizer": "5.14.0",
"strip-css-comments": "5.0.0",
"transform-ast": "2.4.4",
"typescript": "5.6.3",
"vite": "5.4.11",
"vite-plugin-checker": "0.8.0",
"typescript": "5.8.3",
"vite": "6.3.5",
"vite-plugin-checker": "0.9.3",
"workbox-build": "7.3.0",
"workbox-core": "7.3.0",
"workbox-precaching": "7.3.0"
@ -47,47 +48,48 @@
"vaadin": {
"dependencies": {
"@polymer/polymer": "3.5.2",
"@vaadin/bundles": "24.5.5",
"@vaadin/bundles": "24.8.0",
"@vaadin/common-frontend": "0.0.19",
"@vaadin/polymer-legacy-adapter": "24.5.5",
"@vaadin/react-components": "24.5.5",
"@vaadin/react-components-pro": "24.5.5",
"@vaadin/polymer-legacy-adapter": "24.8.0",
"@vaadin/react-components": "24.8.0",
"@vaadin/react-components-pro": "24.8.0",
"@vaadin/vaadin-development-mode-detector": "2.0.7",
"@vaadin/vaadin-lumo-styles": "24.5.5",
"@vaadin/vaadin-material-styles": "24.5.5",
"@vaadin/vaadin-themable-mixin": "24.5.5",
"@vaadin/vaadin-lumo-styles": "24.8.0",
"@vaadin/vaadin-material-styles": "24.8.0",
"@vaadin/vaadin-themable-mixin": "24.8.0",
"@vaadin/vaadin-usage-statistics": "2.1.3",
"construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3",
"lit": "3.2.1",
"proj4": "2.12.1",
"lit": "3.3.0",
"proj4": "2.15.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.28.0",
"react-router": "7.6.1",
"signature_pad": "4.1.5"
},
"devDependencies": {
"@babel/preset-react": "7.26.3",
"@preact/signals-react-transform": "0.4.0",
"@rollup/plugin-replace": "6.0.1",
"@rollup/pluginutils": "5.1.3",
"@types/react": "18.3.13",
"@types/react-dom": "18.3.1",
"@vitejs/plugin-react": "4.3.3",
"@babel/preset-react": "7.27.1",
"@preact/signals-react-transform": "0.5.1",
"@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.1.4",
"@types/react": "18.3.23",
"@types/react-dom": "18.3.7",
"@vitejs/plugin-react": "4.5.0",
"async": "3.2.6",
"glob": "10.4.5",
"glob": "11.0.2",
"magic-string": "0.30.17",
"rollup-plugin-brotli": "3.1.0",
"rollup-plugin-visualizer": "5.12.0",
"rollup-plugin-visualizer": "5.14.0",
"strip-css-comments": "5.0.0",
"transform-ast": "2.4.4",
"typescript": "5.6.3",
"vite": "5.4.11",
"vite-plugin-checker": "0.8.0",
"typescript": "5.8.3",
"vite": "6.3.5",
"vite-plugin-checker": "0.9.3",
"workbox-build": "7.3.0",
"workbox-core": "7.3.0",
"workbox-precaching": "7.3.0"
},
"hash": "6126bd412c2a8696938f50edd1abae217c30c0119a0496af47b9d6ba1762921f"
"hash": "5e2a99194f7c0b939e722574ef486d86e32935203f1b5047346903f60dcb24d6"
},
"overrides": {
"@vaadin/bundles": "$@vaadin/bundles",
@ -99,7 +101,6 @@
"@vaadin/common-frontend": "$@vaadin/common-frontend",
"react-dom": "$react-dom",
"construct-style-sheets-polyfill": "$construct-style-sheets-polyfill",
"react-router-dom": "$react-router-dom",
"lit": "$lit",
"@polymer/polymer": "$@polymer/polymer",
"react": "$react",
@ -108,6 +109,7 @@
"@vaadin/vaadin-themable-mixin": "$@vaadin/vaadin-themable-mixin",
"@vaadin/vaadin-lumo-styles": "$@vaadin/vaadin-lumo-styles",
"@vaadin/vaadin-material-styles": "$@vaadin/vaadin-material-styles",
"signature_pad": "$signature_pad"
"signature_pad": "$signature_pad",
"react-router": "$react-router"
}
}
}

+ 3
- 3
pom.xml View File

@ -6,18 +6,18 @@
<groupId>mx.gob.jumapacelaya</groupId>
<artifactId>sistema-mantenimiento</artifactId>
<name>sistema-mantenimiento</name>
<version>1.0-SNAPSHOT</version>
<version>2.1</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<vaadin.version>24.5.8</vaadin.version>
<vaadin.version>24.8.0</vaadin.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.8</version>
<version>3.5.8</version>
</parent>
<repositories>


BIN
src/main/bundles/dev.bundle View File


+ 20
- 180
src/main/frontend/themes/sistema-mantenimiento/styles.css View File

@ -1,11 +1,10 @@
@import url('./main-layout.css');
/* ----------- Estilos para el MainLayout.java ----------- */
/* Estilos para el DrawerToggle*/
.drawer-toggle {
background-color: #bc955b;
:root {
--lumo-primary-color: #BC955B;
--lumo-primary-text-color: #BC955B;
--lumo-border-radius: 15px;
--lumo-font-family: 'Montserrat';
}
/* Estilos para el encabezado */
@ -26,12 +25,9 @@
width: 300px;
}
/* Estilos para el fondo de la aplicacion */
.app-layout {
background-image: url('/images/bckgndNvo.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
vaadin-grid::part(selected-row-cell) {
background-color: #a02142;
color: white;
}
/* ------------------ FIN -------------------------------------- */
/*
@ -59,19 +55,6 @@
font-size: 18px;
color: #ddc9a3;
}
/* Estilo para el campo de texto nomenclatura */
.nomenclatura-txt {
margin-top: 15px;
margin-right: 15px;
}
/* Estilo para el logo CELAYA */
.celaya-logo {
width: 100px;
margin-left: 15px;
margin-top: 15px;
}
/* ------------------------ FIN -------------------------- */
/*
/*
@ -90,176 +73,21 @@
text-align: center;
}
/* Estilo para el grid */
.act-diaria-grid {
margin-top: 20px;
}
/* ---------------------------- FIN------------------------ */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* Estilos especificos para el TextField, TextArea y Fecha*/
vaadin-text-field::part(label), vaadin-text-area::part(label), vaadin-date-picker::part(label) {
color: #691b31; /* Color de la etiqueta */
}
vaadin-text-field::part(input-field), vaadin-text-area::part(input-field), vaadin-password-field::part(input-field),
vaadin-number-field::part(input-field), vaadin-date-picker::part(input-field) {
border: 1px solid #691b31; /* Color del borde */
color: #691b31; /* Color del texto */
}
vaadin-month-calendar::part(date) {
color: #691b31; /* Color para los dias del calendario */
}
vaadin-button {
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(105, 27, 49, 0.28), 0 2px 8px rgba(0, 0, 0, 0.13);
transition: box-shadow 0.2s, border 0.2s;
}
vaadin-button::after {
content: "";
position: absolute;
inset: 0;
background: rgba(240,223,223,0.2);
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
vaadin-button:hover::after {
box-shadow: 0 16px 36px rgba(105,27,49,0.35), 0 4px 16px rgba(0,0,0,0.18);
border-color: #BC955B;
opacity: 1;
}
vaadin-month-calendar::part(date):hover /* Estilos para cuando se posiciona el puntero sobre el dia */{
background-color: #a02142;
opacity: 50%;
border-radius: 5px;
}
vaadin-month-calendar::part(date focused) {
background-color: #ddc9a3; /* Color de la fecha seleccionada */
}
vaadin-date-picker-overlay-content > vaadin-button, vaadin-date-picker-year::part(year-number) {
color: #691b31;
}
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* Estilos específicos para el Upload */
vaadin-upload > vaadin-button {
color: #691b31;
}
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* Estilos específicos para el ComboBox */
vaadin-combo-box::part(input-field) {
background-color: #ddc9a3; /* Color de fondo */
color: #691b31; /* Color del texto */
border: 1px solid #691b31; /* Color del borde */
}
vaadin-combo-box::part(label) {
color: #691b31; /* Color de las etiquetas */
}
vaadin-combo-box::part(dropdown) {
background-color: rgba(221, 201, 163, 1); /* Color del fondo del dropdown */
}
vaadin-combo-box-item::part(checkmark)::before, vaadin-combo-box-item:hover
{
color: #691b31;
}
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) INDICADOR DE CARGA DE LA APLICACION (*)(*) (*)(*) */
.v-loading-indicator { /* Cambiar el color de la barra de carga que sale cuando la aplicacion esta cargando */
background: #691b31 !important;
}
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) ESTILOS PARA LA SIDEBAR (*)(*) (*)(*) */
vaadin-side-nav-item[aria-current="page"]::part(content) {
color: #691b31; /* Color de texto seleccionado */
}
vaadin-side-nav-item:hover::part(content) {
background-color: #ddc9a3; /* Color de fondo al hacer hover*/
fill-opacity: 50%;
border-radius: 5px;
}
vaadin-side-nav-item::part(content) {
color: #a02142; /* Color del texto deseado */
}
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/*Estilos especificos para el Grid */
vaadin-grid::part(selected-row-cell) {
background-color: rgba(221, 201, 163, 0.7);
}
vaadin-grid::part(selected-row) {
color: #691b31;
font-weight: bold;
}
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/*Estilos especificos para los botones */
vaadin-button[theme~="primary"] {
background-color: #691b31;
}
vaadin-button:not([theme]) {
color: #a02142;
}
vaadin-button[theme~="tertiary-inline"], vaadin-button[theme~="icon"] {
color: #a02142;
}
/* Estilos para el theme personalizado 'subir-archivo' */
vaadin-button[theme~="subir-archivo"] {
background-color: #691b31;
color: #ffffff;
}
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/*Estilos especificos para los CheckBox y RadioButtons */
vaadin-checkbox[checked]::part(checkbox) {
background-color: #691b31; /* Cambia el color del CheckBox */
}
vaadin-checkbox-group::part(label), vaadin-radio-group::part(label) {
color: #691b31;
}
vaadin-radio-button[checked]::part(radio) {
background-color: #691b31;
}
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
@ -298,6 +126,18 @@ vaadin-popover-overlay::part(overlay) {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.dashboard-widget-content {
padding: 1.5rem;
}
.pregunta-widget-container {
border-top: 3px solid #a02142;
}
.pregunta-widget-container span {
line-height: 1.2;
}
@media (max-width: 900px) {


+ 5
- 3
src/main/java/mx/gob/jumapacelaya/Application.java View File

@ -1,13 +1,14 @@
package mx.gob.jumapacelaya;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.server.PWA;
import com.vaadin.flow.theme.Theme;
import com.vaadin.flow.theme.lumo.Lumo;
import mx.gob.jumapacelaya.services.DatabaseService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
/**
* The entry point of the Spring Boot application.
@ -17,7 +18,8 @@ import org.springframework.boot.autoconfigure.validation.ValidationAutoConfigura
*
*/
@SpringBootApplication
@Theme(value = "sistema-mantenimiento")
@Push
@Theme(value = "sistema-mantenimiento", variant = Lumo.LIGHT)
@PWA(name = "Aplicacion de Mantenimiento de Equipo de Computo", shortName = "Mantenimiento de Computo", iconPath = "icons/icon.png")
public class Application implements AppShellConfigurator {


+ 48
- 66
src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java View File

@ -7,6 +7,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
@ -51,16 +52,29 @@ public class RedmineClient {
StringBuilder url = new StringBuilder(REDMINE_URL + "/issues.json?key=" + user.getKey()
+ statusFilter + "&offset=" + offset + "&limit=" + limit);
url.append("&sort=created_on:desc");
// Filtro por fechas
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
if (fechaDesde != null) {
url.append("&created_on=%3E%3D").append(fechaDesde.format(formatter));
}
if (fechaHasta != null) {
url.append("&created_on=%3C%3D").append(fechaHasta.format(formatter));
if (fechaDesde != null || fechaHasta != null) {
url.append("&created_on=");
if (fechaDesde != null && fechaHasta != null) {
// En lugar de >< usamos %3E%3C y en lugar de | usamos %7C
url.append("%3E%3C")
.append(fechaDesde.toString())
.append("%7C")
.append(fechaHasta.toString());
} else if (fechaDesde != null) {
url.append("%3E%3D").append(fechaDesde.toString());
} else {
url.append("%3C%3D").append(fechaHasta.toString());
}
}
//log.info("URL: " + url.toString());
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url.toString()))
.header("Content-Type", "application/json")
@ -71,10 +85,10 @@ public class RedmineClient {
if (response.statusCode() == 200) {
tickets.addAll(parseTickets(response.body()));
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
log.error("Error al obtener tickets. Codigo de estado: {}", response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
log.error("Ocurrió una excepción al procesar la respuesta del servidor: ", e);
}
return tickets;
@ -95,11 +109,20 @@ public class RedmineClient {
// Filtro por fechas
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
if (fechaDesde != null) {
url.append("&created_on=%3E%3D").append(fechaDesde.format(formatter));
}
if (fechaHasta != null) {
url.append("&created_on=%3C%3D").append(fechaHasta.format(formatter));
if (fechaDesde != null || fechaHasta != null) {
url.append("&created_on=");
if (fechaDesde != null && fechaHasta != null) {
// En lugar de >< usamos %3E%3C y en lugar de | usamos %7C
url.append("%3E%3C")
.append(fechaDesde.toString())
.append("%7C")
.append(fechaHasta.toString());
} else if (fechaDesde != null) {
url.append("%3E%3D").append(fechaDesde.toString());
} else {
url.append("%3C%3D").append(fechaHasta.toString());
}
}
HttpRequest request = HttpRequest.newBuilder()
@ -113,55 +136,15 @@ public class RedmineClient {
JsonObject jsonObject = JsonParser.parseString(response.body()).getAsJsonObject();
return jsonObject.get("total_count").getAsInt();
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
log.error("Error al obtener los tickets. Codigo de estado: {}", response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
log.error("Ocurrió un error al procesar l respuesta del servidor", e);
}
return 0;
}
//AQUI OBTENGO LOS TICKETS DESDE REDMINE
/*public List<Ticket> getTicketsAuthor(RedmineUser user, boolean includeClosed) {
List<Ticket> tickets = new ArrayList<>();
HttpClient client = HttpClient.newHttpClient();
int offset = 0;
// Si includeClose es true, incluira todos los tikets si no, incluira solo los que estan abiertos
String statusFilter = includeClosed ? "&status_id=*" : "&status_id=open";
while (true) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey() + "&author_id=" + user.getId() + statusFilter + "&offset=" + offset))
.header("Content-Type", "application/json")
.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
List<Ticket> pageTickets = parseTickets(responseBody);
tickets.addAll(pageTickets);
if (pageTickets.size() < PAGE_SIZE) {
break;
}
offset += PAGE_SIZE;
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
break;
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
System.out.println("Total tickets obtenidos: " + tickets.size());
return tickets;
}*/
// Aquí se parsean todos los tickets que existen y se les da un formato con los campos a mostrarse
private List<Ticket> parseTickets(String json) {
List<Ticket> tickets = new ArrayList<>();
@ -232,11 +215,10 @@ public class RedmineClient {
tickets.add(ticketObj);
}
} else {
System.out.println("La respuesta JSON no contiene la clave 'issues'");
log.info("La respuesta JSON no contiene la clave 'issues'. Respuesta completa: {}", json);
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Ocurrió un error al parsear los tickets: " + e.getMessage());
log.error("Ocurrió un error al parsear los tickets: ", e);
}
return tickets;
}
@ -268,7 +250,7 @@ public class RedmineClient {
//return response.body();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
log.error("Error al obtener la cuenta del usuario: {}", e);
return null;
}
}
@ -309,7 +291,7 @@ public class RedmineClient {
return newUser;
} catch (Exception e) {
e.printStackTrace();
log.error("Error al crear el usuario: {}", e);
return null;
}
}
@ -328,13 +310,13 @@ public class RedmineClient {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
System.out.println("Datos del usuario " + responseBody);
log.info("Datos del usuario " + responseBody);
return parseUser(responseBody);
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
log.error("Error en la respuesta {}", response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
log.error("Error al obtener el usuario por nombre de usuario: {}", e);
}
return null;
}
@ -376,10 +358,10 @@ public class RedmineClient {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 204) {
String responseBody = response.body();
System.out.println("Ticket " + ticketId + " cerrado correctamente.");
log.info("Ticket " + ticketId + " cerrado correctamente.");
} else {
System.err.println("Error al cerrar el ticket: " + response.statusCode());
System.err.println(response.body());
log.error("Error al cerrar el ticket: {}", response.statusCode());
log.error(response.body());
}
}
@ -411,7 +393,7 @@ public class RedmineClient {
return LocalDateTime.parse(dateStr);
}
} catch (DateTimeParseException ex) {
System.err.println("Error al parsear fecha '" + fieldName + "': " + dateStr);
log.error("Error al parsear fecha '" + fieldName + "': " + dateStr);
return null;
}
}


+ 0
- 1
src/main/java/mx/gob/jumapacelaya/api/SecurityService.java View File

@ -1 +0,0 @@
package mx.gob.jumapacelaya.api;

+ 16
- 33
src/main/java/mx/gob/jumapacelaya/controller/SecurityConfiguration.java View File

@ -19,6 +19,21 @@ import org.springframework.ldap.core.support.LdapContextSource;
public class SecurityConfiguration extends VaadinWebSecurity {
@Value("${spring.ldap.domain}")
private String ldapDomain;
@Value("${spring.ldap.urls}")
private String ldapUrls;
@Value("${spring.ldap.url}")
private String ldapUrl;
@Value("${spring.ldap.base}")
private String ldapBase;
@Value("${spring.ldap.password}")
private String ldapPassword;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
@ -40,45 +55,13 @@ public class SecurityConfiguration extends VaadinWebSecurity {
// !Esta es la real autenticacion con ldap
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("JUMAPACELAYA.GOB.MX", "ldap://172.16.0.1");
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(ldapDomain, ldapUrl);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
auth.authenticationProvider(provider);
}
// !Autenticacion local solo para que lo vea el departamento de calidad
/*@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// Configura la autenticación en memoria con un usuario local
auth.inMemoryAuthentication()
.withUser("admin")
.password("{noop}admin") // {noop} indica que la contraseña no está cifrada (solo para pruebas)
.roles("ADMIN")
.and()
.withUser("jlermal")
.password("{noop}Temporal1")
.roles("USER")
.and()
.withUser("mramirezg")
.password("{noop}Temporal1")
.roles("ADMIN");
}*/
@Value("${spring.ldap.urls}")
private String ldapUrls;
@Value("${spring.ldap.base}")
private String ldapBase;
@Value("${spring.ldap.password}")
private String ldapPassword;
@Bean
public LdapContextSource ldapContextSource() {
LdapContextSource contextSource = new LdapContextSource();


+ 0
- 1
src/main/java/mx/gob/jumapacelaya/datasource/MysqlDataSource.java View File

@ -24,7 +24,6 @@ public class MysqlDataSource {
@Bean(name = "mysqlJdbcTemplate")
@Autowired
public JdbcTemplate mysqlTemplate(@Qualifier("mysqlDB") DataSource mysqlDB) {
return new JdbcTemplate(mysqlDB);
}

+ 0
- 1
src/main/java/mx/gob/jumapacelaya/datasource/OracleDataSource.java View File

@ -20,7 +20,6 @@ public class OracleDataSource {
}
@Bean(name = "oracleTemplate")
@Autowired
public JdbcTemplate oracleJdbcTemplate(@Qualifier("oracleDB") DataSource oracleDB) {
return new JdbcTemplate(oracleDB);
}


+ 38
- 0
src/main/java/mx/gob/jumapacelaya/models/encuestas/ConteoEncuestas.java View File

@ -0,0 +1,38 @@
package mx.gob.jumapacelaya.models.encuestas;
public class ConteoEncuestas {
private int totalEnviadas;
private int totalRespondidas;
private Double porcentajeRespondidas;
public ConteoEncuestas(int totalEnviadas, int totalRespondidas, Double porcentajeRespondidas) {
this.totalEnviadas = totalEnviadas;
this.totalRespondidas = totalRespondidas;
this.porcentajeRespondidas = porcentajeRespondidas;
}
public int getTotalEnviadas() {
return totalEnviadas;
}
public void setTotalEnviadas(int totalEnviadas) {
this.totalEnviadas = totalEnviadas;
}
public int getTotalRespondidas() {
return totalRespondidas;
}
public void setTotalRespondidas(int totalRespondidas) {
this.totalRespondidas = totalRespondidas;
}
public Double getPorcentajeRespondidas() {
return porcentajeRespondidas;
}
public void setPorcentajeRespondidas(Double porcentajeRespondidas) {
this.porcentajeRespondidas = porcentajeRespondidas;
}
}

+ 68
- 0
src/main/java/mx/gob/jumapacelaya/models/encuestas/ConteoRespuestas.java View File

@ -0,0 +1,68 @@
package mx.gob.jumapacelaya.models.encuestas;
public class ConteoRespuestas {
private int preguntaId;
private String pregunta;
private int totalRespuestas;
private int totalSi;
private int totalNo;
private Double porcentaje;
public ConteoRespuestas(int preguntaId, String pregunta, int totalRespuestas, int totalSi, int totalNo, Double porcentaje) {
this.preguntaId = preguntaId;
this.pregunta = pregunta;
this.totalRespuestas = totalRespuestas;
this.totalSi = totalSi;
this.totalNo = totalNo;
this.porcentaje = porcentaje;
}
public int getPreguntaId() {
return preguntaId;
}
public void setPreguntaId(int preguntaId) {
this.preguntaId = preguntaId;
}
public String getPregunta() {
return pregunta;
}
public void setPregunta(String pregunta) {
this.pregunta = pregunta;
}
public int getTotalRespuestas() {
return totalRespuestas;
}
public void setTotalRespuestas(int totalRespuestas) {
this.totalRespuestas = totalRespuestas;
}
public int getTotalSi() {
return totalSi;
}
public void setTotalSi(int totalSi) {
this.totalSi = totalSi;
}
public int getTotalNo() {
return totalNo;
}
public void setTotalNo(int totalNo) {
this.totalNo = totalNo;
}
public Double getPorcentaje() {
return porcentaje;
}
public void setPorcentaje(Double porcentaje) {
this.porcentaje = porcentaje;
}
}

+ 102
- 0
src/main/java/mx/gob/jumapacelaya/models/encuestas/MantenimientosSinEncuesta.java View File

@ -0,0 +1,102 @@
package mx.gob.jumapacelaya.models.encuestas;
import java.time.LocalDate;
public class MantenimientosSinEncuesta {
private int mantenimientoId;
private int planId;
private LocalDate fecha;
private String periodo;
private String tipomant;
private String departamento;
private String nomUsuario;
private String email;
private String encuesta;
public MantenimientosSinEncuesta(int mantenimientoId, int planId, LocalDate fecha,
String periodo, String tipomant, String departamento,
String nomUsuario, String email, String encuesta) {
this.mantenimientoId = mantenimientoId;
this.planId = planId;
this.fecha = fecha;
this.periodo = periodo;
this.tipomant = tipomant;
this.departamento = departamento;
this.nomUsuario = nomUsuario;
this.email = email;
this.encuesta = encuesta;
}
public int getMantenimientoId() {
return mantenimientoId;
}
public void setMantenimientoId(int mantenimientoId) {
this.mantenimientoId = mantenimientoId;
}
public int getPlanId() {
return planId;
}
public void setPlanId(int planId) {
this.planId = planId;
}
public LocalDate getFecha() {
return fecha;
}
public void setFecha(LocalDate fecha) {
this.fecha = fecha;
}
public String getPeriodo() {
return periodo;
}
public void setPeriodo(String periodo) {
this.periodo = periodo;
}
public String getTipomant() {
return tipomant;
}
public void setTipomant(String tipomant) {
this.tipomant = tipomant;
}
public String getDepartamento() {
return departamento;
}
public void setDepartamento(String departamento) {
this.departamento = departamento;
}
public String getNomUsuario() {
return nomUsuario;
}
public void setNomUsuario(String nomUsuario) {
this.nomUsuario = nomUsuario;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getEncuesta() {
return encuesta;
}
public void setEncuesta(String encuesta) {
this.encuesta = encuesta;
}
}

+ 147
- 69
src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java View File

@ -1,20 +1,19 @@
package mx.gob.jumapacelaya.services;
import mx.gob.jumapacelaya.models.*;
import mx.gob.jumapacelaya.models.encuestas.Pregunta;
import mx.gob.jumapacelaya.models.encuestas.Respuesta;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OraclePreparedStatement;
import oracle.sql.CLOB;
import org.apache.poi.ss.usermodel.*;
import mx.gob.jumapacelaya.models.encuestas.*;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.security.interfaces.RSAKey;
import java.sql.*;
import java.time.LocalDate;
import java.util.ArrayList;
@ -24,6 +23,7 @@ import java.util.UUID;
@Service
public class DatabaseService {
private static final Logger logger = LoggerFactory.getLogger(DatabaseService.class);
@Value("${db.url}")
private String dbUrl;
@ -62,7 +62,7 @@ public class DatabaseService {
tiposDeMantenimientos.add(tipo);
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener tipos de mantenimientos: ", e);
}
return tiposDeMantenimientos;
}
@ -83,13 +83,11 @@ public class DatabaseService {
}
} catch (SQLException e) {
e.printStackTrace();
System.err.println("Error al obtener nomenclatura: " + e.getMessage());
logger.error("Error al obtener nomenclatura: ", e);
}
return nomenclatura;
}
/* -------------- Metodo para obtener a los usuarios ---------------- */
public List<Usuario> getUsuarios() {
List<Usuario> usuarios = new ArrayList<>();
@ -109,12 +107,11 @@ public class DatabaseService {
usuarios.add(usuario);
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener usuarios: ", e);
}
return usuarios;
}
/* -------------- Metodo para obtener los departamentos ---------------- */
public List<DepartamentosModel> getDepartamentos() {
List<DepartamentosModel> departamentos = new ArrayList<>();
@ -132,12 +129,11 @@ public class DatabaseService {
departamentos.add(departamentosModel);
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener departamentos: ", e);
}
return departamentos;
}
/* -------------- Metodo para obtener los tipos de hardware ---------------- */
public List<TiposHardware> getTiposHardware() {
List<TiposHardware> tiposHardware = new ArrayList<>();
@ -155,13 +151,11 @@ public class DatabaseService {
tiposHardware.add(tiposHardwareModel);
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener tipos de hardware: ", e);
}
return tiposHardware;
}
/* ----------------Obtener el Plan Anual de Mantenimiento ---------------- */
public List<PlanAnual> getPlanAnual() {
List<PlanAnual> planAnualList = new ArrayList<>();
@ -208,14 +202,12 @@ public class DatabaseService {
);
planAnualList.add(planAnual);
}
System.out.println("Registros obtenidos: " + planAnualList.size());
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener plan anual: ", e);
}
return planAnualList;
}
/* ----------------Obtener el Plan Anual de Mantenimiento por ID ---------------- */
public PlanAnual getPlanAnualPorId(int id) {
String query = """
@ -262,13 +254,11 @@ public class DatabaseService {
}
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener plan anual por id: ", e);
}
return null;
}
/* ----------------Obtener detalles del mantenimiento ---------------- */
public DetalleMantenimientoModel getDetalleMantenimientoPorPlanAnualId(int planAnualId) {
String query = "SELECT\n" +
@ -296,7 +286,7 @@ public class DatabaseService {
try (Connection connection = getMysqlConnection();
PreparedStatement statement = connection.prepareStatement(query)) {
statement.setInt(1, planAnualId);
try (ResultSet rs = statement.executeQuery()) {
@ -319,12 +309,71 @@ public class DatabaseService {
}
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener detalles del mantenimiento por plan anual id: ", e);
}
return null;
}
/* ----------------Obtener mantenimientos sin encuesta respondida por periodo ---------------- */
public List<MantenimientosSinEncuesta> getEncuestPendientes(String mes, int anio) {
List<MantenimientosSinEncuesta> lista = new ArrayList<>();
String query = """
SELECT
m.MANTENIMIENTOID MANTID,
m.PLANANUALID PLANID,
m.FECHA,
ms.NOMBRE PERIODO,
t.NOMBRE TIPOMANT,
d.DESCRIPCION DEPTO,
u.NOMBRE NOMUSUARIO,
u.EMAIL,
m.ENCUESTA
FROM MANTENIMIENTOS m
JOIN TIPOMANT t
ON t.TIPOMANTID = m.TIPOMANTID
JOIN DEPARTAMENTOS d
on d.DEPARTAMENTOID = m.DEPARTAMENTOID
JOIN USUARIOS u
on u.EMPLEADOID = m.EMPLEADOID
JOIN PLANANUAL p
ON p.PLANANUALID = m.PLANANUALID
JOIN MESES ms
ON ms.MESID = p.MESID
WHERE m.ENCUESTA = 'N'
AND m.TIPOMANTID = 1
AND m.PLANANUALID IS NOT NULL
AND UPPER(ms.NOMBRE) = UPPER(?)
AND EXTRACT(YEAR FROM m.FECHA) = ?
""";
try(Connection connection = getMysqlConnection();
PreparedStatement stmt = connection.prepareStatement(query)) {
stmt.setString(1, mes.toUpperCase());
stmt.setInt(2, anio);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
lista.add(new MantenimientosSinEncuesta(
rs.getInt("MANTID"),
rs.getInt("PLANID"),
rs.getDate("FECHA").toLocalDate(),
rs.getString("PERIODO"),
rs.getString("TIPOMANT"),
rs.getString("DEPTO"),
rs.getString("NOMUSUARIO"),
rs.getString("EMAIL"),
rs.getString("ENCUESTA")
));
}
} catch (SQLException e) {
logger.error("Error al obtener mantenimientos sin encuesta respondida por periodo: ", e);
}
return lista;
}
/* ----------------Obtener detalles del hardaware por ID ---------------- */
public List<HardwareDetalle> getHardwaredetallePorMantId(int mantenimientoId) {
@ -333,10 +382,10 @@ public class DatabaseService {
"FROM HARDWAREDET h\r\n" + //
"INNER JOIN TIPOSHARDWARE t ON h.TIPOHARDWAREID = t.TIPOHARDWAREID \r\n" + //
"WHERE h.MANTENIMIENTOID = ?";
try (Connection conn = getMysqlConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, mantenimientoId);
ResultSet rs = stmt.executeQuery();
@ -353,19 +402,18 @@ public class DatabaseService {
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener detalles del hardaware por ID: ", e);
}
return detalles;
}
/* ---------------- Obtener las actualizaciones de seguridad ---------------- */
public List<ActualizacioneSeguridadModel> getActualizacionesSeg(int mantenimientoId) {
List<ActualizacioneSeguridadModel> actualizaciones = new ArrayList<>();
String query = "SELECT *\r\n" + //
"FROM ACTUALIZACIONESSEG a\r\n" + //
"WHERE a.MANTENIMIENTOID = ?";
try (Connection conn = getMysqlConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
@ -383,12 +431,11 @@ public class DatabaseService {
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener las actualizaciones de seguridad: ", e);
}
return actualizaciones;
}
/* ---------------- Obtener los mantenimientos correctivos ---------------- */
public List<MantCorrectivosModel> getMantenimientosCorrectivos(int tipomantId) {
List<MantCorrectivosModel> mantCorrectivos = new ArrayList<>();
@ -430,13 +477,11 @@ public class DatabaseService {
}
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener los mantenimientos correctivos: ", e);
}
return mantCorrectivos;
}
/*-=iii=<() *-=iii=<() *-=iii=<() *-=iii=<() *-=iii=<() *-=iii=<()*/
/*-=iii=<() *-=iii=<() *-=iii=<() *-=iii=<() *-=iii=<()*/
/*-=iii=<() *-=iii=<() *-=iii=<() *-=iii=<()*/
@ -517,14 +562,13 @@ public class DatabaseService {
connection.commit();
} catch (SQLException e) {
System.err.println("Error al insertar mantenimiento: " + e.getMessage());
e.printStackTrace();
logger.error("Error al insertar mantenimiento: ", e);
// En caso de error, se hace rollback
if (connection != null) {
try {
connection.rollback();
} catch (SQLException rollbackEx) {
System.err.println("Error al hacer rollback: " + rollbackEx.getMessage());
logger.error("Error al hacer rollback: ", rollbackEx);
}
}
} finally {
@ -560,12 +604,11 @@ public class DatabaseService {
int rowsAffected = preparedStatement.executeUpdate();
isInserted = rowsAffected > 0;
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al insertar hardware: ", e);
}
return isInserted;
}
public int getUltimoMantenimientoId() {
int ultimoId = -1;
try (Connection connection = getMysqlConnection()) {
@ -577,12 +620,11 @@ public class DatabaseService {
}
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener el ultimo id de mantenimiento: ", e);
}
return ultimoId;
}
// INSERTAR EN LA TABLA: ACTUALIZACIONESSEG
public boolean insertActualizacionSeg(String descripcion, String otras, int mantenimientoId) {
String query = "INSERT INTO ACTUALIZACIONESSEG (descripcion, otrasactualizaciones, mantenimientoid) VALUES (?, ?, ?)";
@ -602,7 +644,6 @@ public class DatabaseService {
}
}
// INSERTAR ARCHIVO EXCEL EN LA TABLA: PLANANUAL
public void insertarDesdeExcel(InputStream inputStream) {
String query = "INSERT INTO PLANANUAL (NOMEQUIPO, AREA, MONITOR, TECLADO, MOUSE, " +
@ -677,7 +718,6 @@ public class DatabaseService {
}
}
// INSERTAR NUEVO EQUIPO INDIVIDUAL EN PLANANUAL
public void insertarNuevoEquipo(String nomequipo, String area, boolean monitor, boolean teclado,
boolean mouse, boolean regulador, boolean cpu, boolean impresora,
@ -724,7 +764,6 @@ public class DatabaseService {
}
// Método auxiliar para obtener un valor de tipo Date de una celda
private Date getDateCellValue(Cell cell) {
if (cell != null) {
@ -780,7 +819,6 @@ public class DatabaseService {
return false; // Valor por defecto si la celda es nula
}
/* ----------------Actualizar los detalles del del mantenimiento por ID ---------------- */
public boolean actualizarPlanAnual(int planAnualId, String nombreEquipo) {
String sql = "UPDATE PLANANUAL SET NOMEQUIPO=? WHERE PLANANUALID=?";
@ -790,7 +828,7 @@ public class DatabaseService {
stmt.setInt(2, planAnualId);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al actualizar el plan anual: ", e);
return false;
}
}
@ -817,12 +855,11 @@ public class DatabaseService {
stmt.setInt(8, mantenimientoId);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al actualizar el mantenimiento: ", e);
return false;
}
}
/* ----------------Actualizar los detalles del hardaware por ID ---------------- */
public boolean actualizarHardwareDetalle(HardwareDetalle detalle) {
String sql = "UPDATE HARDWAREDET SET MODELO=?, NUMSERIE=?, PLACA=? WHERE HARDWAREDETID=?";
@ -834,12 +871,11 @@ public class DatabaseService {
stmt.setInt(4, detalle.getHardwareDetId());
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al actualizar el detalle del hardware: ", e);
return false;
}
}
/* ----------------Actualizar las actualizaciones de seguridad por ID ---------------- */
public boolean actualizarActualizacionSeg(ActualizacioneSeguridadModel actualizacion) {
String sql = "UPDATE ACTUALIZACIONESSEG SET OTRASACTUALIZACIONES=? WHERE ACTUALIZACIONSEGID=?";
@ -849,12 +885,11 @@ public class DatabaseService {
stmt.setInt(2, actualizacion.getActualizacionsegId());
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al actualizar la actualización de seguridad: ", e);
return false;
}
}
/* ---------------- Insertar en bitacora ---------------- */
public boolean insertarBitacora(int mantenimientoid, String usuarioid, LocalDate fechora, String motivo) {
String query = "INSERT INTO BITACORACTUALIZACIONES (MANTENIMIENTOID, USUARIOID, FECHORA, MOTIVO) VALUES (?, ?, ?, ?)";
@ -866,12 +901,11 @@ public class DatabaseService {
stmt.setString(4, motivo);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al insertar en bitacora: ", e);
return false;
}
}
/* ---------------- Insertar en bitacora de eliminacion de equipos ---------------- */
public boolean insertarBitacoraEliminacion(int plananualid, String usuarioid, LocalDate fechora, String motivo) {
String query = "INSERT INTO BITACORAELIMINACIONES (PLANANUALID, USUARIOID, FECHAHORA, MOTIVO) VALUES (?, ?, ?, ?)";
@ -883,12 +917,11 @@ public class DatabaseService {
stmt.setString(4, motivo);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al insertar en bitacora de eliminacion de equipos: ", e);
return false;
}
}
/* ---------------- Eliminar equipo de PLAN ANUAL ---------------- */
public void eliminarEquipoPlanAnual(int planAnualId) {
String sql = "DELETE FROM PLANANUAL WHERE PLANANUALID = ?";
@ -903,8 +936,7 @@ public class DatabaseService {
System.out.println("No se encontró el equipo con ID: " + planAnualId);
}
} catch (SQLException e) {
System.err.println("Error al eliminar el equipo de PLANANUAL: " + e.getMessage());
e.printStackTrace();
logger.error("Error al eliminar el equipo de PLANANUAL: ", e);
}
}
@ -935,19 +967,18 @@ public class DatabaseService {
preguntas.add(pregunta);
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al obtener las preguntas de la encuesta: ", e);
}
return preguntas;
}
public void insertRespuestas(int manteniminetoId, List<Respuesta> respuestas, int empleadoid, String token) {
public void insertRespuestas(int manteniminetoId, List<Respuesta> respuestas, int empleadoid, String comentarios, String token) {
String insertQuery = """
insert into RESPUESTAS
(MANTENIMIENTOID,PREGUNTAID,FECHARESPUESTA,RESPUESTA,EMPLEADOID)
(MANTENIMIENTOID,PREGUNTAID,FECHARESPUESTA,RESPUESTA,EMPLEADOID,COMENTARIOS)
values
(?,?,?,?,?)
(?,?,?,?,?,?)
""";
String updateQuery = """
@ -978,6 +1009,7 @@ public class DatabaseService {
insertStmt.setTimestamp(3, ahora);
insertStmt.setBoolean(4, r.getRespuesta());
insertStmt.setInt(5, empleadoid);
insertStmt.setString(6, comentarios);
insertStmt.addBatch();
}
@ -1003,7 +1035,6 @@ public class DatabaseService {
}
}
public String crearTokenEncuesta(int mantenimientoid) {
String token = UUID.randomUUID().toString();
Timestamp expira = new Timestamp(System.currentTimeMillis() + (24 * 60 * 60 * 1000));
@ -1018,7 +1049,7 @@ public class DatabaseService {
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al crear el token de encuesta: ", e);
}
return token;
}
@ -1035,9 +1066,56 @@ public class DatabaseService {
return rs.getInt("MANTENIMIENTOID");
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error al validar el token de encuesta: ", e);
}
return null;
}
public List<ConteoEncuestas> getTotalEncuestas() {
List<ConteoEncuestas> totalEncuestas = new ArrayList<>();
String query = "SELECT * FROM VW_CONTEO_ENCUESTAS";
try (Connection conn = getMysqlConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
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);
}
return totalEncuestas;
}
public List<ConteoRespuestas> getTotalRespuestas() {
List<ConteoRespuestas> totalRespuestas = new ArrayList<>();
String query = "SELECT * FROM VW_CONTEO_RESPUESTAS";
try (Connection conn = getMysqlConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(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);
}
} catch (SQLException e) {
logger.error("Error al obtener el total de respuestas: ", e);
}
return totalRespuestas;
}
/***************************************************************************************************************************************/
}

+ 8
- 12
src/main/java/mx/gob/jumapacelaya/services/EmailService.java View File

@ -1,21 +1,14 @@
package mx.gob.jumapacelaya.services;
import com.vaadin.flow.component.notification.Notification;
import jakarta.activation.DataSource;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.util.ByteArrayDataSource;
import mx.gob.jumapacelaya.models.Usuario;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMailMessage;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.InputStream;
@Service
public class EmailService {
@ -23,10 +16,12 @@ public class EmailService {
@Autowired
private JavaMailSender mailSender;
private static final Logger log = LoggerFactory.getLogger(EmailService.class);
public void enviarCorreo(String destinatario, String asunto, String cuerpo, String imagePath) {
try {
MimeMessage mensaje = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mensaje, true);
MimeMessageHelper helper = new MimeMessageHelper(mensaje, true, "UTF-8");
helper.setTo(destinatario);
helper.setSubject(asunto);
helper.setFrom("noreply@jumapacelaya.gob.mx");
@ -37,10 +32,11 @@ public class EmailService {
helper.addInline("image_id", imgResource);
mailSender.send(mensaje);
System.out.println("Correo enviado con imagen exitosamente");
log.info("Correo enviado a {}", destinatario);
} catch (Exception e) {
e.printStackTrace();
log.error("Error al enviar el correo: ", e);
throw new RuntimeException("Error al enviar el correo: " + e.getMessage());
}
}


+ 1
- 1
src/main/java/mx/gob/jumapacelaya/services/LdapService.java View File

@ -25,7 +25,7 @@ public class LdapService {
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchControls.setReturningAttributes(new String[]{"givenName", "sn", "mail"});
searchControls.setReturningObjFlag(true);
searchControls.setReturningObjFlag(false);
List<CustomUserDetails> result = ldapTemplate.search("", filter.encode(), searchControls, (Attributes attrs) -> {
String firstName = getAttribute(attrs, "givenName");


+ 261
- 113
src/main/java/mx/gob/jumapacelaya/ui/ActDiariaView.java View File

@ -11,9 +11,7 @@ import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridVariant;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
@ -24,9 +22,9 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.popover.Popover;
import com.vaadin.flow.component.popover.PopoverPosition;
import com.vaadin.flow.component.progressbar.ProgressBar;
import com.vaadin.flow.component.richtexteditor.RichTextEditor;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.function.SerializableBiConsumer;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamRegistration;
@ -34,31 +32,29 @@ import com.vaadin.flow.server.StreamResource;
import jakarta.annotation.security.PermitAll;
import mx.gob.jumapacelaya.api.RedmineClient;
import mx.gob.jumapacelaya.api.ServerProperties;
import mx.gob.jumapacelaya.models.ActividadDiaria;
import mx.gob.jumapacelaya.models.RedmineUser;
import mx.gob.jumapacelaya.models.Ticket;
import mx.gob.jumapacelaya.services.LdapService;
import mx.gob.jumapacelaya.services.UserService;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.lineawesome.LineAwesomeIcon;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Stream;
@ -91,11 +87,12 @@ public class ActDiariaView extends VerticalLayout {
// Layout de opciones
opcionesLyt = new HorizontalLayout();
opcionesLyt.setWidthFull();
opcionesLyt.setMargin(false);
opcionesLyt.setAlignItems(Alignment.BASELINE);
//opcionesLyt.setMargin(false);
opcionesLyt.getStyle()
.set("box-shadow", "0 4px 8px rgba(0,0,0,0.2)")
.set("border-radius", "10px")
.set("background-color", "white")
//.set("background-color", "white")
.set("padding", "1rem")
.set("margin", "1rem auto");
@ -103,18 +100,42 @@ public class ActDiariaView extends VerticalLayout {
chkSoloAbiertos = new Checkbox("Ver solo abiertos");
chkSoloAbiertos.setValue(false); // inicial: mostrar cerrados
fechaDesde = new DatePicker("Fecha desde:");
fechaHasta = new DatePicker("Fecha hasta:");
btnBuscar = new Button("Buscar");
fechaDesde = new DatePicker();
fechaDesde.setPlaceholder("Fecha desde:");
fechaDesde.setLocale(new Locale("es", "MX"));
DatePicker.DatePickerI18n i18n = new DatePicker.DatePickerI18n()
.setWeekdays(List.of("Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"))
.setWeekdaysShort(List.of("Dom","Lun","Mar","Mié","Jue","Vie","Sab"))
.setMonthNames(List.of("Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"))
.setFirstDayOfWeek(1)
.setToday("Hoy")
.setCancel("Cancelar")
.setDateFormat("dd/MM/yyyy");
fechaDesde.setI18n(i18n);
fechaHasta = new DatePicker();
fechaHasta.setPlaceholder("Fecha hasta:");
fechaHasta.setLocale(new Locale("es", "MX"));
DatePicker.DatePickerI18n i18nh = new DatePicker.DatePickerI18n()
.setWeekdays(List.of("Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"))
.setWeekdaysShort(List.of("Dom","Lun","Mar","Mié","Jue","Vie","Sab"))
.setMonthNames(List.of("Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"))
.setFirstDayOfWeek(1)
.setToday("Hoy")
.setCancel("Cancelar")
.setDateFormat("dd/MM/yyyy");
fechaHasta.setI18n(i18nh);
btnBuscar = new Button("Buscar", VaadinIcon.SEARCH.create());
// Listener del checkbox
chkSoloAbiertos.addValueChangeListener(e -> {
boolean soloAbiertos = e.getValue(); // marcado = abiertos
loadTickets(soloAbiertos);
fechaDesde.setEnabled(!soloAbiertos);
fechaHasta.setEnabled(!soloAbiertos);
btnBuscar.setEnabled(!soloAbiertos);
grid.getDataProvider().refreshAll();
});
// Estado inicial
@ -128,6 +149,30 @@ public class ActDiariaView extends VerticalLayout {
opcionesLyt.add(chkSoloAbiertos, fechaDesde, fechaHasta, btnBuscar);
grid.setItems(
query -> {
try {
return redmineClient.getTickets(
userService.getRedmineUser(),
chkSoloAbiertos.getValue(),
query.getOffset(),
query.getLimit(),
fechaDesde.getValue(),
fechaHasta.getValue()
).stream();
} catch (Exception e) {
log.error("Error al cargar tickets", e);
return Stream.empty();
}
},
query -> redmineClient.getTotalTickets(
userService.getRedmineUser(),
chkSoloAbiertos.getValue(),
fechaDesde.getValue(),
fechaHasta.getValue()
)
);
// Configuración de columnas del grid
grid.addColumn(Ticket::getId).setHeader("No.")
.setAutoWidth(true)
@ -148,6 +193,7 @@ public class ActDiariaView extends VerticalLayout {
return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")) : "";
}).setHeader("Fecha creación")
.setAutoWidth(true)
.setSortable(true)
.setKey("fechaCreacion");
grid.addColumn(ticket -> {
@ -155,6 +201,7 @@ public class ActDiariaView extends VerticalLayout {
return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")) : "";
}).setHeader("Fecha cierre")
.setAutoWidth(true)
.setSortable(true)
.setKey("fechaCierre");
grid.addColumn(ticket -> {
@ -198,7 +245,7 @@ public class ActDiariaView extends VerticalLayout {
} else {
return "";
}
}).setHeader("Duración").setAutoWidth(true).setKey("duracion");
}).setHeader("Duración").setAutoWidth(true).setSortable(true).setKey("duracion");
grid.addColumn(ticket ->
@ -217,7 +264,6 @@ public class ActDiariaView extends VerticalLayout {
grid.addComponentColumn(ticket -> {
Button btnVer = new Button(new Icon(VaadinIcon.EYE));
btnVer.addClickListener(event -> showDescription(ticket));
btnVer.getStyle().set("color", "#A02142");
return btnVer;
});
@ -229,6 +275,7 @@ public class ActDiariaView extends VerticalLayout {
btnColumns = new Button(VaadinIcon.GRID_H.create());
// Botón para exportar
btnExport = new Button("Exportar", LineAwesomeIcon.FILE_EXCEL.create());
btnExport.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
btnExport.addClickListener(e -> exportTicketsCerrados());
showColumnsLyt = new HorizontalLayout(btnExport, btnColumns);
showColumnsLyt.setJustifyContentMode(JustifyContentMode.END);
@ -281,30 +328,6 @@ public class ActDiariaView extends VerticalLayout {
// Invertir parámetro para que coincida con RedmineClient
boolean paramCliente = !soloAbiertos;
grid.setItems(
query -> {
try {
return redmineClient.getTickets(
user,
chkSoloAbiertos.getValue(),
query.getOffset(),
query.getLimit(),
fechaDesde.getValue(),
fechaHasta.getValue()
).stream();
} catch (Exception e) {
e.printStackTrace();
return Stream.empty();
}
},
query -> redmineClient.getTotalTickets(
user,
chkSoloAbiertos.getValue(),
fechaDesde.getValue(),
fechaHasta.getValue()
)
);
}
@ -365,7 +388,7 @@ public class ActDiariaView extends VerticalLayout {
UI.getCurrent().getPage().executeJs("setTimeout(() => { window.location.reload(); }, 3000);");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
log.error("Error al cerrar el ticket " + ticket.getId(), e);
Notification.show("Error al cerrar el ticket " + ticket.getId());
}
}
@ -399,85 +422,210 @@ public class ActDiariaView extends VerticalLayout {
// Metodo para exportar los tickets cerrados a un archivo de Excel
private void exportTicketsCerrados() {
try {
RedmineUser user = userService.getRedmineUser();
// Traer TODOS los tickets cerrados, respetando fechas si están seleccionadas
List<Ticket> ticketsCerrados = redmineClient.getAllTickets(
user,
false, // false = cerrados
fechaDesde.getValue(),
fechaHasta.getValue()
);
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Tickets cerrados");
String[] headers = {"ID","Tipo","Tiempo estimado","Fecha creación","Fecha cierre",
"Fecha actualización","Situación","Estado","Duración","Autor","Asunto"};
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
final UI ui = UI.getCurrent();
final RedmineUser user = userService.getRedmineUser();
final LocalDate desde = fechaDesde.getValue();
final LocalDate hasta = fechaHasta.getValue();
int rowNum = 1;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
for (Ticket ticket : ticketsCerrados) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(ticket.getId());
row.createCell(1).setCellValue(ticket.getType());
row.createCell(2).setCellValue(ticket.getEstimatedHrs() + " horas");
row.createCell(3).setCellValue(ticket.getDateCreate() != null ? ticket.getDateCreate().format(formatter) : "");
row.createCell(4).setCellValue(ticket.getDateClose() != null ? ticket.getDateClose().format(formatter) : "");
row.createCell(5).setCellValue(ticket.getUpdateOn() != null ? ticket.getUpdateOn().format(formatter) : "");
row.createCell(7).setCellValue(ticket.getStatus());
row.createCell(9).setCellValue(ticket.getAuthor() != null ? ticket.getAuthor().getUsername() : "");
row.createCell(10).setCellValue(ticket.getSubject());
if (ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long dias = ChronoUnit.DAYS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(8).setCellValue(dias + " días");
} else {
row.createCell(8).setCellValue("");
if (ui == null) return;
Dialog loadingDialog = createLoadingDialog("Generando archivo por favor espere...");
loadingDialog.open();
new Thread(() -> {
try {
// Traer TODOS los tickets cerrados, respetando fechas si están seleccionadas
List<Ticket> ticketsCerrados = redmineClient.getAllTickets(
user,
false, // false = cerrados
desde,
hasta
);
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Tickets cerrados");
CellStyle titleStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 20);
titleStyle.setFont(titleFont);
titleStyle.setAlignment(HorizontalAlignment.CENTER);
CellStyle subTitleStyle = workbook.createCellStyle();
Font subTitleFont = workbook.createFont();
subTitleFont.setFontHeightInPoints((short) 18);
subTitleStyle.setFont(subTitleFont);
subTitleStyle.setAlignment(HorizontalAlignment.CENTER);
int ultimaCol = 10;
Row row1 = sheet.createRow(1);
Cell cell1 = row1.createCell(0);
cell1.setCellValue("JUNTA MUNICIPAL DE AGUA POTABLE Y ALCANTARILLADO DE CELAYA.");
cell1.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, ultimaCol));
Row row2 = sheet.createRow(2);
Cell cell2 = row2.createCell(0);
cell2.setCellValue("División Del Norte #132, Col. El Vergel, Celaya, Gto. C.P: 38078");
cell2.setCellStyle(subTitleStyle);
sheet.addMergedRegion(new CellRangeAddress(2, 2, 0, ultimaCol));
try (
InputStream jmpaLogo = ActDiariaView.class.getClassLoader()
.getResourceAsStream("META-INF/resources/images/LOGO_24'27.png");
InputStream adminLogo = ActDiariaView.class.getClassLoader()
.getResourceAsStream("META-INF/resources/images/LOGO_admon2.png")
) {
if (jmpaLogo != null || adminLogo != null) {
byte[] bytes = IOUtils.toByteArray(jmpaLogo);
byte[] bytes1 = IOUtils.toByteArray(adminLogo);
int pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
int pictureIdx1 = workbook.addPicture(bytes1, Workbook.PICTURE_TYPE_PNG);
Drawing<?> drawing = sheet.createDrawingPatriarch();
CreationHelper helper = workbook.getCreationHelper();
CreationHelper helper1 = workbook.getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
ClientAnchor anchor1 = helper1.createClientAnchor();
// Posición en la esquina superior izquierda
anchor.setCol1(1);
anchor.setRow1(1);
anchor1.setCol1(10);
anchor1.setRow1(1);
Picture pict = drawing.createPicture(anchor, pictureIdx);
pict.resize(0.30, 0.60); // Ajusta según el tamaño de tu imagen original
Picture pict1 = drawing.createPicture(anchor1, pictureIdx1);
pict1.resize(0.30, 0.37);
} else {
log.error("No se encontró el archivo de imagen en la ruta especificada");
}
} catch (Exception e) {
log.error("Error al procesar el logo", e);
}
if (ticket.getEstimatedHrs() != null && ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long horasReales = ChronoUnit.HOURS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(6).setCellValue(horasReales <= ticket.getEstimatedHrs() ? "En tiempo" : "Excedido");
} else {
row.createCell(6).setCellValue("Sin estimación");
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setColor(IndexedColors.WHITE.getIndex());
headerStyle.setFont(headerFont);
headerStyle.setFillForegroundColor(IndexedColors.MAROON.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headerStyle.setAlignment(HorizontalAlignment.CENTER);
headerStyle.setBorderBottom(BorderStyle.THIN);
String[] headers = {"ID","Tipo","Tiempo estimado","Fecha creación","Fecha cierre",
"Fecha actualización","Situación","Estado","Duración","Autor","Asunto"};
int rowNum = 6;
Row headerRow = sheet.createRow(rowNum++);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
}
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
for (Ticket ticket : ticketsCerrados) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(ticket.getId());
row.createCell(1).setCellValue(ticket.getType());
row.createCell(2).setCellValue(ticket.getEstimatedHrs() + " horas");
row.createCell(3).setCellValue(ticket.getDateCreate() != null ? ticket.getDateCreate().format(formatter) : "");
row.createCell(4).setCellValue(ticket.getDateClose() != null ? ticket.getDateClose().format(formatter) : "");
row.createCell(5).setCellValue(ticket.getUpdateOn() != null ? ticket.getUpdateOn().format(formatter) : "");
row.createCell(7).setCellValue(ticket.getStatus());
row.createCell(9).setCellValue(ticket.getAuthor() != null ? ticket.getAuthor().getUsername() : "");
row.createCell(10).setCellValue(ticket.getSubject());
if (ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long dias = ChronoUnit.DAYS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(8).setCellValue(dias + " días");
} else {
row.createCell(8).setCellValue("");
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
workbook.close();
if (ticket.getEstimatedHrs() != null && ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long horasReales = ChronoUnit.HOURS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(6).setCellValue(horasReales <= ticket.getEstimatedHrs() ? "En tiempo" : "Excedido");
} else {
row.createCell(6).setCellValue("Sin estimación");
}
}
StreamResource resource = new StreamResource("tickets_cerrados.xlsx",
() -> new ByteArrayInputStream(out.toByteArray()));
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
StreamRegistration registration = UI.getCurrent().getSession()
.getResourceRegistry().registerResource(resource);
UI.getCurrent().getPage().executeJs(
"window.open('" + registration.getResourceUri().toString() + "','_blank')"
);
ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
workbook.close();
} catch (Exception e) {
log.error("Error exportando tickets", e);
Notification.show("Error al exportar los tickets", 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
}
ui.access(() -> {
try {
StreamResource resource = new StreamResource("rptGeneralDeTicketsMsaAyda.xlsx",
() -> new ByteArrayInputStream(out.toByteArray()));
StreamRegistration registration = ui.getSession()
.getResourceRegistry().registerResource(resource);
ui.getPage().executeJs(
"window.open('" + registration.getResourceUri().toString() + "','_blank')"
);
} catch (Exception ex) {
log.error("Error al registrar recurso", ex);
} finally {
loadingDialog.close();
}
});
} catch (Exception e) {
log.error("Error exportando tickets", e);
ui.access(() -> {
loadingDialog.close();
Notification.show("Error al generar el reporte", 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
});
}
}).start();
}
private void filtrarTickets() {
boolean soloAbiertos = chkSoloAbiertos.getValue();
loadTickets(soloAbiertos);
grid.getDataProvider().refreshAll();
}
private Dialog createLoadingDialog(String mensaje) {
Dialog dialog = new Dialog();
dialog.setCloseOnEsc(false);
dialog.setCloseOnOutsideClick(false);
VerticalLayout layout = new VerticalLayout();
layout.setAlignItems(Alignment.CENTER);
layout.setPadding(true);
layout.setSpacing(true);
ProgressBar progressBar = new ProgressBar();
progressBar.setIndeterminate(true);
progressBar.setWidth("15rem");
Span texto = new Span(mensaje);
texto.getStyle().set("font-weight", "bold");
texto.getStyle().set("color", "var(--lumo-primary-text-color)");
layout.add(texto, progressBar);
dialog.add(layout);
return dialog;
}
}

+ 203
- 0
src/main/java/mx/gob/jumapacelaya/ui/ConfiguracionView.java View File

@ -0,0 +1,203 @@
package mx.gob.jumapacelaya.ui;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.VaadinSession;
import jakarta.annotation.security.PermitAll;
import java.util.List;
@PermitAll
@PageTitle("Preferencias del Sistema")
@Route(value = "configuracion", layout = MainLayout.class)
public class ConfiguracionView extends VerticalLayout {
private static final String THEME_LOCALSTORAGE_KEY = "appThemePreference";
private boolean initializing = true;
private final List<ColorOptions> primaryTextColorOptions = List.of(
new ColorOptions("Big Dip O´Ruby", "#A02142"),
new ColorOptions("Claret", "#691B31"),
new ColorOptions("Aztec Gold", "#BC955B"),
new ColorOptions("Lion", "#DDC9A3"),
new ColorOptions("Nickel", "#6F7271")
);
private final List<ColorOptions> primaryColorOptions = List.of(
new ColorOptions("Big Dip O´Ruby", "#A02142"),
new ColorOptions("Claret", "#691B31"),
new ColorOptions("Aztec Gold", "#BC955B"),
new ColorOptions("Lion", "#DDC9A3"),
new ColorOptions("Nickel", "#6F7271")
);
public ConfiguracionView() {
setSpacing(true);
add(new H2("⚙\uFE0F Preferencias del Sistema"));
loadCssVariablesFromLocalStorage();
RadioButtonGroup<String> themeSelector = createThemeSelector();
ComboBox<ColorOptions> primaryColor = createColorCombo(
"Color primario",
"--lumo-primary-color",
primaryColorOptions
);
ComboBox<ColorOptions> primaryTextColor = createColorCombo(
"Color de textos",
"--lumo-primary-text-color",
primaryTextColorOptions
);
HorizontalLayout row1 = new HorizontalLayout(themeSelector);
HorizontalLayout row2 = new HorizontalLayout(primaryColor, primaryTextColor);
VerticalLayout layoutPadre = new VerticalLayout(row1, row2);
layoutPadre.getStyle()
.set("border-radius", "10px")
.set("box-shadow", "0 4px 8px rgba(0, 0, 0, 0.2)");
add(layoutPadre);
UI.getCurrent().getPage().executeJs("return true;")
.then(Boolean.class, ok -> initializing = false);
}
/* -------------------- THEME -------------------- */
private RadioButtonGroup<String> createThemeSelector() {
RadioButtonGroup<String> selector = new RadioButtonGroup<>("Modo de interfaz");
selector.setItems("Claro", "Oscuro");
UI.getCurrent().getPage().executeJs(
"return localStorage.getItem($0)", THEME_LOCALSTORAGE_KEY
).then(String.class, theme ->
selector.setValue("dark".equals(theme) ? "Oscuro" : "Claro")
);
selector.addValueChangeListener(e -> {
if (initializing) return;
boolean dark = "Oscuro".equals(e.getValue());
UI.getCurrent().getPage().executeJs(
dark
? "document.documentElement.setAttribute('theme','dark')"
: "document.documentElement.removeAttribute('theme')"
);
UI.getCurrent().getPage().executeJs(
"localStorage.setItem($0,$1)",
THEME_LOCALSTORAGE_KEY,
dark ? "dark" : "light"
);
});
return selector;
}
/* -------------------- COLORS -------------------- */
private ComboBox<ColorOptions> createColorCombo(
String label,
String cssVariable,
List<ColorOptions> options
) {
ComboBox<ColorOptions> combo = new ComboBox<>(label);
combo.setItems(options);
combo.setWidth("250px");
combo.setAllowCustomValue(false);
UI.getCurrent().getPage().executeJs(
"return localStorage.getItem($0)",
"config:" + cssVariable
).then(String.class, saved -> {
options.stream()
.filter(o -> o.getHexValue().equalsIgnoreCase(saved))
.findFirst()
.ifPresent(combo::setValue);
});
combo.addValueChangeListener(e -> {
if (initializing || e.getValue() == null) return;
applyCssVariable(cssVariable, e.getValue().getHexValue());
});
combo.setRenderer(colorRenderer());
return combo;
}
private void applyCssVariable(String variable, String value) {
UI.getCurrent().getPage().executeJs(
"""
document.documentElement.style.setProperty($0,$1);
localStorage.setItem($2,$1);
""",
variable,
value,
"config:" + variable
);
}
private void loadCssVariablesFromLocalStorage() {
UI.getCurrent().getPage().executeJs(
"""
['--lumo-primary-color','--lumo-primary-text-color'].forEach(v=>{
const val = localStorage.getItem('config:'+v);
if(val) document.documentElement.style.setProperty(v,val);
});
"""
);
}
/* -------------------- RENDERER -------------------- */
private ComponentRenderer<Div, ColorOptions> colorRenderer() {
return new ComponentRenderer<>(opt -> {
Div wrapper = new Div();
wrapper.getStyle().set("display","flex").set("align-items","center");
Div dot = new Div();
dot.getStyle()
.set("width","14px")
.set("height","14px")
.set("border-radius","50%")
.set("margin-right","8px")
.set("background-color", opt.getHexValue())
.set("border","1px solid var(--lumo-border-color)");
wrapper.add(dot);
wrapper.add(opt.getName());
return wrapper;
});
}
/* -------------------- MODEL -------------------- */
public static class ColorOptions {
private final String name;
private final String hexValue;
public ColorOptions(String name, String hexValue) {
this.name = name;
this.hexValue = hexValue;
}
public String getName() { return name; }
public String getHexValue() { return hexValue; }
@Override
public String toString() {
return name;
}
}
}

+ 16
- 5
src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java View File

@ -9,11 +9,15 @@ import java.util.List;
import java.util.Map;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.component.page.History;
import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import mx.gob.jumapacelaya.services.EmailService;
import mx.gob.jumapacelaya.services.ReportService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@ -53,6 +57,8 @@ import mx.gob.jumapacelaya.services.SecurityService;
@CssImport("./themes/sistema-mantenimiento/styles.css")
public class DetallesMantView extends VerticalLayout implements BeforeEnterObserver {
private static final Logger logger = LoggerFactory.getLogger(DetallesMantView.class);
@Value("${app.base-url}")
private String baseUrl;
@ -75,6 +81,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
private final Button btnCancelar;
private final Button btnGuardar;
private final Button btnEnviarEncuesta = new Button("Enviar encuesta", LineAwesomeIcon.ENVELOPE_SOLID.create());
private final Button btnVolverAtras = new Button(LineAwesomeIcon.ARROW_LEFT_SOLID.create());
private int planAnualIdActual;
private int mantenimientoIdActual;
private Dialog confirmDialog;
@ -102,7 +109,6 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
mainLayout.getStyle()
.set("box-shadow","0 4px 8px rgba(0,0,0,0.2)")
.set("border-radius", "12px")
.set("background-color", "white")
.set("padding", "1rem")
.set("margin", "1rem auto");
@ -197,9 +203,14 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
gridActualizaciones.addThemeVariants(GridVariant.LUMO_ROW_STRIPES);
History history = UI.getCurrent().getPage().getHistory();
btnVolverAtras.addClickListener(e -> history.back());
btnVolverAtras.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
btnVolverAtras.setTooltipText("Volver a la lista de mantenimientos");
HorizontalLayout botonesHeaderLyt = new HorizontalLayout();
botonesHeaderLyt.setWidthFull();
botonesHeaderLyt.add(btnImprimirRepo, btnEnviarEncuesta);
botonesHeaderLyt.add(btnVolverAtras, btnImprimirRepo, btnEnviarEncuesta);
HorizontalLayout botonesLayout = new HorizontalLayout();
botonesLayout.setWidthFull();
@ -297,7 +308,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
} catch (Exception ex) {
Notification.show("Error al generar el reporte: " + ex.getMessage(), 4000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
ex.printStackTrace();
logger.error("Error al generar el reporte: ", ex);
}
});
@ -542,7 +553,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
String imagePath = "META-INF/resources/images/imgCorreo/imgEncuesta.png";
emailService.enviarCorreo(destinatario,asunto,cuerpo,imagePath);
Notification.show("Encuesta enviada correctamente para el mantenimiento No. " + mantenimientoIdActual, 3000, Notification.Position.MIDDLE)
Notification.show("Encuesta enviada correctamente para el mantenimiento No. " + mantenimientoIdActual, 3000, Notification.Position.BOTTOM_END)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
} else {
Notification.show("Por favor, seleccione un usuario destino", 3000, Notification.Position.MIDDLE);
@ -580,7 +591,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
layout.setAlignItems(Alignment.CENTER);
avisoEncuestaNtf = new Notification(layout);
avisoEncuestaNtf.setPosition(Notification.Position.TOP_CENTER);
avisoEncuestaNtf.setPosition(Notification.Position.TOP_END);
avisoEncuestaNtf.addThemeVariants(NotificationVariant.LUMO_WARNING);
avisoEncuestaNtf.setDuration(5000);
avisoEncuestaNtf.open();


+ 14
- 5
src/main/java/mx/gob/jumapacelaya/ui/EncuestaView.java View File

@ -10,6 +10,7 @@ import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
@ -32,11 +33,10 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
private final DatabaseService encuestasDBService;
private int mantenimientoId = -1;
private final VerticalLayout mainLyt = new VerticalLayout();
private final Span pregunta1Txt = new Span();
private final RadioButtonGroup<String> pregunta1Rb = new RadioButtonGroup<>();
private final Button btnEnviar = new Button("Enviar");
private final TextField txtNumEmpl = new TextField("No. Empleado:");
private String token;
private final TextArea txtComentarios = new TextArea("Comentarios:");
private static class RespuestaComponente {
@ -56,7 +56,6 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
setSpacing(true);
setPadding(true);
setSizeFull();
this.getStyle()
.set("background-image", "url('/images/LOGO_1024X768.jpg')")
@ -65,9 +64,11 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
.set("background-position", "center");
mainLyt.setHeightFull();
mainLyt.setWidth("55%");
//mainLyt.setMaxHeight("90vh");
mainLyt.getStyle()
.set("overflow-y", "auto")
.set("box-shadow", "0 4px 8px rgba(0,0,0,0.2)")
.set("border-radius", "12px")
.set("background-color", "white")
@ -119,6 +120,10 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
respuestasUI.add(new RespuestaComponente(p,radios));
}
txtComentarios.setWidthFull();
txtComentarios.setMaxHeight("5rem");
preguntasLyt.add(txtComentarios);
HorizontalLayout gracias = new HorizontalLayout(new H3("¡Gracias!"));
gracias.setWidthFull();
gracias.setJustifyContentMode(JustifyContentMode.CENTER);
@ -133,7 +138,7 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
mainLyt.removeAll();
mainLyt.add(titulos,preguntasLyt,gracias, btnEnviarLyt);
mainLyt.add(titulos,preguntasLyt ,gracias, btnEnviarLyt);
}
private void procesarRespuestas() {
@ -155,11 +160,15 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
rc.radios.getValue().equals("Si")
));
}
txtComentarios.getValue();
String comentarios = txtComentarios.isEmpty() ? null : txtComentarios.getValue();
encuestasDBService.insertRespuestas(
mantenimientoId,
respuestas,
empleadoId,
comentarios,
this.token
);


+ 28
- 7
src/main/java/mx/gob/jumapacelaya/ui/MainLayout.java View File

@ -1,5 +1,6 @@
package mx.gob.jumapacelaya.ui;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.applayout.AppLayout;
import com.vaadin.flow.component.applayout.DrawerToggle;
import com.vaadin.flow.component.button.Button;
@ -13,12 +14,15 @@ import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.sidenav.SideNav;
import com.vaadin.flow.component.sidenav.SideNavItem;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.server.menu.MenuConfiguration;
import com.vaadin.flow.theme.lumo.LumoUtility;
import mx.gob.jumapacelaya.services.SecurityService;
@CssImport("./themes/sistema-mantenimiento/styles.css")
public class MainLayout extends AppLayout {
public class MainLayout extends AppLayout implements BeforeEnterObserver {
private H2 viewTitle;
private final SecurityService securityService;
@ -45,7 +49,7 @@ public class MainLayout extends AppLayout {
String u = securityService.getAuthenticatedUser();
Span usrNameLabel = new Span(u);
usrNameLabel.getStyle().set("color", "#691b31");
//usrNameLabel.getStyle().set("color", "#691b31");
usrNameLabel.getStyle().set("font-weight", "bold");
usrNameLabel.getStyle().set("font-size", "20px");
@ -70,7 +74,7 @@ public class MainLayout extends AppLayout {
headerLayout.setPadding(true);
headerLayout.setSpacing(false);
headerLayout.setAlignItems(FlexComponent.Alignment.CENTER);
headerLayout.getStyle().set("background-color", "#DDC9A3");
//headerLayout.getStyle().set("background-color", "#DDC9A3");
Image imgLogo = new Image("images/LOGO_900X160.png", "Logo");
imgLogo.setWidthFull();
@ -78,7 +82,7 @@ public class MainLayout extends AppLayout {
headerLayout.add(imgLogo);
Scroller scroller = new Scroller(createNavigation());
scroller.getStyle().set("background-color", "#691b31");
//scroller.getStyle().set("background-color", "#691b31");
addToDrawer(headerLayout, scroller, createFooter());
}
@ -89,9 +93,10 @@ public class MainLayout extends AppLayout {
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.getStyle().set("background-color", "white");
nav.addItem(new SideNavItem("Resultados de Encuestas", ResultEncuestasView.class, VaadinIcon.CLIPBOARD_TEXT.create()));
nav.addItem(new SideNavItem("Preferencias del Sistema", ConfiguracionView.class, VaadinIcon.COG.create()));
nav.getStyle().set("border-radius", "5px");
nav.getStyle().set("opacity", "0.9");
return nav;
}
@ -101,9 +106,25 @@ public class MainLayout extends AppLayout {
return layout;
}
private String getCurrentPageTitle() {
return MenuConfiguration.getPageHeader(getContent()).orElse("");
}
@Override
protected void afterNavigation() {
super.afterNavigation();
viewTitle.setText("Mantenimiento de Hardware");
viewTitle.setText(getCurrentPageTitle());
}
@Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
UI.getCurrent().getPage().executeJs("""
const theme = localStorage.getItem('appThemePreference');
if (theme === 'dark') {
document.documentElement.setAttribute('theme', 'dark');
} else {
document.documentElement.removeAttribute('theme');
}
""");
}
}

+ 6
- 8
src/main/java/mx/gob/jumapacelaya/ui/MantCorrectivoView.java View File

@ -1,6 +1,5 @@
package mx.gob.jumapacelaya.ui;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
@ -24,9 +23,6 @@ import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.function.ValueProvider;
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 de.f0rce.signaturepad.SignaturePad;
@ -36,6 +32,8 @@ import mx.gob.jumapacelaya.services.DatabaseService;
import mx.gob.jumapacelaya.services.EmailService;
import mx.gob.jumapacelaya.services.SecurityService;
import mx.gob.jumapacelaya.services.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.lineawesome.LineAwesomeIcon;
import java.time.LocalDate;
@ -48,6 +46,8 @@ import java.util.*;
@CssImport("./themes/sistema-mantenimiento/styles.css")
public class MantCorrectivoView extends VerticalLayout {
private static final Logger logger = LoggerFactory.getLogger(MantCorrectivoView.class);
private VerticalLayout mainLayout;
private final SecurityService securityService;
private final VerticalLayout controlsLayout;
@ -96,7 +96,6 @@ public class MantCorrectivoView extends VerticalLayout {
headerLayout.getStyle()
.set("box-shadow", "0 4px 8px rgba(0,0,0,0.2)")
.set("border-radius", "10px")
.set("background-color", "white")
.set("padding", "1rem")
.set("margin", "1rem auto");
@ -107,7 +106,6 @@ public class MantCorrectivoView extends VerticalLayout {
mainLayout.getStyle()
.set("box-shadow", "0 4px 8px rgba(0,0,0,0.2)")
.set("border-radius", "12px")
.set("background-color", "white")
.set("padding", "1rem")
.set("margin", "1rem auto");
@ -594,7 +592,7 @@ public class MantCorrectivoView extends VerticalLayout {
} catch (Exception e) {
Notification.show("Error al enviar el correo", 4000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
e.printStackTrace();
logger.error("Error al enviar correo: ", e);
}
// Limpiar campos
@ -624,7 +622,7 @@ public class MantCorrectivoView extends VerticalLayout {
} catch (Exception ex) {
Notification.show("Ocurrio un error inesperado: " + ex.getMessage(), 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
ex.printStackTrace();
logger.error("Error al insertar mantenimiento: ", ex);
}
}


+ 7
- 12
src/main/java/mx/gob/jumapacelaya/ui/MantenimientoView.java View File

@ -1,29 +1,24 @@
package mx.gob.jumapacelaya.ui;
import com.vaadin.flow.component.Component;
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.checkbox.CheckboxGroup;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
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.radiobutton.RadioButtonGroup;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.PageTitle;
@ -35,9 +30,8 @@ import mx.gob.jumapacelaya.services.DatabaseService;
import mx.gob.jumapacelaya.services.EmailService;
import mx.gob.jumapacelaya.services.SecurityService;
import mx.gob.jumapacelaya.services.UserService;
import oracle.net.aso.h;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.lineawesome.LineAwesomeIcon;
import java.time.LocalDate;
@ -50,6 +44,8 @@ import java.util.*;
@CssImport("./themes/sistema-mantenimiento/styles.css")
public class MantenimientoView extends VerticalLayout implements BeforeEnterObserver {
private static final Logger logger = LoggerFactory.getLogger(MantenimientoView.class);
private final SecurityService securityService;
private final VerticalLayout controlsLayout;
private final DatabaseService databaseService;
@ -98,7 +94,6 @@ public class MantenimientoView extends VerticalLayout implements BeforeEnterObse
mainLayout.getStyle()
.set("box-shadow","0 4px 8px rgba(0,0,0,0.2)")
.set("border-radius", "12px")
.set("background-color", "white")
.set("padding", "1rem")
.set("margin", "1rem auto");
@ -718,7 +713,7 @@ public class MantenimientoView extends VerticalLayout implements BeforeEnterObse
} catch (Exception e) {
Notification.show("Error al enviar el correo", 4000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
e.printStackTrace();
logger.error("Error al enviar el correo", e);
}
// Limpiar campos
@ -748,7 +743,7 @@ public class MantenimientoView extends VerticalLayout implements BeforeEnterObse
} catch (Exception ex) {
Notification.show("Ocurrio un error inesperado: " + ex.getMessage(), 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
ex.printStackTrace();
logger.error("Error al insertar mantenimiento", ex);
}
}


+ 214
- 28
src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java View File

@ -4,15 +4,12 @@ import com.vaadin.flow.component.Component;
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.checkbox.Checkbox;
import com.vaadin.flow.component.checkbox.CheckboxGroup;
import com.vaadin.flow.component.checkbox.CheckboxGroupVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridVariant;
import com.vaadin.flow.component.grid.HeaderRow;
@ -28,12 +25,11 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.popover.Popover;
import com.vaadin.flow.component.popover.PopoverPosition;
import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
import com.vaadin.flow.component.textfield.NumberField;
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.provider.ListDataProvider;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
@ -42,20 +38,24 @@ import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import jakarta.annotation.security.PermitAll;
import mx.gob.jumapacelaya.models.PlanAnual;
import mx.gob.jumapacelaya.models.encuestas.MantenimientosSinEncuesta;
import mx.gob.jumapacelaya.services.DatabaseService;
import mx.gob.jumapacelaya.services.EmailService;
import mx.gob.jumapacelaya.services.ReportService;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.vaadin.lineawesome.LineAwesomeIcon;
import java.io.*;
import java.lang.reflect.Array;
import java.sql.Date;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.Year;
import java.time.format.DateTimeFormatter;
@ -70,6 +70,11 @@ import java.util.stream.IntStream;
@CssImport("./themes/sistema-mantenimiento/styles.css")
public class PlanAnualView extends VerticalLayout {
private static final Logger logger = LoggerFactory.getLogger(PlanAnualView.class);
private final EmailService emailService;
@Value("${app.base-url}")
private String baseUrl;
private final Environment env;
private final ReportService reportService;
@ -90,7 +95,7 @@ public class PlanAnualView extends VerticalLayout {
private Popover reportePopover;
ComboBox<Integer> yearFilter;
public PlanAnualView(DatabaseService databaseService, Environment env, ReportService reportService) {
public PlanAnualView(DatabaseService databaseService, Environment env, ReportService reportService, EmailService emailService) {
this.databaseService = databaseService;
this.env = env;
this.reportService = reportService;
@ -113,9 +118,14 @@ public class PlanAnualView extends VerticalLayout {
PlanAnualFilter planAnualFilter = new PlanAnualFilter(dataView);
planAnualFilter.setExcludeRealizado(true);
yearFilter.addValueChangeListener(event ->
planAnualFilter.setYear(event.getValue())
);
// Aplicar el valor actual del ComboBox al filtro al inicializar
if (yearFilter != null && yearFilter.getValue() != null) {
planAnualFilter.setYear(yearFilter.getValue());
}
if (yearFilter != null) {
yearFilter.addValueChangeListener(event -> planAnualFilter.setYear(event.getValue()));
}
HeaderRow headerRow = planAnualGrid.appendHeaderRow();
/*headerRow.getCell(planAnualGrid.getColumnByKey("smtColumnKey"))
@ -130,6 +140,9 @@ public class PlanAnualView extends VerticalLayout {
headerRow.getCell(planAnualGrid.getColumnByKey("mesplaneado"))
.setComponent(createFilterHeader("Mes Planeado", planAnualFilter::setMesPlaneado));
headerRow.getCell(planAnualGrid.getColumnByKey("encuesta"))
.setComponent(createFilterHeader("Encuesta", planAnualFilter::setEncuesta));
// MENU CONTEXTUAL DEL GRID
@ -228,6 +241,7 @@ public class PlanAnualView extends VerticalLayout {
this.setSizeFull();
add(filtrosLayout, uploadLayout);
add(gridLayout);
this.emailService = emailService;
}
private void setupHeader() {
@ -264,7 +278,6 @@ public class PlanAnualView extends VerticalLayout {
if ("PENDIENTE".equalsIgnoreCase(estado)) {
btn = new Button(new Icon(VaadinIcon.EDIT));
btn.setTooltipText("Realizar mantenimiento");
btn.getStyle().set("color", "#A02142");
btn.addClickListener(event -> {
int idPlananual = planAnual.getNumero();
@ -286,7 +299,6 @@ public class PlanAnualView extends VerticalLayout {
} else if ("REALIZADO".equalsIgnoreCase(estado)) {
btn = new Button(new Icon(VaadinIcon.EYE));
btn.setTooltipText("Ver detalles");
btn.getStyle().set("color", "#A02142");
btn.addClickListener(event -> {
int idPlananual = planAnual.getNumero();
@ -369,14 +381,8 @@ public class PlanAnualView extends VerticalLayout {
btnAddEquipo.setTooltipText("Agregar nuevo equipo");
btnEnviarEncuestas = new Button(VaadinIcon.ENVELOPE.create());
ConfirmDialog enviarEncConfirm = new ConfirmDialog();
enviarEncConfirm.setHeader("Enviar encuestas");
enviarEncConfirm.setText("¿Deseas enviar las encuestas de satisfacción?, Esto enviara la encuesta solo a los mantenimientos realizados");
enviarEncConfirm.setCancelable(true);
enviarEncConfirm.addCancelListener(e -> enviarEncConfirm.close());
enviarEncConfirm.setConfirmText("Enviar");
enviarEncConfirm.addConfirmListener(e -> {});
btnEnviarEncuestas.addClickListener(e -> enviarEncConfirm.open());
btnEnviarEncuestas.addClickListener(e -> showParametrosDialog());
btnEnviarEncuestas.setTooltipText("Enviar encuestas masivamente");
yearFilter = new ComboBox<>();
int currentYear = Year.now().getValue();
@ -384,12 +390,14 @@ public class PlanAnualView extends VerticalLayout {
.boxed().collect(Collectors.toList());
yearFilter.setItems(years);
yearFilter.setPlaceholder("Año");
yearFilter.setValue(currentYear);
yearFilter.setClearButtonVisible(true);
List<PlanAnual> todosLosPlanes = databaseService.getPlanAnual();
btnImprimirLayout = new HorizontalLayout(btnColumns, btnImprimirRpt, btnAddEquipo/*, btnEnviarEncuestas*/, yearFilter);
btnImprimirLayout = new HorizontalLayout(btnColumns, btnImprimirRpt, btnAddEquipo, btnEnviarEncuestas, yearFilter);
btnImprimirLayout.setAlignItems(Alignment.BASELINE);
HorizontalLayout columnSelectorLayout = new HorizontalLayout();
columnSelectorLayout.setAlignItems(Alignment.END);
@ -541,6 +549,11 @@ public class PlanAnualView extends VerticalLayout {
private String equipo;
private String departamento;
private String mesPlaneado;
private LocalDate fechaProgramada;
private LocalDate fechaRealizacion;
private String encuesta;
private String estado;
private String situacion;
private Integer year;
private boolean excludeRealizado = true;
@ -569,6 +582,11 @@ public class PlanAnualView extends VerticalLayout {
this.dataView.refreshAll();
}
public void setEncuesta(String encuesta) {
this.encuesta = encuesta;
this.dataView.refreshAll();
}
public void setYear(Integer year) {
this.year = year;
dataView.refreshAll();
@ -586,6 +604,7 @@ public class PlanAnualView extends VerticalLayout {
boolean matchesEquipo = matches(planAnual.getNomEquipo(), equipo);
boolean matchesDepartamento = matches(planAnual.getDepartamento(), departamento);
boolean matchesMesPlaneado = matches(planAnual.getMesplaneado(), mesPlaneado);
boolean matchesEncuesta = matches(planAnual.getEncuesta(), encuesta);
boolean matchesYear = true;
if (year != null) {
@ -608,7 +627,8 @@ public class PlanAnualView extends VerticalLayout {
&& matchesDepartamento
&& matchesMesPlaneado
&& matchesYear
&& matchesEstado;
&& matchesEstado
&& matchesEncuesta;
}
private boolean matches(String value, String serachTerm) {
@ -689,7 +709,7 @@ public class PlanAnualView extends VerticalLayout {
} catch (Exception ex) {
Notification.show("Error al genrar el reporte: " + ex.getMessage(), 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
ex.printStackTrace();
logger.error("Error al generar el reporte", ex);
}
});
@ -903,7 +923,7 @@ public class PlanAnualView extends VerticalLayout {
} catch (Exception ex) {
Notification.show("Error al generar el reporte: " + ex.getMessage(), 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
ex.printStackTrace();
logger.error("Error al generar el reporte", ex);
}
});
@ -1005,4 +1025,170 @@ public class PlanAnualView extends VerticalLayout {
dialog.getFooter().add(dialogFooter);
dialog.open();
}
private void showParametrosDialog() {
Dialog dialog = new Dialog();
dialog.setHeaderTitle("Ingresar periodo");
TextField txtMes = new TextField("Mes:");
txtMes.setPlaceholder("ENERO, FEBRERO, MARZO...");
txtMes.setClearButtonVisible(true);
TextField txtAnio = new TextField("Año");
txtAnio.setPlaceholder("Ej. 2025");
Button btnBuscar = new Button("Buscar", VaadinIcon.SEARCH.create(), e -> {
String mes = txtMes.getValue();
String anioTexto = txtAnio.getValue();
if (mes.isEmpty() || anioTexto.isEmpty()) {
Notification.show("Mes y año son requeridos.", 3000, Notification.Position.MIDDLE);
return;
}
int anio;
try {
anio = Integer.parseInt(anioTexto);
} catch (NumberFormatException ex) {
Notification.show("El año debe ser numérico.", 3000, Notification.Position.MIDDLE);
return;
}
dialog.close();
showEncuestasDialog(mes, anio);
});
btnBuscar.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
Button btnCancelar = new Button("Cancelar", VaadinIcon.CLOSE_CIRCLE.create(), e -> dialog.close());
HorizontalLayout actions = new HorizontalLayout(btnBuscar, btnCancelar);
VerticalLayout layout = new VerticalLayout(txtMes, txtAnio, actions);
layout.setPadding(false);
layout.setSpacing(true);
dialog.add(layout);
dialog.open();
}
private void showEncuestasDialog(String mes, int anio) {
Dialog dialog = new Dialog();
dialog.setWidth("95%");
dialog.setHeight("90%");
dialog.setHeaderTitle("Encuestas pendientes por enviar...");
Grid<MantenimientosSinEncuesta> grid = new Grid<>(MantenimientosSinEncuesta.class, false);
grid.addColumn(MantenimientosSinEncuesta::getMantenimientoId)
.setHeader("ID")
.setAutoWidth(true);
grid.addColumn(item -> item.getFecha().format(DateTimeFormatter.ofPattern("dd/MM/yyyy")))
.setHeader("Fecha")
.setAutoWidth(true);
grid.addColumn(MantenimientosSinEncuesta::getPeriodo)
.setHeader("Periodo")
.setAutoWidth(true);
grid.addColumn(createStatusRender())
.setHeader("Encuesta");
grid.addColumn(MantenimientosSinEncuesta::getDepartamento)
.setHeader("Departamento");
grid.addColumn(MantenimientosSinEncuesta::getNomUsuario)
.setHeader("Usuario")
.setAutoWidth(true);
grid.addColumn(MantenimientosSinEncuesta::getEmail)
.setHeader("Correo")
.setAutoWidth(true);
grid.addComponentColumn(item -> {
Button btnEnviar = new Button(
"Enviar",
LineAwesomeIcon.ENVELOPE_SOLID.create()
);
btnEnviar.addThemeVariants(ButtonVariant.LUMO_PRIMARY,
ButtonVariant.LUMO_SUCCESS);
btnEnviar.addClickListener(e -> {
enviarEncuestaDesdeGrid(item);
btnEnviar.setEnabled(false);
});
return btnEnviar;
}).setHeader("Accion")
.setFrozen(true);
List<MantenimientosSinEncuesta> lista = databaseService.getEncuestPendientes(mes, anio);
grid.setItems(lista);
grid.addThemeVariants(GridVariant.LUMO_WRAP_CELL_CONTENT);
dialog.add(grid);
dialog.getFooter().add(new Button("Cerrar", LineAwesomeIcon.TIMES_SOLID.create(), e -> dialog.close()));
dialog.open();
}
private void enviarEncuestaDesdeGrid(MantenimientosSinEncuesta item) {
if (item.getEmail() == null || item.getEmail().isBlank()) {
Notification.show("El usuario no tiene correo", 3000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
return;
}
int mantenimientoId = item.getMantenimientoId();
String token = databaseService.crearTokenEncuesta(mantenimientoId);
String linkEncuesta = baseUrl + "/encuesta?token=" + token;
String cuerpo = """
<html>
<body>
<a href="%s">
<img src="cid:image_id"/>
</a>
</body>
</html>
""".formatted(linkEncuesta);
String asunto = "Encuesta de satisfacción - Mantenimiento #" + mantenimientoId;
String imagePath = "META-INF/resources/images/imgCorreo/imgEncuesta.png";
emailService.enviarCorreo(
item.getEmail(),
asunto,
cuerpo,
imagePath
);
Notification.show("Encuesta enviada correctamente", 3000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
}
private ComponentRenderer<Span, MantenimientosSinEncuesta> createStatusRender() {
return new ComponentRenderer<>(encuesta -> {
Span span = new Span(encuesta.getEncuesta());
switch (encuesta.getEncuesta().toUpperCase()) {
case "N":
String theme1 = String.format("badge %s", "error");
span.getElement().setAttribute("theme", theme1);
break;
case "S":
String theme2 = String.format("badge %s", "success");
span.getElement().setAttribute("theme", theme2);
break;
default:
String theme3 = String.format("badge %s", "");
span.getElement().setAttribute("theme", theme3);
}
return span;
});
}
}

+ 200
- 0
src/main/java/mx/gob/jumapacelaya/ui/ResultEncuestasView.java View File

@ -0,0 +1,200 @@
package mx.gob.jumapacelaya.ui;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.dashboard.Dashboard;
import com.vaadin.flow.component.dashboard.DashboardSection;
import com.vaadin.flow.component.dashboard.DashboardWidget;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.SvgIcon;
import com.vaadin.flow.component.icon.VaadinIcon;
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 jakarta.annotation.security.PermitAll;
import mx.gob.jumapacelaya.models.encuestas.ConteoEncuestas;
import mx.gob.jumapacelaya.models.encuestas.ConteoRespuestas;
import mx.gob.jumapacelaya.services.DatabaseService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.lineawesome.LineAwesomeIcon;
import java.util.List;
@PermitAll
@PageTitle("Resultados de Encuestas")
@Route(value = "resultados-encuestas", layout = MainLayout.class)
public class ResultEncuestasView extends VerticalLayout {
private static final Logger logger = LoggerFactory.getLogger(ResultEncuestasView.class);
private final DatabaseService databaseService;
private DashboardWidget totalEncu;
private DashboardWidget totalResp;
private DashboardWidget porcentajeResp;
private DashboardWidget pregunta1;
private DashboardWidget pregunta2;
private DashboardWidget pregunta3;
private DashboardWidget pregunta4;
private DashboardWidget pregunta5;
private DashboardWidget pregunta6;
public ResultEncuestasView(DatabaseService databaseService) {
this.databaseService = databaseService;
createToolbar();
createDashboard();
}
public HorizontalLayout createToolbar() {
Button btnExport = new Button("Exportar a PDF", LineAwesomeIcon.FILE_PDF.create());
btnExport.addThemeVariants(ButtonVariant.LUMO_SMALL);
HorizontalLayout toolbar = new HorizontalLayout(btnExport);
toolbar.setAlignItems(Alignment.BASELINE);
toolbar.setWidthFull();
add(toolbar);
return toolbar;
}
public Dashboard createDashboard() {
Dashboard dashboard = new Dashboard();
dashboard.setMinimumColumnWidth("150px");
dashboard.setMaximumColumnCount(3);
// Secciónes del dashboard
DashboardSection generalSection = dashboard.addSection("General");
DashboardSection preguntasSection = dashboard.addSection("Preguntas");
// Widgets para la sección General
totalEncu = new DashboardWidget("Total Encuestas");
totalResp = new DashboardWidget("Total Respuestas");
porcentajeResp = new DashboardWidget("Porcentaje de Respuestas");
generalSection.add(totalEncu);
generalSection.add(totalResp);
generalSection.add(porcentajeResp);
// Widget para la sección Preguntas
pregunta1 = new DashboardWidget("Pregunta 1");
pregunta2 = new DashboardWidget("Pregunta 2");
pregunta3 = new DashboardWidget("Pregunta 3");
pregunta4 = new DashboardWidget("Pregunta 4");
pregunta5 = new DashboardWidget("Pregunta 5");
pregunta6 = new DashboardWidget("Pregunta 6");
preguntasSection.add(pregunta1);
preguntasSection.add(pregunta2);
preguntasSection.add(pregunta3);
preguntasSection.add(pregunta4);
preguntasSection.add(pregunta5);
preguntasSection.add(pregunta6);
mostrarTotales();
mostrarPreguntas();
add(dashboard);
return dashboard;
}
private Div createDiv(String valor, LineAwesomeIcon icon, String color) {
Div content = new Div();
content.setClassName("dashboard-widget-content");
content.getStyle()
.set("display", "flex")
.set("align-items", "center")
.set("justify-content", "center")
.set("gap", "15px");
SvgIcon vIcon = icon.create();
vIcon.getStyle().set("color", color);
vIcon.setSize("40px");
Span text = new Span(valor);
text.getStyle().set("font-size", "2.2rem");
text.getStyle().set("font-weight", "800");
content.add(vIcon, text);
return content;
}
private Div createPreguntaContent(ConteoRespuestas data) {
Div container = new Div();
container.setClassName("pregunta-widget-container");
container.getStyle().set("padding", "10px");
Span txtPregunta = new Span(data.getPregunta());
txtPregunta.getStyle().set("font-size", "0.9em")
.set("display", "block")
.set("margin-bottom", "15px")
.set("height", "45px")
.set("overflow", "hidden");
HorizontalLayout metrics = new HorizontalLayout();
metrics.setJustifyContentMode(JustifyContentMode.BETWEEN);
metrics.setWidthFull();
metrics.add(
createStat("Si", String.valueOf(data.getTotalSi()), "#27ae60"),
createStat("No", String.valueOf(data.getTotalNo()), "#e74c3c"),
createStat("%", String.format("%.0f", data.getPorcentaje()), "#BC955B")
);
container.add(txtPregunta, metrics);
return container;
}
private VerticalLayout createStat(String label, String value, String color) {
Span l = new Span(label);
l.getStyle().set("font-size", "0.7em").set("color", "gray");
Span v = new Span(value);
v.getStyle().set("font-weight", "bold").set("color", color);
VerticalLayout vl = new VerticalLayout(l,v);
vl.setAlignItems(Alignment.CENTER);
vl.setSpacing(false);
vl.setPadding(false);
return vl;
}
public void mostrarTotales() {
List<ConteoEncuestas> datos = databaseService.getTotalEncuestas();
if (!datos.isEmpty()) {
ConteoEncuestas totales = datos.get(0);
totalEncu.setContent(createDiv(
String.valueOf(totales.getTotalEnviadas()),
LineAwesomeIcon.CLIPBOARD_LIST_SOLID, "#BC955B"
));
totalResp.setContent(createDiv(
String.valueOf(totales.getTotalRespondidas()),
LineAwesomeIcon.CHECK_CIRCLE, "#BC955B"
));
porcentajeResp.setContent(createDiv(
String.valueOf(totales.getPorcentajeRespondidas()),
LineAwesomeIcon.PERCENT_SOLID, "#BC955B"
));
}
}
public void mostrarPreguntas() {
List<ConteoRespuestas> lista = databaseService.getTotalRespuestas();
DashboardWidget[] widgets = {pregunta1,pregunta2,pregunta3,pregunta4,pregunta5,pregunta6};
for (int i = 0; i < lista.size() && i < widgets.length; i++) {
ConteoRespuestas data = lista.get(i);
DashboardWidget widget = widgets[i];
widget.setTitle("Pregunta #" + data.getPreguntaId());
widget.setContent(createPreguntaContent(data));
}
}
}

+ 0
- 3
src/main/java/mx/gob/jumapacelaya/ui/login/LoginView.java View File

@ -22,9 +22,6 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver {
private static final Logger log = LoggerFactory.getLogger(LoginView.class);
private final LoginForm login = new LoginForm();
// Usuario local para demostracion
private final String localUser = "admin";
private final String localPassword = "admin";
public LoginView(){


BIN
src/main/resources/META-INF/resources/images/LOGO_admon2.png View File

Before After
Width: 597  |  Height: 569  |  Size: 84 KiB

BIN
src/main/resources/META-INF/resources/images/imgCorreo/imgEncuesta.png View File

Before After
Width: 1024  |  Height: 1536  |  Size: 3.2 MiB Width: 1024  |  Height: 1536  |  Size: 3.2 MiB

+ 5
- 2
src/main/resources/application.properties View File

@ -1,6 +1,7 @@
server.port=${PORT:8080}
logging.level.org.atmosphere = warn
spring.profiles.active=dev
server.servlet.session.cookie.secure=true
# Launch the default browser when starting the application in development mode
vaadin.launch-browser=true
@ -12,13 +13,15 @@ spring.jpa.defer-datasource-initialization = true
#Configuracion LDAP
spring.ldap.urls=ldap://172.1.0.1:389
spring.ldap.urls=ldap://172.16.0.1:389
spring.ldap.url=ldap://172.16.0.1
spring.ldap.domain=JUMAPACELAYA.GOB.MX
spring.ldap.base=DC=JUMAPACELAYA,DC=GOB,DC=MX
spring.ldap.username=administrator
spring.ldap.password=Dr3na$134%4guA
###################PRODUCTIVO####################
redmine.url=https://proyman.jumapacelaya.gob.mx/
redmine.url=https://proyman.jumapacelaya.gob.mx
redmine.api_key=69be2a5df9bacce02722f566fdf0731d728a1b86


+ 1
- 1
tsconfig.json View File

@ -10,7 +10,7 @@
"jsx": "react-jsx",
"inlineSources": true,
"module": "esNext",
"target": "es2020",
"target": "es2022",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,


Loading…
Cancel
Save