5 Commits

9 changed files with 217 additions and 72 deletions
Split View
  1. BIN
      src/main/bundles/prod.bundle
  2. +77
    -36
      src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java
  3. +12
    -10
      src/main/java/mx/gob/jumapacelaya/api/SecurityConfig.java
  4. +18
    -5
      src/main/java/mx/gob/jumapacelaya/models/Ticket.java
  5. +8
    -4
      src/main/java/mx/gob/jumapacelaya/services/UserService.java
  6. +4
    -7
      src/main/java/mx/gob/jumapacelaya/views/crearnuevoticket/CrearnuevoTicketView.java
  7. +43
    -3
      src/main/java/mx/gob/jumapacelaya/views/tickets/AllTicketsView.java
  8. +54
    -6
      src/main/java/mx/gob/jumapacelaya/views/tickets/MisTicketsView.java
  9. +1
    -1
      src/main/resources/application.properties

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


+ 77
- 36
src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java View File

@ -49,6 +49,7 @@ public class RedmineClient {
String statusFilter = includeClosed ? "&status_id=*" : "&status_id=open";
String responseBody = null;
while (true) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey() + statusFilter + "&offset=" + offset))
@ -58,45 +59,10 @@ public class RedmineClient {
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
responseBody = response.body();
List<Ticket> pageTickets = parseTickets(responseBody);
tickets.addAll(pageTickets);
if (pageTickets.size() < PAGE_SIZE) {
break;
}
offset += PAGE_SIZE;
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
break;
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
System.out.println("Total tickets obtenidos: " + tickets.size());
return tickets;
}
//AQUI OBTENGO LOS TICKETS DESDE REDMINE
public List<Ticket> getTicketsAuthor(RedmineUser user) {
List<Ticket> tickets = new ArrayList<>();
HttpClient client = HttpClient.newHttpClient();
int offset = 0;
while (true) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey() + "&author_id=" + user.getId() + "&offset=" + offset))
.header("Content-Type", "application/json")
.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
List<Ticket> pageTickets = parseTickets(responseBody);
tickets.addAll(pageTickets);
if (pageTickets.size() < PAGE_SIZE) {
break;
}
@ -165,6 +131,7 @@ public class RedmineClient {
}
// Agrega el ticket a la lista
tickets.add(new Ticket(id, subject, description, status, date != null ? date.toString() : "", trackerId));
}
@ -178,6 +145,42 @@ public class RedmineClient {
return tickets;
}
//AQUI OBTENGO LOS TICKETS DESDE REDMINE
public List<Ticket> getTicketsAuthor(RedmineUser user) {
List<Ticket> tickets = new ArrayList<>();
HttpClient client = HttpClient.newHttpClient();
int offset = 0;
while (true) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey() + "&author_id=" + user.getId() + "&offset=" + offset))
.header("Content-Type", "application/json")
.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
List<Ticket> pageTickets = parseTickets(responseBody);
tickets.addAll(pageTickets);
if (pageTickets.size() < PAGE_SIZE) {
break;
}
offset += PAGE_SIZE;
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
break;
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
System.out.println("Total tickets obtenidos: " + tickets.size());
return tickets;
}
@ -439,4 +442,42 @@ public class RedmineClient {
return null;
}
}
public List<String> getTicketComments(int ticketId, RedmineUser user) {
List<String> comments = new ArrayList<>();
HttpClient client = HttpClient.newHttpClient();
String url = REDMINE_URL + "/issues/" + ticketId + ".json?include=journals&key=" + user.getKey();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
JsonObject jsonResponse = JsonParser.parseString(responseBody).getAsJsonObject();
JsonArray journals = jsonResponse.getAsJsonObject("issue").getAsJsonArray("journals");
for (JsonElement journalElement : journals) {
JsonObject journal = journalElement.getAsJsonObject();
if (journal.has("notes") && !journal.get("notes").isJsonNull()) {
String note = journal.get("notes").getAsString().trim();
if (!note.isEmpty()) {
comments.add(note);
}
}
}
} else {
System.err.println("Error en la respuesta al obtener los comentarios: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
return comments;
}
}

+ 12
- 10
src/main/java/mx/gob/jumapacelaya/api/SecurityConfig.java View File

@ -49,20 +49,22 @@ public class SecurityConfig extends VaadinWebSecurity {
)
.formLogin(formLogin -> formLogin
.loginPage("/login")
/*.successHandler((request, response, authentication) -> {
//String username = authentication.getName();
.successHandler((request, response, authentication) -> {
String username = authentication.getName();
log.debug("Configure:loginSuccess: {}", authentication.getName());
if (userService.getAuthenticatedRedmineUser() == null) {
log.debug("Configure:getAuthenticatedRedmineUser Error: {}", authentication.getName());
response.sendRedirect("/login");
} //else {
//response.sendRedirect("");
//}
})*/
var user = userService.getAuthenticatedRedmineUser();
log.debug("Authenticated user: {}", user);
if (user == null) {
log.warn("Usuario autenticado pero no encontrado en Redmine. Redirigiendo a página de error.");
response.sendRedirect("");
} else {
response.sendRedirect("");
}
})
.failureUrl("/login?error=true") // Corrigiendo la URL de fallo
);
super.configure(http);
setLoginView(http, LoginView.class);
}


+ 18
- 5
src/main/java/mx/gob/jumapacelaya/models/Ticket.java View File

@ -4,6 +4,7 @@ package mx.gob.jumapacelaya.models;
import java.sql.Date;
import java.time.LocalDate;
import java.util.List;
public class Ticket {
@ -15,6 +16,9 @@ public class Ticket {
private LocalDate dateCreate;
private User author;
private Integer trackerId;
private List<String> comments;
public Ticket(int id, String subject, String description, String status, String dateCreate, Integer trackerId) {
@ -25,6 +29,7 @@ public class Ticket {
this.dateCreate = LocalDate.parse(dateCreate);
this.author = author;
this.trackerId = trackerId;
this.comments = comments;
}
public int getId() {
@ -86,18 +91,26 @@ public class Ticket {
}
switch (trackerId) {
case 1,3,4,7:
case 5,7,8,11,12:
return "1-2 dias Max";
case 2,12:
case 2,14,16:
return "2 hrs Max";
case 5:
case 10:
return "5 dias aprox.";
case 8,11:
case 15:
return "2-6 hrs Max";
case 9,10:
case 6,13:
return "2 hrs Max";
default:
return "N/A";
}
}
public List<String> getComments() {
return comments;
}
public void setComments(List<String> comments) {
this.comments = comments;
}
}

+ 8
- 4
src/main/java/mx/gob/jumapacelaya/services/UserService.java View File

@ -1,5 +1,7 @@
package mx.gob.jumapacelaya.services;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.server.VaadinService;
import mx.gob.jumapacelaya.api.RedmineClient;
import mx.gob.jumapacelaya.api.SecurityService;
@ -39,8 +41,8 @@ public class UserService {
CustomUserDetails userDetails = ldapService.getUserDetails(username);
if (userDetails != null) {
if (userDetails.getEmail() == null || userDetails.getEmail().isEmpty()) {
logger.error("El usuario: " + username + " no tiene correo electronico.");
return null;
logger.warn("El usuario {} no tiene correo electrónico. Asignando uno por defecto.", username);
userDetails.setEmail(username + "@jumapacelaya.gob.mx"); // Asigna un correo por defecto
}
RedmineUser newUser = RedmineClient.createRedmineUser(
username,
@ -48,6 +50,7 @@ public class UserService {
userDetails.getLastName(),
userDetails.getEmail()
);
if (newUser != null) {
logger.info("Usuario creado en Redmine: " + newUser);
return newUser;
@ -89,8 +92,9 @@ public class UserService {
}
}
}else{
logger.error("No se pudo obtener al usuario autenticado en Redmine");
securityService.logout();
logger.error("No se pudo obtener al usuario autenticado en Redmine. Permitiendo acceso limitado");
Notification.show("No se pudo obtener al usuario autenticado en Redmine. Permitiendo acceso limitado")
.addThemeVariants(NotificationVariant.LUMO_WARNING);
}
}
return userclient;


+ 4
- 7
src/main/java/mx/gob/jumapacelaya/views/crearnuevoticket/CrearnuevoTicketView.java View File

@ -31,10 +31,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Route(value = "", layout = MainLayout.class)
@PermitAll
@ -159,13 +156,13 @@ public class CrearnuevoTicketView extends VerticalLayout {
tipoTickets.setPlaceholder("Seleccione un tipo de ticket");
Map<String, RedmineClient.TicketType> ticketTypesMap = api.getTicketTypes();
Set<String> ticketTypesSet = Set.of("Acceso/Permiso/Bajas", "Soporte de Software", "Capacitacion de Software",
/*Set<String> ticketTypesSet = Set.of("Acceso/Permiso/Bajas", "Soporte de Software", "Capacitacion de Software",
"Configuracion de Software", "Digitalizacion GIS", "Documento", "Funcionalidad", "Reporte",
"Mantenimiento Correctivo", "Desarrollo de Software", "Actividad","Entrega de Consumibles","Instalación/Configuracion GIS");
List<String> filteredTicketTypes = ticketTypesMap.keySet().stream()
.filter(ticketTypesSet::contains)
.collect(Collectors.toList());
tipoTickets.setItems(filteredTicketTypes);
.collect(Collectors.toList());*/
tipoTickets.setItems(ticketTypesMap.keySet());
tipoTickets.addValueChangeListener(event -> {
String selectedType = event.getValue();
@ -240,7 +237,7 @@ public class CrearnuevoTicketView extends VerticalLayout {
issueDetails.put("project_id", "soporte-tecnico-t-i");
issueDetails.put("subject", asunto.getValue());
String areaValue = area.getValue().trim();
String descriptionCompleta = descripcion.getValue() + ("Area: " + areaValue + "\n\n"); // Concatenamos el valor del campo Area a la descripcion del ticket
String descriptionCompleta = descripcion.getValue() + (" \n" + " Area: " + areaValue + "\n\n"); // Concatenamos el valor del campo Area a la descripcion del ticket
issueDetails.put("description", descriptionCompleta);
issueDetails.put("tracker_id", selectedTrackerId);


+ 43
- 3
src/main/java/mx/gob/jumapacelaya/views/tickets/AllTicketsView.java View File

@ -6,6 +6,8 @@ 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.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
@ -63,9 +65,10 @@ public class AllTicketsView extends VerticalLayout {
grid.addColumn(ticket -> ticket.tiempoEst(ticket.getTrackerId())).setHeader("Tiempo estimado de atencion").setAutoWidth(false);
grid.addComponentColumn(ticket -> {
Button btnVer = new Button("Ver");
Button btnVer = new Button("Ver", new Icon(VaadinIcon.EYE));
btnVer.setTooltipText("Ver descripción");
btnVer.addClickListener(event -> showDescription(ticket));
btnVer.getStyle().set("color", "#691b31");
btnVer.getStyle().set("color", "#A02142");
return btnVer;
}).setHeader("Descripcion").setAutoWidth(true);
@ -98,11 +101,15 @@ public class AllTicketsView extends VerticalLayout {
textEditor.setValue(ticket.getDescription());
textEditor.setReadOnly(true);
Button verNotas = new Button("Comentarios");
verNotas.addClickListener(event -> showComents(ticket));
Button closeButton = new Button("Cerrar");
closeButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
closeButton.addClickListener(e -> dialog.close());
HorizontalLayout buttonLayout = new HorizontalLayout(closeButton);
HorizontalLayout buttonLayout = new HorizontalLayout(verNotas, closeButton);
buttonLayout.setWidthFull();
buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
buttonLayout.add(closeButton);
@ -112,6 +119,39 @@ public class AllTicketsView extends VerticalLayout {
dialog.open();
}
private void showComents(Ticket ticket) {
Dialog comentDialog = new Dialog();
comentDialog.getElement().setAttribute("arial-label", "Comentarios del ticket");
comentDialog.setMaxHeight("500px");
comentDialog.setMaxWidth("1100px");
VerticalLayout layout = new VerticalLayout();
layout.setPadding(true);
layout.setSpacing(true);
// Obtenemos los comentarios desde Proyman
List<String> comentarios = redmineClient.getTicketComments(ticket.getId(), userService.getRedmineUser());
if (comentarios != null && !comentarios.isEmpty()) {
for (String comentario : comentarios) {
Span commentSpan = new Span(comentario);
commentSpan.getElement().getStyle().set("background", "#f1f1f1");
commentSpan.getElement().getStyle().set("padding", "10px");
commentSpan.getElement().getStyle().set("border-radius", "5px");
layout.add(commentSpan);
}
} else {
layout.add(new Span("No hay comentarios para este ticket."));
}
Button closeButton = new Button("Cerrar", event -> comentDialog.close());
closeButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
layout.add(closeButton);
comentDialog.add(layout);
comentDialog.open();
}
private void loadTickets() {
try {
List<Ticket> tickets = redmineClient.getTickets(userService.getRedmineUser(), true);


+ 54
- 6
src/main/java/mx/gob/jumapacelaya/views/tickets/MisTicketsView.java View File

@ -6,6 +6,8 @@ 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.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
@ -68,9 +70,10 @@ public class MisTicketsView extends VerticalLayout {
grid.addColumn(ticket -> ticket.tiempoEst(ticket.getTrackerId())).setHeader("Tiempo estimado de atencion").setAutoWidth(false);
grid.addComponentColumn(ticket -> {
Button btnVer = new Button("Ver");
Button btnVer = new Button("Ver", new Icon(VaadinIcon.EYE));
btnVer.setTooltipText("Ver descripción");
btnVer.addClickListener(event -> showDescription(ticket));
btnVer.getStyle().set("color", "#691b31");
btnVer.getStyle().set("color", "#A02142");
return btnVer;
}).setHeader("Descripcion").setAutoWidth(true);
@ -102,11 +105,14 @@ public class MisTicketsView extends VerticalLayout {
textEditor.setValue(ticket.getDescription());
textEditor.setReadOnly(true);
Button verNotas = new Button("Comentarios");
verNotas.addClickListener(event -> showComents(ticket));
Button closeButton = new Button("Cerrar");
closeButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
closeButton.addClickListener(e -> dialog.close());
HorizontalLayout buttonLayout = new HorizontalLayout(closeButton);
HorizontalLayout buttonLayout = new HorizontalLayout(verNotas, closeButton);
buttonLayout.setWidthFull();
buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
buttonLayout.add(closeButton);
@ -116,6 +122,39 @@ public class MisTicketsView extends VerticalLayout {
dialog.open();
}
private void showComents(Ticket ticket) {
Dialog comentDialog = new Dialog();
comentDialog.getElement().setAttribute("arial-label", "Comentarios del ticket");
comentDialog.setMaxHeight("500px");
comentDialog.setMaxWidth("1100px");
VerticalLayout layout = new VerticalLayout();
layout.setPadding(true);
layout.setSpacing(true);
// Obtenemos los comentarios desde Proyman
List<String> comentarios = redmineClient.getTicketComments(ticket.getId(), userService.getRedmineUser());
if (comentarios != null && !comentarios.isEmpty()) {
for (String comentario : comentarios) {
Span commentSpan = new Span(comentario);
commentSpan.getElement().getStyle().set("background", "#f1f1f1");
commentSpan.getElement().getStyle().set("padding", "10px");
commentSpan.getElement().getStyle().set("border-radius", "5px");
layout.add(commentSpan);
}
} else {
layout.add(new Span("No hay comentarios para este ticket."));
}
Button closeButton = new Button("Cerrar", event -> comentDialog.close());
closeButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
layout.add(closeButton);
comentDialog.add(layout);
comentDialog.open();
}
private void loadTickets() {
try {
List<Ticket> tickets = redmineClient.getTicketsAuthor(userService.getRedmineUser());
@ -132,13 +171,22 @@ public class MisTicketsView extends VerticalLayout {
Span span = new Span(ticket.getStatus());
switch (ticket.getStatus().toLowerCase()) {
case "análisis":
span.getElement().getStyle().set("color","purple");
span.getElement().getStyle().set("color","orange");
break;
case "desarrollo":
span.getElement().getStyle().set("color","green");
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");
break;
default:
span.getElement().getStyle().set("color","blue");
span.getElement().getStyle().set("color","green");
break;
}
return span;


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

@ -34,4 +34,4 @@ spring.servlet.multipart.max-request-size=10MB
##############TIEMPO DE DURACION DE LA SESION#################
server.servlet.session.timeout=30m
vaadin.session.expiration=30m
server.servlet.session.cookie.secure=true

Loading…
Cancel
Save