90 Commits

Author SHA1 Message Date
  mramirezg 2a3838902f Solucion de historias no relacionadas y fusion de camnios en la rama main 1 week ago
  mramirezg 63c21f2efb Se arreglo un bug que hacia que dejara de funcionar el filtrar por año 2 weeks ago
  mramirezg 9da4a05c55 Se agrego una nueva validacion para los mantenimientos que se realizaron en los primeros meses del año y que se registraron en papel para que ya no se muestren en el grid 2 weeks ago
  mramirezg 6e68caeb80 Se agregaron validaciones para que las encuestas se puedan responder solo una vez por medio de tokens en el link de la encuesta asi como tambien tiempo de vida del link 2 weeks ago
  mramirezg bc50fac492 Se volvio al mecanismo anterior de enviar encuestas por mantenimiento individual por el momento y se cambio la imagen que llega en el correo cuando un mantenimiento es realizado 2 weeks ago
  mramirezg 5104833d39 Cambio de estilo a los botones y se quito el boton de enviar encuesta por cada mantenimiento, ahora se agrego uno general que enviara todos los correos al mismo tiempo 1 month ago
  mramirezg 3cba9bcfef Cambio en algunos colores del grid 2 months ago
  mramirezg e87da067ee se agrego la funcion para enviar la encuesta de satisfaccion por correo electronico al usuario 2 months ago
  mramirezg f9ee7092ad Ya se pueden filtrat por rango de fechas los tickets 4 months ago
  mramirezg 95aa5228e9 Se agrego la funcionalidad para poder exportar los tickets a una hoja de calculo 4 months ago
  mramirezg 5a77ec8afa Se arreglo el error de que no mostraba las horas correctamente en las columnas de fechas, y tambien ya funiciona bien el checkBox para filtrar por tickets cerrados o abiertos 4 months ago
  mramirezg 7d4084c23e Se arreglo el error al dejar las firmas en blanco durante la edicion de estas 4 months ago
  mramirezg 3b48c32aa9 Se agrego el modulo de mantenimientos correctivos, ya se pueden dar de alta los manteniminetos correctivos -estos no estan ligados a ningun plan anual de mantenimento- asi como tambien se agrego un dialogo que muestra la lista de los mantenimientos correctivos que han realizado 5 months ago
  mramirezg 96f1fbac72 Se agrego la capacidad de poser quitar mantenimientos que corresponden a equipos que ya no se encuentran o que se cambiaron a otra area y se le cambio el nombre 6 months ago
  mramirezg c8308ef18a Se corrigio un error con la clase downloadLink que iba acumulando links con los reportes anteriores y se confundian, tambien se cambio el reporte de listado ahora mostrando incluso los que aun no se han hecho, dentro y fuera de tiempo, asi como el porcentaje de efectividad del periodo 6 months ago
  mramirezg 7ad9ce82bb Se agrego otro reporte de listado de mantenimientos realizados durante un periodo, asi como tambien el total, el numero de realizados fuera de tiempo y el nuemero de realizados a tiempo. Tambien se agrego la funcionalidad para que el usuario pueda dar de alta nuevas computadoras al plan anual 6 months ago
  mramirezg 28747c20b1 Se agrego el reporte para buscar por rango de fechas 6 months ago
  mramirezg a3e93f85d7 Se agrego la posibilidad de filtrar las columnas del plan anual seleccionando las que solamente se deseen ver y se re organizaron los mantenimientos planeados por fecha 6 months ago
  mramirezg 377fdc1d5f Se agrego una nueva carateristica que si el mantenimiento se esta realizando en un mes distinto del que estaba planeado se le pedira al usuario una justificacion de por que se esta haciendo a destiempo y esta se guardara en una nueva columna en la tabla de mantenimientos, ademas en el grid principal del plan anual se fijaron las dos primeras columnas para facilitar la navegacion al formulario de mantenimiento, los radio buttons se cambiaron por botones normales para filtrar por pendientes y realizados 6 months ago
  mramirezg ec6f42542d Se cambio la base de datos completa y tambien se agrego el reporte imprimible del mantenimiento 6 months ago
  mramirezg b2d8f8852f Se añadio una feature que compara las dos fechas tanto la de programacion como la de realizacion y dice si el mantenimiento fue realizado a tiempo o fuera de tiempo, ademas ya no se permite actualizar la fecha de programacion 6 months ago
  mramirezg 3c3a5554d2 Feature con reporte de mantenimiento 6 months ago
  mramirezg 95b4a457ea Se agrego un boton especifico para editar las firmas y modificacion al query que guarda la informacion del mantenimiento con firmas 7 months ago
  mramirezg cfbed03ff7 Se puso una imagen fija en el lugar donde va la firma de la gerencia, en lo que se tiene una imagen de la firma real, ya no se pueden modificar las firmas una vez guadado el mantenimiento 7 months ago
  mramirezg 85750ab155 Ya se pueden agregar o midificar las firmas depues de que se guardo el reporte 7 months ago
  mramirezg 9effe65afa Se agrego la fucnion para actualizar tambien las actualizaciones de seguridad 7 months ago
  mramirezg fb8f239a19 Se modifico las conexiones a BD haciendola mas flexible tanto para usar la base de datos de desarrollo como para la base de datos productiva 7 months ago
  mramirezg 82788329e2 Ya se puede editar los campos de firmas del usuario y del smt la de la gerencia no porque esa estara fija siempre, pero las otras dos ya se pueden modificar en modo de edicion 7 months ago
  mramirezg 7b5a395ad6 Ya se muestran las firmas como imagenes, falta poder hacer una nueva firma en modo edicion 7 months ago
  mramirezg fee71d0de0 Se agrego otro grid editable donde se muestran las actualizaciones de seguridad que se le hicieron al equipo durante el mantenimiento 7 months ago
  mramirezg 9231de1632 Se corrigio un error que ya no permitia ver por el estado del mantenimiento al seleccionar un radio button 7 months ago
  mramirezg cc99963b92 Se cambio la organizacion de las columnas del plan anual y tambien se agregaron mas filtros para ver si estan realizados o pendientes y para buscar por departamento o equipo 7 months ago
  mramirezg 652f95c3aa se agrego la bitacora para cuando se haga alguna modificacion a los registros 8 months ago
  mramirezg ae15dc548e ya se actualizan los registros en la base de datos al confirmar la actualizacion por medio de un boton 8 months ago
  mramirezg 4473e15de9 Ahora ya permite editar los campos de texto, asi como tambien los registros del grid dando clic en el boton de editar 8 months ago
  mramirezg 5c4890d400 Se agrego la vista para que se pueda ver informacion sobre el mantenimiento realizado ya se muestran los datos, falta agregar funcionalidad a los botones 8 months ago
  mramirezg b7fa3bffda Se mejoro el procedimientos de validacion de campos en el formulario de mantenimiento y en la insercion a la base de datos 8 months ago
  mramirezg 6cbc9e644b Correcion de configuracion para la nueva Base de Datos Oracle asi como correcion en algunos querys 9 months ago
  mramirezg 8a0af1abda Correcion de errores al enviar el correo y modificacioones en la UI y UX 11 months ago
  mramirezg 3922a36226 correcion en el los colores del estado del ticket y tambien se corrigio un error que no filtraba bien los tipos de ticket Actividad y Mantenimiento correctivo 11 months ago
  mramirezg 4c5136f9b8 se agrego la validacion para que muestre el uload para subir archivos o el grid en base a si hay o no registros en la tabla 1 year ago
  mramirezg 7bf8dc0137 Se agrego un filtro para que el grid del plan anual solo mustre los mantenimientos con estado pendiente para que asi sea mas facil identificar cuales mantenimientos faltan 1 year ago
  mramirezg ff42ceab96 Se corriegieron detalles visuales en la vista de Actividades Diarias asi como uno que otro error 1 year ago
  mramirezg 3eb5de8df0 Se corriegio en tamaño en las colunas de departamento y equipo para que se mostraran los nombres completos ademas tambien se cambio la cadena Base64 de firmas vacias ya que por alguna razon las firmas vacias empezaron a tener otra cadena Base64 1 year ago
  mramirezg 65e3e03cb1 Se arreglo el error de la imagen que llega por correo que llega duplicada y tambien se arreglo el error el cual hacia que si se dejaba un campo de firma en blanco de todos modos insertaba algo en la tabla, ahora ya no; si se queda en blanco el campo de firma en la base de datos se queda nulo el registro 1 year ago
  mramirezg ee3d58d722 Se agrego un nuevo campo de busqueda en la columna del SMT para poder buscar por el nombre de este y tambien se agrego la funcionalidad para que en el campo del nombre del equipo lo obtenga directamente desde el plan anual y no se tenga que escribir manualmente 1 year ago
  mramirezg 58af4c2d31 Ya se envian los correos electronicos cuando se selecciona a un usuario en el ComboBox 1 year ago
  mramirezg 33c8e19d6e se añadio la conexion a la base de datos de finanzas para de ahi obtener los departamentos y los usuarios 1 year ago
  mramirezg cf602598d6 Se cambiaron los estilos con los nuevos colores de la nueva admnistracion 1 year ago
  mramirezg fb056b99bd se añadio la columna de estado en el grid asi como tambien la funcionalidad de que cuando se guarde el mantenimiento se actualice la fecha de realizacion con esa fecha 1 year ago
  mramirezg dfc4c6becb El comboBox de los tipos de hardware tambien ya muestran los nombres y no los id's de los tipos, tambien se cambiaron los temas de algunas notificaciones 1 year ago
  mramirezg 90e3c13c5a Los combos ya muestran los nombres en lugar de sus id's pero a la BD se insertan sus id´s y tambien se agrego otro campo de texto para el nombre de la computadora 1 year ago
  mramirezg 6b2d18c965 ya se muestra lo que hay en la tabla de mttoprogramados en el grid de la vista de plan anual 1 year ago
  mramirezg 151c247ab3 Se agrego el componente de upload a la vista y tambien la funcion para que inserte el archivo en la tabla desde el upload 1 year ago
  mramirezg 890638a141 Se arreglaron errores en el metodo para insertar los archivos de excel con los formatos de las columnas 1 year ago
  mramirezg 33d1811cde El metodo para insertar los archivos ya funciona si se le especifica la ruta del archivo 1 year ago
  mramirezg 638d593f49 ya se insertan todos los datos del formulario en sus respectivas tablas y ademas se agregaron validaciones y notificaciones de error en caso de que falle alguna insercion 1 year ago
  mramirezg 83306d0c21 ya se insertan los tiposd de hardware con sus respectivos campos de numero de serie, modelo y placa 1 year ago
  mramirezg 560dac1346 Se agrego el boton para guardar en la vista de mantenimientos 1 year ago
  mramirezg 437e554431 se agregaron validaciones para que cuando se seleccione a algun usuario en el campo de firma correspondiente se ponga su nombre completo del usuario 1 year ago
  mramirezg c286d95310 Se agregaron los campos para poder firmar los mantenimientos cuando han sido realizados 1 year ago
  mramirezg 69c98f137c Se agregaron validaciones en la vista de actividades diarias si es de tipo mantenimiento se redirecciona a la vista de mantenimientos y si es de tipo actividad cambia el estado a terminada 1 year ago
  Marco Antonio Ramírez Galván 3bdf04fad3 Ya se filtran los tickets en la vista de actividades diarias se muestran solo los que son de tipo Actividad y Mantenimiento 1 year ago
  Marco Antonio Ramírez Galván 96b9a4bed5 Se agrego una columna con un boton de realizar 1 year ago
  Marco Antonio Ramírez Galván df3a6a68f9 Se agrego la columna del tipo de ticket que es y tambien se agrego una columna con la fecha en que se cerro el ticket 1 year ago
  Marco Antonio Ramírez Galván d97728efea Se agrego la vista de el listado de actividades que lo obtiene directamente desde ProyMan y tambien se agrego de que la fecha se establezca automaticamente cuando le dan clic al boton de realizar 1 year ago
  Marco Antonio Ramírez Galván 7fbbca892d Se agrego datos de mustra al grid de plan anual y alguna que otra funcion 1 year ago
  dbaylonv 8742e4ba65 Formulario del MTTO CORRECTIVO en la pantalla de Mantenimiento con validaciones 1 year ago
  dbaylonv b9fc532eb4 Merge branch 'master' of http://gitea.jumapacelaya.gob.mx:3000/dbaylonv/Sistema_mantenimiento_correctivo_y_preventivo 1 year ago
  Marco Antonio Ramírez Galván fe04ecac01 Se añadio el grid de la vista de mantenimiento anual 1 year ago
  Marco Antonio Ramírez Galván e2bf084581 Se agregaron los componentes faltantes en la pantalla de mantenimientos y tambien se hicieron responsivos para los distintos tamaños de pantallas 1 year ago
  dbaylonv ad020b2e0b Merge branch 'master' of http://gitea.jumapacelaya.gob.mx:3000/dbaylonv/Sistema_mantenimiento_correctivo_y_preventivo 1 year ago
  Marco Antonio Ramírez Galván 4794f781a3 Se agregaron dos metodos nuevos en DatabaseService para obtener a los usuarios de las tablas de usuariosfinan y otra para obtener los departamentos de departamentosfinan asi como tambien la funcionalidad de que se mustren en la vista de mantenimientos, tambien se agregaron otros componentes visuales a la vista como el checkbox para selecionar el tipo de hardware y los campos de texto para introducir su numero de serie, modelo y placa y algunas validaciones entre otras cosas mas 1 year ago
  dbaylonv 05282ddbab Merge branch 'master' of http://gitea.jumapacelaya.gob.mx:3000/dbaylonv/Sistema_mantenimiento_correctivo_y_preventivo 1 year ago
  dbaylonv 669180cd65 boton 1 year ago
  Marco Antonio Ramírez Galván 5ac47c5f53 Se movieron todos los estilos a clases de css en vez de seguir estando en el estilo en la propia linea del codigo 1 year ago
  Marco Antonio Ramírez Galván 4092e9c008 se actualizo el componente de fecha para que al seleccionar el tipo preventivo se establesca en la fecha del sistema 1 year ago
  dbaylonv 6cfbeb7843 Corecciones de Jacqueline 1 year ago
  dbaylonv b9450b7c83 Modificaciones del Marco 1 year ago
  dbaylonv 6bbb654105 Depuracion de ActDiaria 1 year ago
  Marco Antonio Ramírez Galván a3438a30ec Se agrego diseño a todal las paginas con los colores oficiales y logotipos y tambien se agrego la nueva vista del Plan Anual 1 year ago
  Marco Antonio Ramírez Galván 497ca91d04 Se afrego la conexion a la base de datos asi como tambien el combo box del tipo de mantenimiento ya trae la infomracion directamente desde la db y en base al elemento que se seleccione muestra su nomenclatura 1 year ago
  dbaylonv b73e2c71fe Pantalla ActDiriaria terminado el diseño 1 year ago
  dbaylonv 0550db3d2e Pantalla con los Grid para visualizar informacion 1 year ago
  dbaylonv 6571336821 Pantalla ActDiariaView en las rutas y se comenzo a desarrollar 1 year ago
  Marco Antonio Ramírez Galván 5868215ca5 Se agrego configuracion para la conexion a la base de datos asi como tambien funcionalidades locales a la vista de mantenimientos 1 year ago
  dbaylonv 448433af80 Reestructura del proyecto 1 year ago
  Marco Antonio Ramírez Galván 5e7892e2f9 Se agrego el diseño del formulario sin funcionalidad de la vista de mantenimientos 1 year ago
  Marco Antonio Ramírez Galván 75271e2cb3 Se hicieron cambios y ajustes visuales a la interfaz de usuario 1 year ago
  dbaylonv 262473ec67 Login con validacion y LDAP 1 year ago
19 changed files with 1403 additions and 356 deletions
Split View
  1. BIN
      src/main/bundles/dev.bundle
  2. +24
    -2
      src/main/frontend/themes/sistema-mantenimiento/styles.css
  3. +169
    -70
      src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java
  4. +12
    -1
      src/main/java/mx/gob/jumapacelaya/models/PlanAnual.java
  5. +87
    -32
      src/main/java/mx/gob/jumapacelaya/models/Ticket.java
  6. +48
    -0
      src/main/java/mx/gob/jumapacelaya/models/encuestas/Pregunta.java
  7. +66
    -0
      src/main/java/mx/gob/jumapacelaya/models/encuestas/Respuesta.java
  8. +168
    -24
      src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java
  9. +0
    -1
      src/main/java/mx/gob/jumapacelaya/services/EmailService.java
  10. +319
    -59
      src/main/java/mx/gob/jumapacelaya/ui/ActDiariaView.java
  11. +206
    -143
      src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java
  12. +216
    -0
      src/main/java/mx/gob/jumapacelaya/ui/EncuestaView.java
  13. +1
    -1
      src/main/java/mx/gob/jumapacelaya/ui/MantenimientoView.java
  14. +79
    -21
      src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java
  15. BIN
      src/main/resources/META-INF/resources/images/LOGO_1024X768.jpg
  16. BIN
      src/main/resources/META-INF/resources/images/imgCorreo/MttoRealizado.png
  17. BIN
      src/main/resources/META-INF/resources/images/imgCorreo/imgEncuesta.png
  18. +4
    -1
      src/main/resources/application-dev.properties
  19. +4
    -1
      src/main/resources/application-prod.properties

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


+ 24
- 2
src/main/frontend/themes/sistema-mantenimiento/styles.css View File

@ -115,6 +115,29 @@ 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%;
@ -192,8 +215,7 @@ vaadin-side-nav-item::part(content) {
/* (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) (*)(*) */
/*Estilos especificos para el Grid */
vaadin-grid::part(selected-row-cell) {
background-color: #ddc9a3;
opacity: 100%;
background-color: rgba(221, 201, 163, 0.7);
}
vaadin-grid::part(selected-row) {


+ 169
- 70
src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java View File

@ -3,15 +3,19 @@ package mx.gob.jumapacelaya.api;
import com.google.gson.*;
import mx.gob.jumapacelaya.models.RedmineUser;
import mx.gob.jumapacelaya.models.Ticket;
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;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
@ -21,11 +25,11 @@ import java.util.Map;
@Component
public class RedmineClient {
private static final Logger log = LoggerFactory.getLogger(RedmineClient.class);
private static final int PAGE_SIZE = 25;
//private static final int PAGE_SIZE = 25;
static String REDMINE_URL;
static String API_KEY;
public static final Gson GSON = new Gson();
public RedmineClient(@Value("${redmine.url}") String redmineUrl, @Value("${redmine.api_key}") String apiKey) {
REDMINE_URL = redmineUrl;
@ -34,47 +38,92 @@ public class RedmineClient {
}
//AQUI OBTENGO LOS TICKETS DESDE REDMINE
public List<Ticket> getTickets(RedmineUser user, boolean includeClosed) {
public List<Ticket> getTickets(RedmineUser user, boolean includeClosed,
int offset, int limit,
LocalDate fechaDesde,
LocalDate fechaHasta) {
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";
// Filtrar tickets abiertos o cerrados
String statusFilter = includeClosed ? "&status_id=open" : "&status_id=closed";
StringBuilder url = new StringBuilder(REDMINE_URL + "/issues.json?key=" + user.getKey()
+ statusFilter + "&offset=" + offset + "&limit=" + limit);
while (true) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey() + statusFilter + "&offset=" + offset))
.header("Content-Type", "application/json")
.build();
// 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));
}
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;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url.toString()))
.header("Content-Type", "application/json")
.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
tickets.addAll(parseTickets(response.body()));
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Total tickets obtenidos: " + tickets.size());
return tickets;
}
public int getTotalTickets(RedmineUser user,
boolean includeClosed,
LocalDate fechaDesde,
LocalDate fechaHasta) {
HttpClient client = HttpClient.newHttpClient();
// Filtrar tickets abiertos o cerrados
String statusFilter = includeClosed ? "&status_id=open" : "&status_id=closed";
StringBuilder url = new StringBuilder(REDMINE_URL + "/issues.json?key=" + user.getKey()
+ statusFilter + "&limit=1");
// 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));
}
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url.toString()))
.header("Content-Type", "application/json")
.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
JsonObject jsonObject = JsonParser.parseString(response.body()).getAsJsonObject();
return jsonObject.get("total_count").getAsInt();
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
//AQUI OBTENGO LOS TICKETS DESDE REDMINE
public List<Ticket> getTicketsAuthor(RedmineUser user, boolean includeClosed) {
/*public List<Ticket> getTicketsAuthor(RedmineUser user, boolean includeClosed) {
List<Ticket> tickets = new ArrayList<>();
HttpClient client = HttpClient.newHttpClient();
int offset = 0;
@ -110,7 +159,7 @@ public class RedmineClient {
}
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
@ -123,16 +172,13 @@ public class RedmineClient {
for (JsonElement issueElement : issues) {
JsonObject issue = issueElement.getAsJsonObject();
// Verifica y obtiene el ID
// ID, subject, descripción
int id = issue.has("id") && !issue.get("id").isJsonNull() ? issue.get("id").getAsInt() : 0;
// Verifica y obtiene el subject
String subject = issue.has("subject") && !issue.get("subject").isJsonNull() ? issue.get("subject").getAsString() : "";
// Verifica y obtiene la descripción
String description = issue.has("description") && !issue.get("description").isJsonNull() ? issue.get("description").getAsString() : "";
Double estimatedHrs = issue.has("estimated_hours") && !issue.get("estimated_hours").isJsonNull() ? issue.get("estimated_hours").getAsDouble() : 0.0;
// Verifica y obtiene el status
// Status
String status = "Unknown";
if (issue.has("status") && !issue.get("status").isJsonNull()) {
JsonObject statusObject = issue.getAsJsonObject("status");
@ -141,34 +187,21 @@ public class RedmineClient {
}
}
// Verifica y obtiene la fecha de creación
String dateString = issue.has("created_on") && !issue.get("created_on").isJsonNull() ? issue.get("created_on").getAsString() : "";
LocalDate dateCreate = null;
if (!dateString.isEmpty()) {
try {
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
dateCreate = LocalDate.parse(dateString, formatter);
} catch (DateTimeParseException e) {
System.err.println("Error al parsear la fecha: " + dateString);
e.printStackTrace();
}
}
// Verifica y obtiene la fecha de cierre
String closeDateString = issue.has("closed_on") && !issue.get("closed_on").isJsonNull() ? issue.get("closed_on").getAsString() : "";
LocalDate dateClose = null;
if (!closeDateString.isEmpty()) {
try {
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
dateClose = LocalDate.parse(closeDateString, formatter);
} catch (DateTimeParseException e) {
System.err.println("Error al parsear la fecha de cierre: " + closeDateString);
e.printStackTrace();
// Parse fechas correctamente
LocalDateTime dateCreate = parseDateTime(issue, "created_on");
LocalDateTime dateUpdate = parseDateTime(issue, "updated_on");
LocalDateTime dateClose = parseDateTime(issue, "closed_on");
// Autor
Ticket.User autor = null;
if (issue.has("author") && !issue.get("author").isJsonNull()) {
JsonObject authorObj = issue.getAsJsonObject("author");
if (authorObj.has("name") && !authorObj.get("name").isJsonNull()) {
autor = new Ticket.User(authorObj.get("name").getAsString());
}
}
//Verifica y obtiene el ID del tipo de ticket
// Tracker ID
Integer trackerId = null;
if (issue.has("tracker") && !issue.get("tracker").isJsonNull()) {
JsonObject trackerObject = issue.getAsJsonObject("tracker");
@ -177,12 +210,26 @@ public class RedmineClient {
}
}
// Agrega el ticket a la lista
tickets.add(new Ticket(id, subject, description, status,
dateCreate != null ? dateCreate.toString() : "",
dateClose != null ? dateClose.toString() : "",
trackerId, "Tipo Desconocido"));
// Crear ticket
Ticket ticketObj = new Ticket(
id,
subject,
description,
status,
dateCreate != null ? dateCreate.toString() : "",
dateClose != null ? dateClose.toString() : "",
dateUpdate != null ? dateUpdate.toString() : "",
autor,
trackerId,
"Tipo Desconocido",
estimatedHrs
);
// Log para verificar JSON de ticket
//System.out.println(GSON.toJson(ticketObj));
tickets.add(ticketObj);
}
} else {
System.out.println("La respuesta JSON no contiene la clave 'issues'");
@ -194,6 +241,7 @@ public class RedmineClient {
return tickets;
}
public RedmineUser getMyAccount(String username) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/my/account.json"))
@ -271,7 +319,7 @@ public class RedmineClient {
public RedmineUser getUserByUsername(String username) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/users.json?name=" + username ))
.uri(URI.create(REDMINE_URL + "/users.json?name=" + username))
.header("Content-Type", "application/json")
.header("X-Redmine-API-Key", API_KEY)
.build();
@ -334,4 +382,55 @@ public class RedmineClient {
System.err.println(response.body());
}
}
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(LocalDateTime.class,
(JsonSerializer<LocalDateTime>) (src, typeOfSrc, context) ->
new JsonPrimitive(src.toString()))
.registerTypeAdapter(LocalDateTime.class,
(JsonDeserializer<LocalDateTime>) (json, typeOf, context) ->
LocalDateTime.parse(json.getAsString()))
.setPrettyPrinting()
.create();
// Método auxiliar para parsear echas con hora
private LocalDateTime parseDateTime(JsonObject issue, String fieldName) {
if (!issue.has(fieldName) || issue.get(fieldName).isJsonNull()) {
return null;
}
String dateStr = issue.get(fieldName).getAsString();
try {
// Si viene con 'Z' al final (UTC), convertir a LocalDateTime
if (dateStr.endsWith("Z")) {
return OffsetDateTime.parse(dateStr)
.atZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime();
} else {
return LocalDateTime.parse(dateStr);
}
} catch (DateTimeParseException ex) {
System.err.println("Error al parsear fecha '" + fieldName + "': " + dateStr);
return null;
}
}
public List<Ticket> getAllTickets(RedmineUser user, boolean abiertos,
LocalDate fechaDesde, LocalDate fechaHasta) {
List<Ticket> allTickets = new ArrayList<>();
int offset = 0;
int limit = 100; // Redmine suele permitir hasta 100 por request (puedes probar 1000)
List<Ticket> batch;
do {
batch = getTickets(user, abiertos, offset, limit, fechaDesde, fechaHasta);
allTickets.addAll(batch);
offset += limit;
} while (!batch.isEmpty());
return allTickets;
}
}

+ 12
- 1
src/main/java/mx/gob/jumapacelaya/models/PlanAnual.java View File

@ -21,13 +21,15 @@ public class PlanAnual {
private String smt;
private String estado;
private String situacion;
private String encuesta;
// Constructor
public PlanAnual(int numero, String nomEquipo, String departamento, boolean monitor,
boolean teclado, boolean mouse, boolean regulador,
boolean cpu, boolean impresora, boolean miniPrint,
boolean laptop, boolean escaner, LocalDate fechaProgramada,
LocalDate fechaMantenimiento, String mesplaneado, String smt, String estado, String situacion) {
LocalDate fechaMantenimiento, String mesplaneado, String smt, String estado, String situacion,
String encuesta) {
this.numero = numero;
this.nomEquipo = nomEquipo;
@ -47,6 +49,7 @@ public class PlanAnual {
this.smt = smt;
this.estado = estado;
this.situacion = situacion;
this.encuesta = encuesta;
}
// Getters
@ -142,4 +145,12 @@ public class PlanAnual {
public void setSituacion(String situacion) {
this.situacion = situacion;
}
public String getEncuesta() {
return encuesta;
}
public void setEncuesta(String encuesta) {
this.encuesta = encuesta;
}
}

+ 87
- 32
src/main/java/mx/gob/jumapacelaya/models/Ticket.java View File

@ -2,43 +2,78 @@ package mx.gob.jumapacelaya.models;
import java.sql.Date;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
public class Ticket {
private final int id;
private final String subject;
private final String description;
private final String status;
private final LocalDate dateCreate;
private LocalDate dateClose;
private User author;
private final LocalDateTime dateCreate;
private LocalDateTime dateClose;
private final LocalDateTime updateOn;
private User autor;
private Integer trackerId;
private String type;
private Double estimatedHrs;
public Ticket(int id, String subject, String description, String status, String dateCreate, String dateClose, Integer trackerId, String type) {
public Ticket(int id, String subject, String description, String status,
String dateCreate, String dateClose, String updateOn,
User autor, Integer trackerId, String type, Double estimatedHrs) {
this.id = id;
this.subject = subject;
this.description = description;
this.status = status;
// Manejo de la fecha de creación
if (dateCreate != null && !dateCreate.isEmpty()) {
this.dateCreate = LocalDate.parse(dateCreate); // Solo se parsea si no está vacío
} else {
this.dateCreate = null; // Si está vacío, asignar null
}
// Manejo de la fecha de cierre
if (dateClose != null && !dateClose.isEmpty()) {
this.dateClose = LocalDate.parse(dateClose); // Solo se parsea si no está vacío
} else {
this.dateClose = null; // Si está vacío, asignar null
}
this.author = author;
this.dateCreate = parseDate(dateCreate);
this.dateClose = parseDate(dateClose);
this.updateOn = parseDate(updateOn);
this.autor = autor;
this.trackerId = trackerId;
this.type = type;
this.estimatedHrs = estimatedHrs;
}
/**
* Método para parsear fechas en formato:
* - yyyy-MM-dd
* - yyyy-MM-dd'T'HH:mm:ssZ
* Siempre convierte a zona horaria local del sistema.
*/
private static LocalDateTime parseDate(String dateStr) {
if (dateStr == null || dateStr.isEmpty()) {
return null;
}
List<DateTimeFormatter> formatters = List.of(
DateTimeFormatter.ISO_OFFSET_DATE_TIME,
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"),
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm")
);
for (DateTimeFormatter formatter : formatters) {
try {
return LocalDateTime.parse(dateStr, formatter);
} catch (Exception ignored) {}
}
try {
LocalDate ld = LocalDate.parse(dateStr);
return ld.atStartOfDay();
} catch (Exception e) {
System.err.println("Error al parsear fecha: " + dateStr);
return null;
}
}
public int getId() {
return id;
}
@ -56,15 +91,15 @@ public class Ticket {
}
public User getAuthor() {
return author;
return autor;
}
public void setAuthor(User author) {
this.author = author;
this.autor = author;
}
public Date getDateCreate() {
return java.sql.Date.valueOf(this.dateCreate);
public LocalDateTime getDateCreate() {
return dateCreate;
}
public Integer getTrackerId() {
@ -75,11 +110,11 @@ public class Ticket {
this.trackerId = tipoId;
}
public LocalDate getDateClose() {
public LocalDateTime getDateClose() {
return dateClose;
}
public void setDateClose(LocalDate dateClose) {
public void setDateClose(LocalDateTime dateClose) {
this.dateClose = dateClose;
}
@ -87,6 +122,26 @@ public class Ticket {
this.type = type;
}
public LocalDateTime getUpdateOn() {
return updateOn;
}
public LocalDateTime getDateCreateLocal() {
return this.dateCreate;
}
public LocalDateTime getDateCloseLocal() {
return this.dateClose;
}
public Double getEstimatedHrs() {
return estimatedHrs;
}
public void setEstimatedHrs(Double estimatedHrs) {
this.estimatedHrs = estimatedHrs;
}
public static class User {
private String username;
@ -111,16 +166,16 @@ public class Ticket {
}
switch (trackerId) {
case 1,3,4,7:
return "1-2 dias Max";
case 2,12:
return "2 hrs Max";
case 5:
return "5 dias aprox.";
case 8,11:
return "2-6 hrs Max";
return "12 horas";
case 6,16:
return "2 horas";
case 7,8,12,13:
return "48 horas";
case 9,10:
return "2 hrs Max";
return "72 horas";
case 11,14,15:
return "24 horas";
default:
return "N/A";
}


+ 48
- 0
src/main/java/mx/gob/jumapacelaya/models/encuestas/Pregunta.java View File

@ -0,0 +1,48 @@
package mx.gob.jumapacelaya.models.encuestas;
public class Pregunta {
private int preguntaId;
private int encuestaId;
private String pregunta;
private int orden;
public Pregunta(int preguntaId, int encuestaId, String pregunta, int orden) {
this.preguntaId = preguntaId;
this.encuestaId = encuestaId;
this.pregunta = pregunta;
this.orden = orden;
}
public int getPreguntaId() {
return preguntaId;
}
public void setPreguntaId(int preguntaId) {
this.preguntaId = preguntaId;
}
public int getEncuestaId() {
return encuestaId;
}
public void setEncuestaId(int encuestaId) {
this.encuestaId = encuestaId;
}
public String getPregunta() {
return pregunta;
}
public void setPregunta(String pregunta) {
this.pregunta = pregunta;
}
public int getOrden() {
return orden;
}
public void setOrden(int orden) {
this.orden = orden;
}
}

+ 66
- 0
src/main/java/mx/gob/jumapacelaya/models/encuestas/Respuesta.java View File

@ -0,0 +1,66 @@
package mx.gob.jumapacelaya.models.encuestas;
import java.sql.Timestamp;
public class Respuesta {
private int respuestaId;
private int mantenimientoId;
private int preguntaId;
private Timestamp fechaResp;
private Boolean respuesta;
private int empleadoId;
public Respuesta(int preguntaId, Boolean respuesta) {
this.preguntaId = preguntaId;
this.respuesta = respuesta;
}
public int getRespuestaId() {
return respuestaId;
}
public void setRespuestaId(int respuestaId) {
this.respuestaId = respuestaId;
}
public int getMantenimientoId() {
return mantenimientoId;
}
public void setMantenimientoId(int mantenimientoId) {
this.mantenimientoId = mantenimientoId;
}
public int getPreguntaId() {
return preguntaId;
}
public void setPreguntaId(int preguntaId) {
this.preguntaId = preguntaId;
}
public Timestamp getFechaResp() {
return fechaResp;
}
public void setFechaResp(Timestamp fechaResp) {
this.fechaResp = fechaResp;
}
public Boolean getRespuesta() {
return respuesta;
}
public void setRespuesta(Boolean respuesta) {
this.respuesta = respuesta;
}
public int getEmpleadoId() {
return empleadoId;
}
public void setEmpleadoId(int empleadoId) {
this.empleadoId = empleadoId;
}
}

+ 168
- 24
src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java View File

@ -1,6 +1,8 @@
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;
@ -17,6 +19,7 @@ import java.sql.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Service
public class DatabaseService {
@ -162,17 +165,20 @@ public class DatabaseService {
/* ----------------Obtener el Plan Anual de Mantenimiento ---------------- */
public List<PlanAnual> getPlanAnual() {
List<PlanAnual> planAnualList = new ArrayList<>();
String query = "SELECT p.plananualid, p.nomEquipo, p.area,\n" +
" p.monitor, p.teclado, p.mouse, p.regulador,\n" +
" p.cpu, p.impresora, p.miniPrint, p.laptop, p.escaner,\n" +
" p.fechaprog, m.fecha AS fechaMantenimiento,\n" +
" me.NOMBRE AS MESPLANEADO, p.tecnicosmt, p.estado,\n" +
" COALESCE(v.SITUACION, 'NO REALIZADO') AS SITUACION\n" +
" FROM PLANANUAL p\n" +
" LEFT JOIN MANTENIMIENTOS m ON p.plananualid = m.plananualid\n" +
" LEFT JOIN MESES me ON p.MESID = me.MESID\n" +
" LEFT JOIN VW_SITUACION_MANTENIMIENTO v ON v.PLANANUALID = p.PLANANUALID\n" +
" ORDER BY p.FECHAPROG ASC";
String query = """
SELECT p.plananualid, p.nomEquipo, p.area,
p.monitor, p.teclado, p.mouse, p.regulador,
p.cpu, p.impresora, p.miniPrint, p.laptop, p.escaner,
p.fechaprog, m.fecha AS fechaMantenimiento,
me.NOMBRE AS MESPLANEADO, p.tecnicosmt, p.estado,
COALESCE(v.SITUACION, 'NO REALIZADO') AS SITUACION,
m.ENCUESTA\s
FROM PLANANUAL p
LEFT JOIN MANTENIMIENTOS m ON p.plananualid = m.plananualid
LEFT JOIN MESES me ON p.MESID = me.MESID
LEFT JOIN VW_SITUACION_MANTENIMIENTO v ON v.PLANANUALID = p.PLANANUALID
ORDER BY p.FECHAPROG ASC
""";
try (Connection connection = getMysqlConnection();
Statement statement = connection.createStatement();
@ -197,7 +203,8 @@ public class DatabaseService {
resultSet.getString("mesplaneado"),
resultSet.getString("tecnicosmt"),
resultSet.getString("estado"),
resultSet.getString("SITUACION")
resultSet.getString("SITUACION"),
resultSet.getString("ENCUESTA") == null ? "N/A" : resultSet.getString("ENCUESTA")
);
planAnualList.add(planAnual);
}
@ -211,17 +218,20 @@ public class DatabaseService {
/* ----------------Obtener el Plan Anual de Mantenimiento por ID ---------------- */
public PlanAnual getPlanAnualPorId(int id) {
String query = "SELECT p.plananualid, p.nomEquipo, p.area,\n" +
" p.monitor, p.teclado, p.mouse, p.regulador,\n" +
" p.cpu, p.impresora, p.miniPrint, p.laptop, p.escaner,\n" +
" p.fechaprog, m.fecha AS fechaMantenimiento,\n" +
" me.NOMBRE AS MESPLANEADO, p.tecnicosmt, p.estado,\n" +
" COALESCE(v.SITUACION, 'NO REALIZADO') AS SITUACION\n" +
" FROM PLANANUAL p\n" +
" LEFT JOIN MANTENIMIENTOS m ON p.plananualid = m.plananualid\n" +
" LEFT JOIN MESES me ON p.MESID = me.MESID\n" +
" LEFT JOIN VW_SITUACION_MANTENIMIENTO v ON v.PLANANUALID = p.PLANANUALID\n" +
" where p.plananualid = ?";
String query = """
SELECT p.plananualid, p.nomEquipo, p.area,
p.monitor, p.teclado, p.mouse, p.regulador,
p.cpu, p.impresora, p.miniPrint, p.laptop, p.escaner,
p.fechaprog, m.fecha AS fechaMantenimiento,
me.NOMBRE AS MESPLANEADO, p.tecnicosmt, p.estado,
COALESCE(v.SITUACION, 'NO REALIZADO') AS SITUACION,
m.ENCUESTA
FROM PLANANUAL p
LEFT JOIN MANTENIMIENTOS m ON p.plananualid = m.plananualid
LEFT JOIN MESES me ON p.MESID = me.MESID
LEFT JOIN VW_SITUACION_MANTENIMIENTO v ON v.PLANANUALID = p.PLANANUALID
WHERE p.plananualid = ?
""";
try (Connection connection = getMysqlConnection();
PreparedStatement statement = connection.prepareStatement(query)) {
@ -246,7 +256,8 @@ public class DatabaseService {
resultSet.getString("mesplaneado"),
resultSet.getString("tecnicosmt"),
resultSet.getString("estado"),
resultSet.getString("SITUACION")
resultSet.getString("SITUACION"),
resultSet.getString("encuesta")
);
}
}
@ -896,4 +907,137 @@ public class DatabaseService {
e.printStackTrace();
}
}
/*************************************** ENCUESTAS ******************************************************************************************************/
public List<Pregunta> getPreguntas() {
List<Pregunta> preguntas = new ArrayList<>();
String query = """
select p.*
from PREGUNTAS p
join ENCUESTAS e on e.ENCUESTAID = p.ENCUESTAID
where e.ENCUESTAID = 1
order by p.ORDEN ASC
""";
try (Connection conn = getMysqlConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
Pregunta pregunta = new Pregunta(
rs.getInt("preguntaid"),
rs.getInt("encuestaid"),
rs.getString("pregunta"),
rs.getInt("orden")
);
preguntas.add(pregunta);
}
} catch (SQLException e) {
e.printStackTrace();
}
return preguntas;
}
public void insertRespuestas(int manteniminetoId, List<Respuesta> respuestas, int empleadoid, String token) {
String insertQuery = """
insert into RESPUESTAS
(MANTENIMIENTOID,PREGUNTAID,FECHARESPUESTA,RESPUESTA,EMPLEADOID)
values
(?,?,?,?,?)
""";
String updateQuery = """
update MANTENIMIENTOS
set ENCUESTA = 'S'
where MANTENIMIENTOID = ?
""";
String markTokenUsedQuery = """
update ENCUESTATOKENS
set USADO = '1' where TOKEN = ?
""";
try (Connection conn = getMysqlConnection()) {
conn.setAutoCommit(false);
try (PreparedStatement insertStmt = conn.prepareStatement(insertQuery);
PreparedStatement updateStmt = conn.prepareStatement(updateQuery);
PreparedStatement tokenStmt = conn.prepareStatement(markTokenUsedQuery)) {
Timestamp ahora = new Timestamp(System.currentTimeMillis());
// 1. Insertar las respuestas en el Batch
for (Respuesta r : respuestas) {
insertStmt.setInt(1, manteniminetoId);
insertStmt.setInt(2, r.getPreguntaId());
insertStmt.setTimestamp(3, ahora);
insertStmt.setBoolean(4, r.getRespuesta());
insertStmt.setInt(5, empleadoid);
insertStmt.addBatch();
}
insertStmt.executeBatch();
// 2. Marcar mantenimiento como encuestado
updateStmt.setInt(1, manteniminetoId);
updateStmt.executeUpdate();
// 3. Invalidar el token para que no se use de nuevo
tokenStmt.setString(1, token);
tokenStmt.executeUpdate();
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
}
} catch (Exception e) {
throw new RuntimeException("Error al guardar encuesta y procesar el token", e);
}
}
public String crearTokenEncuesta(int mantenimientoid) {
String token = UUID.randomUUID().toString();
Timestamp expira = new Timestamp(System.currentTimeMillis() + (24 * 60 * 60 * 1000));
String query = "INSERT INTO ENCUESTATOKENS (TOKEN, MANTENIMIENTOID, FECHAEXPIRACION) VALUES (?, ?, ?)";
try (Connection conn = getMysqlConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, token);
stmt.setInt(2, mantenimientoid);
stmt.setTimestamp(3, expira);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return token;
}
public Integer validarTokenYObtenerId(String token) {
String query = "SELECT MANTENIMIENTOID FROM ENCUESTATOKENS " +
"WHERE TOKEN = ? AND FECHAEXPIRACION > NOW() AND USADO = FALSE";
try (Connection conn = getMysqlConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, token);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return rs.getInt("MANTENIMIENTOID");
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/***************************************************************************************************************************************/
}

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

@ -36,7 +36,6 @@ public class EmailService {
helper.addInline("image_id", imgResource);
mailSender.send(mensaje);
System.out.println("Correo enviado con imagen exitosamente");


+ 319
- 59
src/main/java/mx/gob/jumapacelaya/ui/ActDiariaView.java View File

@ -3,10 +3,16 @@ package mx.gob.jumapacelaya.ui;
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.datepicker.DatePicker;
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;
@ -16,10 +22,15 @@ 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.popover.Popover;
import com.vaadin.flow.component.popover.PopoverPosition;
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;
import com.vaadin.flow.server.StreamResource;
import jakarta.annotation.security.PermitAll;
import mx.gob.jumapacelaya.api.RedmineClient;
import mx.gob.jumapacelaya.api.ServerProperties;
@ -28,13 +39,27 @@ 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.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.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Stream;
@PermitAll
@ -43,89 +68,245 @@ import java.util.*;
@CssImport("./themes/sistema-mantenimiento/styles.css")
public class ActDiariaView extends VerticalLayout {
private static final Logger log = LoggerFactory.getLogger(ActDiariaView.class);
private final RedmineClient redmineClient;
private final UserService userService;
private final Grid<Ticket> grid;
private final HorizontalLayout showColumnsLyt;
private final Button btnColumns;
private final Button btnExport;
private final HorizontalLayout opcionesLyt;
private Checkbox chkSoloAbiertos;
private DatePicker fechaDesde;
private DatePicker fechaHasta;
private Button btnBuscar;
public ActDiariaView(ServerProperties properties, RedmineClient redmineClient, UserService userService) {
this.userService = userService;
this.redmineClient = redmineClient;
this.grid = new Grid<>(Ticket.class, false);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
// Layout de opciones
opcionesLyt = new HorizontalLayout();
opcionesLyt.setWidthFull();
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("padding", "1rem")
.set("margin", "1rem auto");
// Checkbox para ver solo abiertos
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");
// Listener del checkbox
chkSoloAbiertos.addValueChangeListener(e -> {
boolean soloAbiertos = e.getValue(); // marcado = abiertos
loadTickets(soloAbiertos);
fechaDesde.setEnabled(!soloAbiertos);
fechaHasta.setEnabled(!soloAbiertos);
btnBuscar.setEnabled(!soloAbiertos);
});
// Estado inicial
fechaDesde.setEnabled(true);
fechaHasta.setEnabled(true);
btnBuscar.setEnabled(true);
btnBuscar.addClickListener(e -> filtrarTickets());
// Cargar tickets iniciales (cerrados)
loadTickets(false);
opcionesLyt.add(chkSoloAbiertos, fechaDesde, fechaHasta, btnBuscar);
// Configuración de columnas del grid
grid.addColumn(Ticket::getId).setHeader("No.")
.setAutoWidth(true).setFlexGrow(0).setSortable(true);
.setAutoWidth(true)
.setSortable(true);
grid.addColumn(Ticket::getType)
.setHeader("Tipo")
.setAutoWidth(true);
.setAutoWidth(true)
.setKey("tipo");
grid.addColumn(Ticket::getSubject).setHeader("Asunto")
.setWidth("25em");
grid.addColumn(ticket -> ticket.tiempoEst(ticket.getTrackerId()))
.setHeader("Tiempo estimado")
.setAutoWidth(true)
.setKey("tiempoEst");
grid.addColumn(createStatusRender()).setHeader("Estado");
grid.addColumn(ticket -> {
LocalDateTime fecha = ticket.getDateCreate();
return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")) : "";
}).setHeader("Fecha creación")
.setAutoWidth(true)
.setKey("fechaCreacion");
grid.addColumn(ticket -> {
Date date = ticket.getDateCreate();
if (date != null) {
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
return formatter.format(date);
LocalDateTime fecha = ticket.getDateClose();
return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")) : "";
}).setHeader("Fecha cierre")
.setAutoWidth(true)
.setKey("fechaCierre");
grid.addColumn(ticket -> {
LocalDateTime fecha = ticket.getUpdateOn();
return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")) : "";
}).setHeader("Fecha actualización")
.setAutoWidth(true)
.setKey("fechaActualizacion");
grid.addColumn(ticket -> {
if (ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
LocalDateTime inicio = ticket.getDateCreateLocal();
LocalDateTime fin = ticket.getDateCloseLocal();
long horasReales = ChronoUnit.HOURS.between(inicio, fin);
Double horasEstimadas = ticket.getEstimatedHrs();
if (horasEstimadas != null) {
return horasReales <= horasEstimadas ? "✅ En tiempo" : "⚠️ Excedido";
} else {
return "Sin estimación";
}
} else {
return "";
}
}).setHeader("Fecha creacion").setAutoWidth(true);
}).setHeader("Situación").setAutoWidth(true).setKey("cumplimientoHoras");
grid.addColumn(createStatusRender())
.setHeader("Estado")
.setKey("estado");
grid.addColumn(ticket -> {
LocalDate fechaCierre = ticket.getDateClose();
if (fechaCierre != null) {
DateTimeFormatter formatterClose = DateTimeFormatter.ofPattern("dd/MM/yyyy");
return fechaCierre.format(formatterClose);
if (ticket.getDateCreateLocal() != null) {
LocalDateTime fechaInicio = ticket.getDateCreateLocal();
LocalDateTime fechaFin = (ticket.getDateCloseLocal() != null)
? ticket.getDateCloseLocal()
: LocalDateTime.now();
long dias = ChronoUnit.DAYS.between(fechaInicio, fechaFin);
return dias + " días";
} else {
return "";
}
}).setHeader("Fecha cierre").setAutoWidth(true);
}).setHeader("Duración").setAutoWidth(true).setKey("duracion");
grid.addColumn(ticket ->
ticket.getAuthor() != null ? ticket.getAuthor().getUsername() : "")
.setHeader("Autor")
.setAutoWidth(true)
.setKey("autor");
grid.addColumn(Ticket::getSubject)
.setHeader("Asunto")
.setWidth("25rem")
.setKey("asunto");
//grid.addColumn(ticket -> ticket.tiempoEst(ticket.getTrackerId())).setHeader("Tiempo estimado de atencion").setAutoWidth(false);
grid.addComponentColumn(ticket -> {
Button btnVer = new Button(new Icon(VaadinIcon.EYE));
btnVer.addClickListener(event -> showDescription(ticket));
btnVer.getStyle().set("color", "#A02142");
return btnVer;
}).setAutoWidth(true);
grid.addColumn(buttonTicketComponentRenderer()).setAutoWidth(true);
});
grid.addThemeVariants(GridVariant.LUMO_WRAP_CELL_CONTENT);
grid.getStyle().set("opacity", "0.8");
grid.setAllRowsVisible(false);
grid.addThemeVariants(GridVariant.LUMO_ROW_STRIPES);
grid.setSizeFull();
// Ajustar tamaño del Grid y Layout
// Botón para mostrar/ocultar columnas
btnColumns = new Button(VaadinIcon.GRID_H.create());
// Botón para exportar
btnExport = new Button("Exportar", LineAwesomeIcon.FILE_EXCEL.create());
btnExport.addClickListener(e -> exportTicketsCerrados());
showColumnsLyt = new HorizontalLayout(btnExport, btnColumns);
showColumnsLyt.setJustifyContentMode(JustifyContentMode.END);
showColumnsLyt.setWidthFull();
HorizontalLayout columnsSelectorLyt = new HorizontalLayout();
columnsSelectorLyt.setAlignItems(Alignment.END);
Popover popover = new Popover();
popover.setModal(true);
popover.setBackdropVisible(true);
popover.setPosition(PopoverPosition.BOTTOM_END);
popover.setTarget(btnColumns);
Div heading = new Div("Ver/Ocultar columnas");
heading.getStyle().set("font-weight", "600");
heading.getStyle().set("padding", "var(--lumo-space-xs)");
List<String> columns = List.of("tipo","tiempoEst","asunto","autor","estado","fechaCreacion","fechaCierre","fechaActualizacion","duracion");
CheckboxGroup<String> chkColumns = new CheckboxGroup<>();
chkColumns.addThemeVariants(CheckboxGroupVariant.LUMO_VERTICAL);
chkColumns.setItems(columns);
chkColumns.setItemLabelGenerator((item) -> {
String label = StringUtils
.join(StringUtils.splitByCharacterTypeCamelCase(item), " ");
return StringUtils.capitalize(label.toLowerCase());
});
chkColumns.addValueChangeListener((e) -> {
columns.forEach((key) -> {
Grid.Column<?> col = grid.getColumnByKey(key);
if (col != null) {
col.setVisible(e.getValue().contains(key));
} else {
System.out.println("Columna no encontrada para: " + key);
}
});
});
Set<String> defaultColumns = Set.of("tipo","tiempoEst","fechaCreacion","fechaCierre","cumplimientoHoras","estado","duracion","autor","asunto");
chkColumns.setValue(defaultColumns);
popover.add(heading, chkColumns);
// Ajustar tamaños
grid.setSizeFull();
setSizeFull();
add(opcionesLyt, showColumnsLyt, grid);
setMargin(false);
}
add(grid);
//expand(grid);
setMargin(false);
loadTickets();
// NOTA: Ajuste en la carga de tickets para que ahora haga LazyLoad al cargar todos los ticktes cerrados
private void loadTickets(boolean soloAbiertos) {
RedmineUser user = userService.getRedmineUser();
// 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()
)
);
}
private void loadTickets() {
try {
List<Ticket> tickets = redmineClient.getTickets(userService.getRedmineUser(), true);
List<Ticket> filteredTickets = tickets.stream()
.filter(ticket -> ticket.getTrackerId() == 13 || ticket.getTrackerId() == 14)
.toList();
grid.setItems(filteredTickets);
} catch (Exception e) {
e.printStackTrace();
// Manejo de error al cargar los tickets
}
}
private ComponentRenderer<Span, Ticket> createStatusRender() {
return new ComponentRenderer<>(ticket -> {
@ -134,34 +315,28 @@ public class ActDiariaView extends VerticalLayout {
// Estilos basados en el estado del ticket
switch (ticket.getStatus().toLowerCase()) {
case "análisis":
span.getElement().getStyle().set("color","orange");
case "rechazado":
String theme1 = String.format("badge %s", "error");
span.getElement().setAttribute("theme",theme1);
break;
case "desarrollo":
span.getElement().getStyle().set("color","blue");
break;
case "rechazada":
span.getElement().getStyle().set("color","red");
break;
case "cerrada":
span.getElement().getStyle().set("color","grey");
break;
case "solicitado":
span.getElement().getStyle().set("color","purple");
case "resuelto","cerrado":
String theme2 = String.format("badge %s", "success");
span.getElement().setAttribute("theme",theme2);
break;
case "qa":
span.getElement().getStyle().set("color","#C21DF2");
String theme3 = String.format("badge %s", "warning");
span.getElement().setAttribute("theme",theme3);
break;
default:
span.getElement().getStyle().set("color","green");
break;
String theme4 = String.format("badge %s", "");
span.getElement().setAttribute("theme",theme4);
}
return span;
});
}
public ComponentRenderer<Button, Ticket> buttonTicketComponentRenderer() {
/*public ComponentRenderer<Button, Ticket> buttonTicketComponentRenderer() {
return new ComponentRenderer<>(ticket -> {
Button button = new Button(new Icon(VaadinIcon.EDIT) );
button.getStyle().set("color", "#A02142");
@ -178,7 +353,7 @@ public class ActDiariaView extends VerticalLayout {
});
return button;
});
}
}*/
public void cerrarTicket(Ticket ticket, RedmineUser user) {
try {
@ -220,4 +395,89 @@ public class ActDiariaView extends VerticalLayout {
dialog.open();
}
// 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]);
}
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 (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");
}
}
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
workbook.close();
StreamResource resource = new StreamResource("tickets_cerrados.xlsx",
() -> new ByteArrayInputStream(out.toByteArray()));
StreamRegistration registration = UI.getCurrent().getSession()
.getResourceRegistry().registerResource(resource);
UI.getCurrent().getPage().executeJs(
"window.open('" + registration.getResourceUri().toString() + "','_blank')"
);
} catch (Exception e) {
log.error("Error exportando tickets", e);
Notification.show("Error al exportar los tickets", 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
}
}
private void filtrarTickets() {
boolean soloAbiertos = chkSoloAbiertos.getValue();
loadTickets(soloAbiertos);
}
}

+ 206
- 143
src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java View File

@ -9,10 +9,12 @@ import java.util.List;
import java.util.Map;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.*;
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.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.vaadin.lineawesome.LineAwesomeIcon;
@ -22,9 +24,6 @@ import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.GridVariant;
import com.vaadin.flow.component.gridpro.GridPro;
import com.vaadin.flow.component.html.H3;
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;
@ -54,28 +53,28 @@ import mx.gob.jumapacelaya.services.SecurityService;
@CssImport("./themes/sistema-mantenimiento/styles.css")
public class DetallesMantView extends VerticalLayout implements BeforeEnterObserver {
private H3 id;
private VerticalLayout mainLayout;
private HorizontalLayout headerLayout;
private HorizontalLayout layout2;
private HorizontalLayout layout3;
private HorizontalLayout botonesLayout;
@Value("${app.base-url}")
private String baseUrl;
private final H3 id;
private final EmailService emailService;
private HorizontalLayout fechasLayout;
private TextField txtId;
private TextField txtEquipo;
private ComboBox<TiposMantenimiento> cbTipo;
private TextField txtFecha;
private TextField txtFechaProgramada;
private TextField txtSituacion;
private ComboBox<Usuario> cbUsuario;
private ComboBox<DepartamentosModel> cbDepartamento;
private GridPro<HardwareDetalle> gridHardware;
private GridPro<ActualizacioneSeguridadModel> gridActualizaciones;
private Button btnEditar;
private Button btnEditarFirmas;
private Button btnImprimirRepo;
private Button btnCancelar;
private Button btnGuardar;
private final TextField txtEquipo;
private final ComboBox<TiposMantenimiento> cbTipo;
private final TextField txtFecha;
private final TextField txtFechaProgramada;
private final TextField txtSituacion;
private final ComboBox<Usuario> cbUsuario;
private final ComboBox<DepartamentosModel> cbDepartamento;
private final GridPro<HardwareDetalle> gridHardware;
private final GridPro<ActualizacioneSeguridadModel> gridActualizaciones;
private final Button btnEditar;
private final Button btnEditarFirmas;
private final Button btnImprimirRepo = new Button("Imprimir Reporte", new Icon(VaadinIcon.PRINT));
private final Button btnCancelar;
private final Button btnGuardar;
private final Button btnEnviarEncuesta = new Button("Enviar encuesta", LineAwesomeIcon.ENVELOPE_SOLID.create());
private int planAnualIdActual;
private int mantenimientoIdActual;
private Dialog confirmDialog;
@ -86,34 +85,31 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
private Image firmaUsuarioImg;
private Image firmaSmtImg;
private Image firmaGciaImg;
Notification avisoEncuestaNtf;
private VerticalLayout userSignLayout;
private VerticalLayout smtSignLayout;
private VerticalLayout gciaSignLayout;
private final SecurityService securityService;
private final DatabaseService service;
private final ReportService reportService;
public DetallesMantView(SecurityService securityService, DatabaseService service, ReportService reportService) {
public DetallesMantView(SecurityService securityService, DatabaseService service, ReportService reportService, EmailService emailService) {
this.service = service;
this.securityService = securityService;
this.reportService = reportService;
setPadding(true);
mainLayout = new VerticalLayout();
VerticalLayout mainLayout = new VerticalLayout();
mainLayout.setHeightFull();
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");
.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");
id = new H3();
headerLayout = new HorizontalLayout();
HorizontalLayout headerLayout = new HorizontalLayout();
headerLayout.setWidthFull();
headerLayout.add(id);
@ -129,7 +125,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
cbDepartamento.setItems(service.getDepartamentos());
cbDepartamento.setItemLabelGenerator(DepartamentosModel::getNombre);
layout2 = new HorizontalLayout();
HorizontalLayout layout2 = new HorizontalLayout();
layout2.setWidthFull();
txtEquipo = new TextField("Equipo:");
txtEquipo.setReadOnly(true);
@ -147,7 +143,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
txtSituacion.setReadOnly(true);
fechasLayout.add(txtFechaProgramada,txtFecha,txtSituacion);
layout3 = new HorizontalLayout();
HorizontalLayout layout3 = new HorizontalLayout();
layout3.setWidthFull();
cbUsuario.setReadOnly(true);
cbDepartamento.setReadOnly(true);
@ -159,22 +155,22 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
// Grid que muestra los detalles del hardware
gridHardware = new GridPro<>();
gridHardware.addColumn(item -> item.getDescripcion())
.setHeader("Descripción");
.setHeader("Descripción");
gridHardware.addEditColumn(HardwareDetalle::getNumSerie)
.text((item, newValue) -> item.setNumSerie(newValue))
.setHeader("No. Serie")
.setEditorComponent(new TextField());
.text((item, newValue) -> item.setNumSerie(newValue))
.setHeader("No. Serie")
.setEditorComponent(new TextField());
gridHardware.addEditColumn(HardwareDetalle::getModelo)
.text((item, newValue) -> item.setModelo(newValue))
.setHeader("Modelo")
.setEditorComponent(new TextField());
.text((item, newValue) -> item.setModelo(newValue))
.setHeader("Modelo")
.setEditorComponent(new TextField());
gridHardware.addEditColumn(HardwareDetalle::getPlaca)
.text((item, newValue) -> item.setPlaca(newValue))
.setHeader("Placa")
.setEditorComponent(new TextField());
.text((item, newValue) -> item.setPlaca(newValue))
.setHeader("Placa")
.setEditorComponent(new TextField());
gridHardware.setWidthFull();
gridHardware.setEditOnClick(false);
@ -186,14 +182,14 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
// Grid que muestra las actualizaciones de seguridad
gridActualizaciones = new GridPro<>();
gridActualizaciones.addEditColumn(ActualizacioneSeguridadModel::getDescripcion)
.text((item, newValue) -> item.setDescripcion(newValue))
.setHeader("Descripción")
.setEditorComponent(new TextField());
.text((item, newValue) -> item.setDescripcion(newValue))
.setHeader("Descripción")
.setEditorComponent(new TextField());
gridActualizaciones.addEditColumn(ActualizacioneSeguridadModel::getOtrasactualizaciones)
.text((item, newValue) -> item.setOtrasactualizaciones(newValue))
.setHeader("Otras Actualizaciones")
.setEditorComponent(new TextField());
.text((item, newValue) -> item.setOtrasactualizaciones(newValue))
.setHeader("Otras Actualizaciones")
.setEditorComponent(new TextField());
gridActualizaciones.setWidthFull();
gridActualizaciones.setEditOnClick(false);
@ -201,16 +197,18 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
gridActualizaciones.addThemeVariants(GridVariant.LUMO_ROW_STRIPES);
HorizontalLayout botonesHeaderLyt = new HorizontalLayout();
botonesHeaderLyt.setWidthFull();
botonesHeaderLyt.add(btnImprimirRepo, btnEnviarEncuesta);
botonesLayout = new HorizontalLayout();
HorizontalLayout botonesLayout = new HorizontalLayout();
botonesLayout.setWidthFull();
botonesLayout.setJustifyContentMode(JustifyContentMode.CENTER);
btnEditar = new Button("Editar", new Icon(VaadinIcon.EDIT));
btnImprimirRepo = new Button("Imprimir Reporte", new Icon(VaadinIcon.PRINT));
btnEditarFirmas = new Button("Editar firmas", LineAwesomeIcon.SIGNATURE_SOLID.create());
btnGuardar = new Button("Guardar", LineAwesomeIcon.SAVE_SOLID.create());
btnCancelar = new Button("Cancelar", new Icon(VaadinIcon.CLOSE_CIRCLE_O));
botonesLayout.add(btnEditar,btnEditarFirmas,btnImprimirRepo,btnGuardar,btnCancelar);
botonesLayout.add(btnEditar,btnEditarFirmas,btnGuardar,btnCancelar);
btnGuardar.setVisible(false);
btnGuardar.getStyle().set("background-color", "#008000");
@ -304,6 +302,12 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
});
btnEnviarEncuesta.addClickListener(e -> {
avisoEncuestaNtf.close();
enviarCorreo();
});
// Se dispara el dialogo de confirmacion
btnGuardar.addClickListener(e -> confirmDialog.open());
@ -317,10 +321,10 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
txtJustificacion.setRequired(true);
confirmDialog.add(
new VerticalLayout(
new Span("¿Estás seguro de que deseas actualizar el mantenimiento?"),
txtJustificacion
)
new VerticalLayout(
new Span("¿Estás seguro de que deseas actualizar el mantenimiento?"),
txtJustificacion
)
);
Button btnConfirmar = new Button("Actualizar", event -> {
@ -371,111 +375,113 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
addSignatureSection();
mainLayout.add(headerLayout,layout2,fechasLayout,layout3,gridHardware,gridActualizaciones,firmasLAyout,botonesLayout);
add(mainLayout);
mainLayout.add(headerLayout, layout2,fechasLayout, layout3,gridHardware,gridActualizaciones,firmasLAyout, botonesLayout);
add(botonesHeaderLyt,mainLayout);
this.setSpacing(false);
this.emailService = emailService;
}
private void realizarActualizacion() {
boolean exito = true;
// Obtén los objetos seleccionados
TiposMantenimiento tipoSeleccionado = cbTipo.getValue();
Usuario usuarioSeleccionado = cbUsuario.getValue();
DepartamentosModel departamentoSeleccionado = cbDepartamento.getValue();
// Obtén los objetos seleccionados
TiposMantenimiento tipoSeleccionado = cbTipo.getValue();
Usuario usuarioSeleccionado = cbUsuario.getValue();
DepartamentosModel departamentoSeleccionado = cbDepartamento.getValue();
int tipoId = tipoSeleccionado != null ? Integer.parseInt(tipoSeleccionado.getTipomantId()) : 0;
int empleadoId = usuarioSeleccionado != null ? Integer.parseInt(usuarioSeleccionado.getEmpleadoId()) : 0;
String departamentoId = departamentoSeleccionado != null ? departamentoSeleccionado.getDepartamentoId() : null;
int tipoId = tipoSeleccionado != null ? Integer.parseInt(tipoSeleccionado.getTipomantId()) : 0;
int empleadoId = usuarioSeleccionado != null ? Integer.parseInt(usuarioSeleccionado.getEmpleadoId()) : 0;
String departamentoId = departamentoSeleccionado != null ? departamentoSeleccionado.getDepartamentoId() : null;
// Fechas (corrige el campo)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate fechaProg = !txtFechaProgramada.getValue().isEmpty() ? LocalDate.parse(txtFechaProgramada.getValue(), formatter) : null;
LocalDate fechaRealizado = !txtFecha.getValue().isEmpty() ? LocalDate.parse(txtFecha.getValue(), formatter) : null;
// Fechas (corrige el campo)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate fechaProg = !txtFechaProgramada.getValue().isEmpty() ? LocalDate.parse(txtFechaProgramada.getValue(), formatter) : null;
LocalDate fechaRealizado = !txtFecha.getValue().isEmpty() ? LocalDate.parse(txtFecha.getValue(), formatter) : null;
// Obtener las firmas como en MantenimientoView
byte[] userSignatureBytes = userSignPad.getImageBase64();
byte[] smtSignatureBytes = smtSignPad.getImageBase64();
// Obtener las firmas como en MantenimientoView
byte[] userSignatureBytes = userSignPad.getImageBase64();
byte[] smtSignatureBytes = smtSignPad.getImageBase64();
String userSignatureBase64 = (userSignatureBytes != null) ? Base64.getEncoder().encodeToString(userSignatureBytes) : null;
String smtSignatureBase64 = (smtSignatureBytes != null) ? Base64.getEncoder().encodeToString(smtSignatureBytes) : null;
String userSignatureBase64 = (userSignatureBytes != null) ? Base64.getEncoder().encodeToString(userSignatureBytes) : null;
String smtSignatureBase64 = (smtSignatureBytes != null) ? Base64.getEncoder().encodeToString(smtSignatureBytes) : null;
// Si la firma está vacía, conserva la anterior
if (userSignatureBase64 == null || esFirmaVacia(userSignatureBase64)) {
userSignatureBase64 = service.getDetalleMantenimientoPorPlanAnualId(planAnualIdActual).getFirmaUser();
}
if (smtSignatureBase64 == null || esFirmaVacia(smtSignatureBase64)) {
smtSignatureBase64 = service.getDetalleMantenimientoPorPlanAnualId(planAnualIdActual).getFirmaSmt();
}
// Si la firma está vacía, conserva la anterior
if (userSignatureBase64 == null || esFirmaVacia(userSignatureBase64)) {
userSignatureBase64 = service.getDetalleMantenimientoPorPlanAnualId(planAnualIdActual).getFirmaUser();
}
if (smtSignatureBase64 == null || esFirmaVacia(smtSignatureBase64)) {
smtSignatureBase64 = service.getDetalleMantenimientoPorPlanAnualId(planAnualIdActual).getFirmaSmt();
}
// ACTUALIZA PLANANUAL (nombre del equipo y fecha programada)
if (!service.actualizarPlanAnual(planAnualIdActual, txtEquipo.getValue())) {
exito = false;
}
// ACTUALIZA MANTENIMIENTOS (tipo, departamento, usuario, fecha realizado)
if (!service.actualizarMantenimiento(
// ACTUALIZA PLANANUAL (nombre del equipo y fecha programada)
if (!service.actualizarPlanAnual(planAnualIdActual, txtEquipo.getValue())) {
exito = false;
}
// ACTUALIZA MANTENIMIENTOS (tipo, departamento, usuario, fecha realizado)
if (!service.actualizarMantenimiento(
mantenimientoIdActual,
tipoId,
departamentoId,
empleadoId,
fechaRealizado,
txtEquipo.getValue(),
userSignatureBase64,
smtSignatureBase64
)) {
exito = false;
}
userSignatureBase64,
smtSignatureBase64
)) {
exito = false;
}
// ACTUALIZA HARDWARE (grid)
for (HardwareDetalle detalle : gridHardware.getListDataView().getItems().toList()) {
if (!service.actualizarHardwareDetalle(detalle)) {
exito = false;
}
// ACTUALIZA HARDWARE (grid)
for (HardwareDetalle detalle : gridHardware.getListDataView().getItems().toList()) {
if (!service.actualizarHardwareDetalle(detalle)) {
exito = false;
}
}
// ACTUALIZA ACTUALIZACIONES DE SEGURIDAD (grid)
for (ActualizacioneSeguridadModel actualizacion : gridActualizaciones.getListDataView().getItems().toList()) {
if (!service.actualizarActualizacionSeg(actualizacion)) {
exito = false;
}
// ACTUALIZA ACTUALIZACIONES DE SEGURIDAD (grid)
for (ActualizacioneSeguridadModel actualizacion : gridActualizaciones.getListDataView().getItems().toList()) {
if (!service.actualizarActualizacionSeg(actualizacion)) {
exito = false;
}
}
if (exito) {
Notification.show("Registros actualizados correctamente", 3000, Notification.Position.MIDDLE)
if (exito) {
Notification.show("Registros actualizados correctamente", 3000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
} else {
Notification.show("Error al actualizar uno o más registros", 3000, Notification.Position.MIDDLE)
} else {
Notification.show("Error al actualizar uno o más registros", 3000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
}
// Opcional: volver a modo solo lectura
txtEquipo.setReadOnly(true);
cbTipo.setReadOnly(true);
txtFecha.setReadOnly(true);
cbUsuario.setReadOnly(true);
cbDepartamento.setReadOnly(true);
btnImprimirRepo.setVisible(true);
btnEditar.setVisible(true);
btnEditarFirmas.setVisible(true);
btnGuardar.setVisible(false);
btnCancelar.setVisible(false);
gridHardware.setEditOnClick(false);
gridHardware.getEditor().cancel();
if (firmaUsuarioImg != null && userSignPad.getParent().isPresent()) {
userSignLayout.replace(userSignPad, firmaUsuarioImg);
}
if (firmaSmtImg != null && smtSignPad.getParent().isPresent()) {
smtSignLayout.replace(smtSignPad, firmaSmtImg);
}
}
// Opcional: volver a modo solo lectura
txtEquipo.setReadOnly(true);
cbTipo.setReadOnly(true);
txtFecha.setReadOnly(true);
cbUsuario.setReadOnly(true);
cbDepartamento.setReadOnly(true);
btnImprimirRepo.setVisible(true);
btnEditar.setVisible(true);
btnEditarFirmas.setVisible(true);
btnGuardar.setVisible(false);
btnCancelar.setVisible(false);
gridHardware.setEditOnClick(false);
gridHardware.getEditor().cancel();
if (firmaUsuarioImg != null && userSignPad.getParent().isPresent()) {
userSignLayout.replace(userSignPad, firmaUsuarioImg);
}
if (firmaSmtImg != null && smtSignPad.getParent().isPresent()) {
smtSignLayout.replace(smtSignPad, firmaSmtImg);
}
}
// Metodo para verificar si la firma corresponde a una cadena de firma vacia
private boolean esFirmaVacia(String firmaBase64) {
String firmaVacia = "iVBORw0KGgoAAAANSUhEUgAAAXkAAAD8CAYAAACSCdTiAAAIbElEQVR4Xu3UAQ0AIAwDQeZfNCPI+Nwc9Lp07rvjCBAgQCApMEY+2atQBAgQ+AJG3iMQIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBAw8n6AAAECYQEjHy5XNAIECBh5P0CAAIGwgJEPlysaAQIEjLwfIECAQFjAyIfLFY0AAQJG3g8QIEAgLGDkw+WKRoAAASPvBwgQIBAWMPLhckUjQICAkfcDBAgQCAsY+XC5ohEgQMDI+wECBAiEBYx8uFzRCBAgYOT9AAECBMICRj5crmgECBBYAuXtOkIWm1QAAAAASUVORK5CYII=";
String firmaVacia = "iVBORw0KGgoAAAANSUhEUgAAAdQAAAD8CAYAAADOr1WDAAAKOElEQVR4Xu3VsQ0AIAwEMbL/0IBYgStNTwrrpZt93/IIECBAgACBL4ER1C8/nwkQIECAwBMQVEMgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgAABQbUBAgQIECAQCAhqgOgEAQIECBAQVBsgQIAAAQKBgKAGiE4QIECAAAFBtQECBAgQIBAICGqA6AQBAgQIEBBUGyBAgAABAoGAoAaIThAgQIAAAUG1AQIECBAgEAgIaoDoBAECBAgQEFQbIECAAAECgYCgBohOECBAgACBA8Bc7Tp3N5/2AAAAAElFTkSuQmCC";
return firmaBase64.equals(firmaVacia);
}
@ -515,18 +521,75 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
}
// METODO PARA ENVIAR LOS CORREOS ELECTRONICOS
private void enviarCorreo() {
Usuario usuarioDestino = cbUsuario.getValue();
if (usuarioDestino != null && usuarioDestino.getEmail() != null) {
String destinatario = usuarioDestino.getEmail();
String asunto = "Encuesta de satisfacción -Mantenimiento #" + mantenimientoIdActual;
String token = service.crearTokenEncuesta(mantenimientoIdActual);
String linkEncuesta = baseUrl + "/encuesta?token=" + token;
String cuerpo = "<html>" +
"<body>" +
"<a href = "+linkEncuesta+">" +
"<img src='cid:image_id'/>"+
"</a>" +
"</body>" +
"</html>";
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)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
} else {
Notification.show("Por favor, seleccione un usuario destino", 3000, Notification.Position.MIDDLE);
}
}
@Override
public void beforeEnter(BeforeEnterEvent event) {
String idParam = event.getLocation().getQueryParameters().getParameters().get("id") != null
? event.getLocation().getQueryParameters().getParameters().get("id").stream().findFirst().orElse(null)
: null;
String encParam = event.getLocation().getQueryParameters().getParameters().getOrDefault("enc", List.of())
.stream().findFirst().orElse("N").toUpperCase();
if (idParam != null) {
try {
int planId = Integer.parseInt(idParam);
DetalleMantenimientoModel detalle = service.getDetalleMantenimientoPorPlanAnualId(planId);
if (encParam.equalsIgnoreCase("N")) {
Icon icono = new Icon(VaadinIcon.WARNING);
icono.setSize("40px");
NativeLabel aviso = new NativeLabel("Aviso:");
aviso.getStyle().set("font-weight", "bold");
NativeLabel label = new NativeLabel("¡No se ha respondido una encuesta para este mantenimiento!");
VerticalLayout textLyt = new VerticalLayout(aviso,label);
textLyt.setPadding(false);
textLyt.setSpacing(false);
HorizontalLayout layout = new HorizontalLayout(icono, textLyt);
layout.setJustifyContentMode(JustifyContentMode.CENTER);
layout.setAlignItems(Alignment.CENTER);
avisoEncuestaNtf = new Notification(layout);
avisoEncuestaNtf.setPosition(Notification.Position.TOP_CENTER);
avisoEncuestaNtf.addThemeVariants(NotificationVariant.LUMO_WARNING);
avisoEncuestaNtf.setDuration(5000);
avisoEncuestaNtf.open();
} else {
btnEnviarEncuesta.setEnabled(false);
btnEnviarEncuesta.getStyle().set("opacity", "0.5");
}
if (detalle != null) {
this.planAnualIdActual = detalle.getPlanAnualId();
this.mantenimientoIdActual = detalle.getId();
@ -534,9 +597,9 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
id.setText("Plan ID: " + idParam);
txtEquipo.setValue(String.valueOf(detalle.getNombreEquipo()));
cbTipo.setValue(
cbTipo.getListDataView().getItems()
.filter(t -> t.getNombre().equals(detalle.getTipo()))
.findFirst().orElse(null)
cbTipo.getListDataView().getItems()
.filter(t -> t.getNombre().equals(detalle.getTipo()))
.findFirst().orElse(null)
);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
@ -548,14 +611,14 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
txtFecha.setValue(fechaReal);
txtSituacion.setValue(situacion);
cbUsuario.setValue(
cbUsuario.getListDataView().getItems()
.filter(u -> u.getNombre().equals(detalle.getUsuario()))
.findFirst().orElse(null)
cbUsuario.getListDataView().getItems()
.filter(u -> u.getNombre().equals(detalle.getUsuario()))
.findFirst().orElse(null)
);
cbDepartamento.setValue(
cbDepartamento.getListDataView().getItems()
.filter(d -> d.getNombre().equals(detalle.getDepartamento()))
.findFirst().orElse(null)
cbDepartamento.getListDataView().getItems()
.filter(d -> d.getNombre().equals(detalle.getDepartamento()))
.findFirst().orElse(null)
);
int mantId = detalle.getId();
List<HardwareDetalle> listaHardware = service.getHardwaredetallePorMantId(mantId);
@ -589,7 +652,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
firmaSmtImg.setWidthFull();
smtSignLayout.replace(smtSignPad, firmaSmtImg);
}
Image firmaGcia = new Image("images/FirmaGerenteTI.png", "Firma de la Gcia. de T.I");
firmaGcia.setHeight("200px");
firmaGcia.setWidthFull();


+ 216
- 0
src/main/java/mx/gob/jumapacelaya/ui/EncuestaView.java View File

@ -0,0 +1,216 @@
package mx.gob.jumapacelaya.ui;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
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.TextField;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import mx.gob.jumapacelaya.models.encuestas.Pregunta;
import mx.gob.jumapacelaya.models.encuestas.Respuesta;
import mx.gob.jumapacelaya.services.DatabaseService;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
@Route("encuesta")
@PageTitle("Encuesta de satisfacción")
@AnonymousAllowed
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 static class RespuestaComponente {
Pregunta pregunta;
RadioButtonGroup<String> radios;
RespuestaComponente(Pregunta pregunta, RadioButtonGroup<String> radios) {
this.pregunta = pregunta;
this.radios = radios;
}
}
private final List<RespuestaComponente> respuestasUI = new ArrayList<>();
public EncuestaView(DatabaseService encuestasDBService) {
this.encuestasDBService = encuestasDBService;
setSpacing(true);
setPadding(true);
setSizeFull();
this.getStyle()
.set("background-image", "url('/images/LOGO_1024X768.jpg')")
.set("background-repeat", "no-repeat")
.set("background-size", "cover")
.set("background-position", "center");
mainLyt.setHeightFull();
mainLyt.setWidth("55%");
mainLyt.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");
btnEnviar.addClickListener(e -> procesarRespuestas());
this.add(mainLyt);
}
private void mostrarMensaje() {
txtNumEmpl.setRequired(true);
txtNumEmpl.setErrorMessage("Por favor, llena este campo");
VerticalLayout titulos = new VerticalLayout();
titulos.setSpacing(false);
titulos.setPadding(false);
titulos.add(
new H2("Mantenimiento Preventivo a Equipo de Computo"),
new Paragraph("La siguiente encuesta tiene como objetivo valorar el servicio que se brinda al proporcionar el mantenimiento preventivo del equipo de computo en el Organismo."),
txtNumEmpl
);
VerticalLayout preguntasLyt = new VerticalLayout();
preguntasLyt.setWidthFull();
preguntasLyt.setPadding(false);
preguntasLyt.setSpacing(false);
List<Pregunta> preguntas = encuestasDBService.getPreguntas();
for (Pregunta p : preguntas) {
HorizontalLayout fila = new HorizontalLayout();
fila.setWidthFull();
fila.setAlignItems(Alignment.CENTER);
fila.getStyle().set("padding", "0.4rem");
fila.getStyle().set("border-bottom", "1px solid #e0e0e0");
Span texto = new Span(p.getPregunta());
texto.getStyle().set("font-weight", "600");
RadioButtonGroup<String> radios = new RadioButtonGroup<>();
radios.setItems("Si", "No");
fila.add(texto,radios);
preguntasLyt.add(fila);
respuestasUI.add(new RespuestaComponente(p,radios));
}
HorizontalLayout gracias = new HorizontalLayout(new H3("¡Gracias!"));
gracias.setWidthFull();
gracias.setJustifyContentMode(JustifyContentMode.CENTER);
btnEnviar.setWidthFull();
btnEnviar.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
HorizontalLayout btnEnviarLyt = new HorizontalLayout(btnEnviar);
btnEnviarLyt.setWidthFull();
btnEnviarLyt.setJustifyContentMode(JustifyContentMode.CENTER);
btnEnviarLyt.setSpacing(false);
btnEnviarLyt.setPadding(false);
mainLyt.removeAll();
mainLyt.add(titulos,preguntasLyt,gracias, btnEnviarLyt);
}
private void procesarRespuestas() {
if (txtNumEmpl.isEmpty()) {
txtNumEmpl.setInvalid(true);
return;
}
int empleadoId = Integer.parseInt(txtNumEmpl.getValue());
Timestamp ahora = new Timestamp(System.currentTimeMillis());
List<Respuesta> respuestas = new ArrayList<>();
for (RespuestaComponente rc : respuestasUI) {
if (rc.radios.getValue() == null) continue;
respuestas.add(new Respuesta(
rc.pregunta.getPreguntaId(),
rc.radios.getValue().equals("Si")
));
}
encuestasDBService.insertRespuestas(
mantenimientoId,
respuestas,
empleadoId,
this.token
);
NotiEncuesta();
mainLyt.setVisible(false);
this.setEnabled(false);
}
private void NotiEncuesta() {
Notification ntf = new Notification();
ntf.setPosition(Notification.Position.MIDDLE);
ntf.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
ntf.setDuration(0);
H2 titulo = new H2("Encuesta enviada");
titulo.getStyle().set("color","white");
Span msj = new Span("Sus respuestas se han guardado correctamente.");
Span msj2 = new Span("Gracias por participar en nuestra encuesta.");
Icon succesIcon = new Icon(VaadinIcon.CHECK_CIRCLE);
succesIcon.setSize("48px");
VerticalLayout layout = new VerticalLayout(succesIcon,titulo,msj,msj2);
layout.setAlignItems(Alignment.CENTER);
ntf.add(layout);
ntf.open();
}
@Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
String token = beforeEnterEvent.getLocation().getQueryParameters()
.getParameters().getOrDefault("token", List.of("")).get(0);
if (!token.isEmpty()) {
Integer idRecuperado = encuestasDBService.validarTokenYObtenerId(token);
if (idRecuperado != null) {
this.mantenimientoId = idRecuperado;
this.token = token;
mostrarMensaje();
} else {
mainLyt.add(new H2("El enlace ha expírado o ya fue utilizado."));
mainLyt.setAlignItems(Alignment.CENTER);
}
} else {
mainLyt.add(new H2("Acceso no autorizado."));
mainLyt.setAlignItems(Alignment.CENTER);
}
}
}

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

@ -565,7 +565,7 @@ public class MantenimientoView extends VerticalLayout implements BeforeEnterObse
"</body>" +
"</html>";
String imagePath = "META-INF/resources/images/imgCorreo/correoMantt.png";
String imagePath = "META-INF/resources/images/imgCorreo/MttoRealizado.png";
emailService.enviarCorreo(destinatario, asunto, cuerpo, imagePath);


+ 79
- 21
src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java View File

@ -33,6 +33,7 @@ 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.value.ValueChangeMode;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
@ -60,6 +61,8 @@ import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@PermitAll
@PageTitle("Plan Anual de Mantenimiento")
@ -82,8 +85,10 @@ public class PlanAnualView extends VerticalLayout {
Button btnColumns;
Button btnImprimirRpt;
Button btnAddEquipo;
Button btnEnviarEncuestas;
HorizontalLayout btnImprimirLayout;
private Popover reportePopover;
ComboBox<Integer> yearFilter;
public PlanAnualView(DatabaseService databaseService, Environment env, ReportService reportService) {
this.databaseService = databaseService;
@ -104,11 +109,14 @@ public class PlanAnualView extends VerticalLayout {
GridListDataView<PlanAnual> dataView = planAnualGrid.setItems(planAnualItems);
// Se crea el filtro para el grid.
PlanAnualFilter planAnualFilter = new PlanAnualFilter(dataView);
planAnualFilter.setExcludeRealizado(true);
yearFilter.addValueChangeListener(event ->
planAnualFilter.setYear(event.getValue())
);
HeaderRow headerRow = planAnualGrid.appendHeaderRow();
/*headerRow.getCell(planAnualGrid.getColumnByKey("smtColumnKey"))
.setComponent(createFilterHeader("S.M.T", planAnualFilter::setSmt));*/
@ -218,7 +226,8 @@ public class PlanAnualView extends VerticalLayout {
this.setMargin(false);
this.setSpacing(false);
this.setSizeFull();
add(filtrosLayout, gridLayout, uploadLayout);
add(filtrosLayout, uploadLayout);
add(gridLayout);
}
private void setupHeader() {
@ -262,13 +271,16 @@ public class PlanAnualView extends VerticalLayout {
LocalDate fechaSistema = LocalDate.now();
String nomEquipo = planAnual.getNomEquipo();
String departamento = planAnual.getDepartamento();
String encuesta = planAnual.getEncuesta();
btn.getUI().ifPresent(ui -> ui.navigate(
"mantenimiento?id=" + idPlananual +
"&fecha=" + fechaSistema +
"&tipo=1" +
"&nomEquipo=" + nomEquipo +
"&departamento=" + departamento));
"&departamento=" + departamento +
"&enc=" + encuesta
));
});
} else if ("REALIZADO".equalsIgnoreCase(estado)) {
@ -278,8 +290,12 @@ public class PlanAnualView extends VerticalLayout {
btn.addClickListener(event -> {
int idPlananual = planAnual.getNumero();
String encuesta = planAnual.getEncuesta();
btn.getUI().ifPresent(ui -> ui.navigate(
"detalles?id=" + idPlananual));
"detalles?id=" + idPlananual +
"&enc=" + encuesta
));
});
} else {
btn = new Button("N/A");
@ -318,6 +334,9 @@ public class PlanAnualView extends VerticalLayout {
planAnualGrid.addColumn((ValueProvider<PlanAnual, String>) PlanAnual::getSituacion)
.setHeader("Situación").setAutoWidth(true).setKey("situacion");
planAnualGrid.addColumn((ValueProvider<PlanAnual, String>) PlanAnual::getEncuesta)
.setHeader("Encuesta").setAutoWidth(true).setKey("encuesta");
/*planAnualGrid.addColumn((ValueProvider<PlanAnual, String>) PlanAnual::getSmt)
.setHeader("S.M.T").setKey("smtColumnKey");*/
@ -349,8 +368,29 @@ public class PlanAnualView extends VerticalLayout {
btnAddEquipo.addClickListener(event -> addNuevoEquipo());
btnAddEquipo.setTooltipText("Agregar nuevo equipo");
btnImprimirLayout = new HorizontalLayout(btnColumns, btnImprimirRpt, btnAddEquipo);
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());
yearFilter = new ComboBox<>();
int currentYear = Year.now().getValue();
List<Integer> years = IntStream.rangeClosed(currentYear - 1, currentYear + 4)
.boxed().collect(Collectors.toList());
yearFilter.setItems(years);
yearFilter.setPlaceholder("Año");
yearFilter.setClearButtonVisible(true);
List<PlanAnual> todosLosPlanes = databaseService.getPlanAnual();
btnImprimirLayout = new HorizontalLayout(btnColumns, btnImprimirRpt, btnAddEquipo/*, btnEnviarEncuestas*/, yearFilter);
btnImprimirLayout.setAlignItems(Alignment.BASELINE);
HorizontalLayout columnSelectorLayout = new HorizontalLayout();
columnSelectorLayout.setAlignItems(Alignment.END);
@ -388,12 +428,11 @@ public class PlanAnualView extends VerticalLayout {
});
Set<String> defaultColumns = Set.of("equipo", "departamento", "mesplaneado",
"fechaProgramada", "fechaMantenimiento", "estado", "situacion");
"fechaProgramada", "fechaMantenimiento", "estado", "situacion", "encuesta");
chkColumns.setValue(defaultColumns);
popover.add(heading, chkColumns);
// Cargar datos
planAnualGrid.setItems(databaseService.getPlanAnual());
return planAnualGrid;
}
@ -495,11 +534,14 @@ public class PlanAnualView extends VerticalLayout {
}
private static class PlanAnualFilter {
private final GridListDataView<PlanAnual> dataView;
private String smt;
private String equipo;
private String departamento;
private String mesPlaneado;
private Integer year;
private boolean excludeRealizado = true;
public PlanAnualFilter(GridListDataView<PlanAnual> dataView) {
@ -527,35 +569,51 @@ public class PlanAnualView extends VerticalLayout {
this.dataView.refreshAll();
}
public void setYear(Integer year) {
this.year = year;
dataView.refreshAll();
}
public void setExcludeRealizado(boolean excludeRealizado) {
this.excludeRealizado = excludeRealizado;
this.dataView.refreshAll();
}
public boolean test(PlanAnual planAnual) {
if (planAnual == null) {
return false; // Avoid NullPointerException
}
if (planAnual == null) return false;
boolean matchesSmt = matches(planAnual.getSmt(), smt);
boolean matchesEquipo = matches(planAnual.getNomEquipo(), equipo);
boolean matchesDepartamento = matches(planAnual.getDepartamento(), departamento);
boolean matchesMesPlaneado = matches(planAnual.getMesplaneado(), mesPlaneado);
boolean matchesYear = true;
if (year != null) {
LocalDate fecha = planAnual.getFechaProgramada();
matchesYear = fecha != null && fecha.getYear() == year;
}
boolean matchesEstado;
if (excludeRealizado) {
// Mostrar solo los pendientes
return matchesSmt && matchesEquipo && matchesDepartamento && matchesMesPlaneado
&& (planAnual.getEstado() == null || !"REALIZADO".equalsIgnoreCase(planAnual.getEstado()));
matchesEstado = planAnual.getEstado() == null
|| !"REALIZADO".equalsIgnoreCase(planAnual.getEstado());
} else {
// Mostrar solo realizados
return matchesSmt && matchesEquipo && matchesDepartamento && matchesMesPlaneado
&& "REALIZADO".equalsIgnoreCase(planAnual.getEstado());
matchesEstado =
"REALIZADO".equalsIgnoreCase(planAnual.getEstado()) &&
!"NO REALIZADO".equalsIgnoreCase(planAnual.getSituacion());
}
return matchesSmt
&& matchesEquipo
&& matchesDepartamento
&& matchesMesPlaneado
&& matchesYear
&& matchesEstado;
}
private boolean matches(String value, String searchTerm) {
return searchTerm == null || searchTerm.isEmpty()
|| value.toLowerCase().contains(searchTerm.toLowerCase());
private boolean matches(String value, String serachTerm) {
return serachTerm == null || serachTerm.isEmpty()
|| (value != null && value.toLowerCase().contains(serachTerm.toLowerCase()));
}
}


BIN
src/main/resources/META-INF/resources/images/LOGO_1024X768.jpg View File

Before After
Width: 1025  |  Height: 769  |  Size: 119 KiB

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

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

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

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

+ 4
- 1
src/main/resources/application-dev.properties View File

@ -1,4 +1,7 @@
#Configuracion de la base de datos
db.url=jdbc:mysql://localhost:3307/mantenimientosdb
db.user=mantenimientos
db.pass=mantenimientos
db.pass=mantenimientos
# URL base local
app.base-url=http://localhost:8080

+ 4
- 1
src/main/resources/application-prod.properties View File

@ -2,4 +2,7 @@
db.url=jdbc:mysql://db:3306/mantenimientosdb
#db.url=jdbc:oracle:thin:@//SVRAPPS:1521/XEPDB1
db.user=mantenimientos
db.pass=mantenimientos
db.pass=mantenimientos
# URL base productivo
app.base-url=https://smt.jumapacelaya.gob.mx

Loading…
Cancel
Save