diff --git a/src/main/java/com/example/application/api/ApiRedmine.java b/src/main/java/com/example/application/api/ApiRedmine.java index 52db0e1..76d0262 100644 --- a/src/main/java/com/example/application/api/ApiRedmine.java +++ b/src/main/java/com/example/application/api/ApiRedmine.java @@ -1,6 +1,9 @@ package com.example.application.api; -import com.nimbusds.jose.shaded.gson.*; + +import com.nimbusds.jose.shaded.gson.Gson; +import com.nimbusds.jose.shaded.gson.JsonObject; +import com.nimbusds.jose.shaded.gson.JsonParser; import java.net.URI; import java.net.http.HttpClient; @@ -12,8 +15,6 @@ import java.util.List; import java.util.Map; public class ApiRedmine { - //private static final String REDMINE_URL = "http://localhost:3000"; - //private static final String API_KEY = "cf3be6168e66c99892c6212ea0bc64e8ab1c6848"; public static final Gson GSON = new Gson(); static String REDMINE_URL; static String API_KEY; @@ -42,7 +43,6 @@ public class ApiRedmine { String jsonPayload = GSON.toJson(payload); try { - // Crear la solicitud HTTP POST HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(REDMINE_URL + "/issues.json")) .header("Content-Type", "application/json") @@ -50,7 +50,6 @@ public class ApiRedmine { .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) .build(); - // Enviar la solicitud HttpClient client = HttpClient.newHttpClient(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); @@ -87,12 +86,10 @@ public class ApiRedmine { private List parseTicketTypes(String json) { List names = new ArrayList<>(); JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject(); - JsonArray trackers = jsonObject.getAsJsonArray("trackers"); - for (JsonElement trackerElement : trackers) { + jsonObject.getAsJsonArray("trackers").forEach(trackerElement -> { JsonObject tracker = trackerElement.getAsJsonObject(); - String name = tracker.get("name").getAsString(); - names.add(name); - } + names.add(tracker.get("name").getAsString()); + }); return names; } @@ -121,9 +118,4 @@ public class ApiRedmine { return null; } } - - public static String createIssueWithAttachment(Map issueDetails, String fileUploadToken) { - return createIssue(issueDetails, fileUploadToken); - } - } diff --git a/src/main/java/com/example/application/api/RedmineClient.java b/src/main/java/com/example/application/api/RedmineClient.java index 1eb2565..b8c2ae1 100644 --- a/src/main/java/com/example/application/api/RedmineClient.java +++ b/src/main/java/com/example/application/api/RedmineClient.java @@ -10,20 +10,19 @@ 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.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -/*Esta clase se encarga de obtener los tickets desde redmine -para poder mostrarlo en la vista de visualizacion de tickets*/ - public class RedmineClient { private static final int PAGE_SIZE = 25; static String REDMINE_URL; static String API_KEY; - public RedmineClient(String redmineUrl, String apiKey) { REDMINE_URL = redmineUrl; API_KEY = apiKey; @@ -65,63 +64,64 @@ public class RedmineClient { private List parseTickets(String json) { List tickets = new ArrayList<>(); - JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject(); - JsonArray issues = jsonObject.getAsJsonArray("issues"); - for (JsonElement issueElement : issues) { - JsonObject issue = issueElement.getAsJsonObject(); - System.out.println(issue); - int id = issue.get("id").getAsInt(); - String subject = issue.get("subject").getAsString(); - String description = issue.has("description") ? issue.get("description").getAsString() : ""; - String status = issue.getAsJsonObject("status").get("name").getAsString(); - - //Aqui se obtiene el nombre del autor quien crea el ticket - JsonObject authorJson = issue.getAsJsonObject("author"); - Ticket.User author = null; - if (authorJson != null) { - System.out.println("Author JSON: " + authorJson); - JsonElement nameElement = authorJson.get("name"); - if (nameElement != null) { - String username = nameElement.getAsString(); - System.out.println("Autor del ticket: " + username); - author = new Ticket.User(username); - } else { - System.out.println("Campo name no encontrado en el autor: " + authorJson); + try { + JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject(); + JsonArray issues = jsonObject.getAsJsonArray("issues"); + if (issues != null) { + for (JsonElement issueElement : issues) { + JsonObject issue = issueElement.getAsJsonObject(); + int id = issue.has("id") ? issue.get("id").getAsInt() : 0; + String subject = issue.has("subject") ? issue.get("subject").getAsString() : ""; + String description = issue.has("description") ? issue.get("description").getAsString() : ""; + String status = issue.has("status") ? issue.getAsJsonObject("status").get("name").getAsString() : "Unknown"; + String dateString = issue.has("created_on") ? issue.get("created_on").getAsString() : ""; + + LocalDate date = null; + if (!dateString.isEmpty()) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; + date = LocalDate.parse(dateString, formatter); + } catch (DateTimeParseException e) { + System.err.println("Error al parsear la fecha: " + dateString); + e.printStackTrace(); + } + } + + JsonObject authorJson = issue.has("author") ? issue.getAsJsonObject("author") : null; + Ticket.User author = null; + if (authorJson != null) { + String username = authorJson.has("name") ? authorJson.get("name").getAsString() : "Unknown"; + author = new Ticket.User(username); + } + + tickets.add(new Ticket(id, subject, description, status, date != null ? date.toString() : "", author)); } - }else { - System.out.println("Autor es NULL para el ticket id: " + id); + } else { + System.out.println("La respuesta JSON no contiene la clave 'issues'"); } - tickets.add(new Ticket(id, subject, description, status, author)); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Ocurrió un error al parsear los tickets: " + e.getMessage()); } return tickets; } public List getTicketsCreatedBy(String username) { List allTickets = getTickets(); - List filteredTickets = allTickets.stream() - .filter( ticket -> { - if (ticket.getAuthor() != null) { - String ticketUsername = ticket.getAuthor().getUsername(); - System.out.println("Comparando: " + username + " con " + ticketUsername); - return username.equals(ticketUsername); - } - return false; - }) - .collect(Collectors.toList()); - System.out.println("Total de tickets creados por " + username + ": " + filteredTickets.size()); - return filteredTickets; + return allTickets.stream() + .filter(ticket -> ticket.getAuthor() != null && username.equals(ticket.getAuthor().getUsername())) + .collect(Collectors.toList()); } - private static class TicketResponse { - private List isues; + private List issues; public List getIssues() { - return isues; + return issues; } public void setIssues(List issues) { - this.isues = issues; + this.issues = issues; } } } diff --git a/src/main/java/com/example/application/models/Ticket.java b/src/main/java/com/example/application/models/Ticket.java index ac27b50..a813f61 100644 --- a/src/main/java/com/example/application/models/Ticket.java +++ b/src/main/java/com/example/application/models/Ticket.java @@ -2,6 +2,9 @@ package com.example.application.models; /*Esta clase obtiene los detalles de los tickets*/ +import java.sql.Date; +import java.time.LocalDate; + public class Ticket { @@ -9,15 +12,17 @@ public class Ticket { private String subject; private String description; private String status; + private LocalDate dateCreate; private User author; - public Ticket(int id, String subject, String description, String status, User author) { + + public Ticket(int id, String subject, String description, String status, String dateCreate, User author) { this.id = id; this.subject = subject; this.description = description; this.status = status; + this.dateCreate = LocalDate.parse(dateCreate); this.author = author; - } public int getId() { @@ -44,6 +49,11 @@ public class Ticket { this.author = author; } + public Date getDateCreate() { + return java.sql.Date.valueOf(this.dateCreate); + } + + public static class User { private String username; diff --git a/src/main/java/com/example/application/views/MainLayout.java b/src/main/java/com/example/application/views/MainLayout.java index cd003d2..7ddcf68 100644 --- a/src/main/java/com/example/application/views/MainLayout.java +++ b/src/main/java/com/example/application/views/MainLayout.java @@ -25,6 +25,7 @@ import org.vaadin.lineawesome.LineAwesomeIcon; /** * The main view is a top-level placeholder for other views. */ +//@PWA(name = "My Application", shortName = "My App", iconPath = "icons/960x960.png", backgroundColor = "#233348", themeColor = "#233348") public class MainLayout extends AppLayout { private H2 viewTitle; @@ -47,7 +48,7 @@ public class MainLayout extends AppLayout { String u = securityService.getAuthenticatedUser().getUsername(); Span usrNameLabel = new Span("Hola " + u); - Button logoutButton = new Button("Cerrar sesion", event -> { + Button logoutButton = new Button("Cerrar sesión", event -> { securityService.logout(); }); logoutButton.getStyle().set("margin-right", "50px"); @@ -58,8 +59,7 @@ public class MainLayout extends AppLayout { headerContent.setWidthFull(); headerContent.add(viewTitle); headerContent.setFlexGrow(1, viewTitle); - headerContent.add(usrNameLabel,logoutButton); - + headerContent.add(usrNameLabel, logoutButton); addToNavbar(true, toggle, headerContent); } @@ -70,7 +70,7 @@ public class MainLayout extends AppLayout { headerLayout.setSpacing(false); headerLayout.setAlignItems(FlexComponent.Alignment.CENTER); - Image imgLogo = new Image("images/1027x160.png","Logo"); + Image imgLogo = new Image("images/1027x160.png", "Logo"); imgLogo.setWidth("300px"); headerLayout.add(imgLogo); @@ -87,13 +87,11 @@ public class MainLayout extends AppLayout { nav.addItem(new SideNavItem("Mis tickets", MisTicketsView.class, LineAwesomeIcon.TICKET_ALT_SOLID.create())); nav.addItem(new SideNavItem("Todos los tickets", AllTicketsView.class, LineAwesomeIcon.LIST_ALT.create())); - return nav; } private Footer createFooter() { Footer layout = new Footer(); - return layout; } diff --git a/src/main/java/com/example/application/views/crearnuevoticket/CrearnuevoTicketView.java b/src/main/java/com/example/application/views/crearnuevoticket/CrearnuevoTicketView.java index 11af4c7..3d9a3fc 100644 --- a/src/main/java/com/example/application/views/crearnuevoticket/CrearnuevoTicketView.java +++ b/src/main/java/com/example/application/views/crearnuevoticket/CrearnuevoTicketView.java @@ -3,7 +3,6 @@ package com.example.application.views.crearnuevoticket; import com.example.application.api.ApiRedmine; import com.example.application.api.ServerPrpperties; import com.example.application.views.MainLayout; -import com.nimbusds.jose.shaded.gson.Gson; import com.nimbusds.jose.shaded.gson.JsonObject; import com.nimbusds.jose.shaded.gson.JsonParser; import com.vaadin.flow.component.button.Button; @@ -55,17 +54,7 @@ public class CrearnuevoTicketView extends VerticalLayout { // Campo para adjuntar archivos buffer = new MemoryBuffer(); uploadFile = new Upload(buffer); - uploadFile.setUploadButton(new Button("Cargar archivo")); - uploadFile.setMaxFiles(1); - uploadFile.addFailedListener(event -> { - Notification.show("Error al cargar el archivo: " + event.getReason().getMessage(), 5000, Notification.Position.MIDDLE); - }); - - // Respuestas Json para verificar posibles errores al enviar los nuevos tickets no visibles en la interfaz - TextArea jsonOutput = new TextArea("JSON Output"); - jsonOutput.setReadOnly(true); - TextArea responseField = new TextArea("Response"); - responseField.setReadOnly(true); + uploadFile.setAcceptedFileTypes("image/jpeg", "image/png", "application/pdf"); // Boton para crear los tickets Button createButton = new Button("Enviar ticket", event -> { @@ -86,26 +75,20 @@ public class CrearnuevoTicketView extends VerticalLayout { } } - String response; - if (fileUploadToken != null) { - response = ApiRedmine.createIssue(issueDetails, fileUploadToken); - } else { - response = ApiRedmine.createIssue(issueDetails, fileUploadToken); - } + String response = ApiRedmine.createIssue(issueDetails, fileUploadToken); handleResponse(response, asunto, descripcion, tipoTickets); }); createButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); - VerticalLayout fieldsLayout = new VerticalLayout(descripcion); + VerticalLayout fieldsLayout = new VerticalLayout(descripcion, uploadFile); fieldsLayout.setAlignItems(Alignment.CENTER); HorizontalLayout firstFields = new HorizontalLayout(tipoTickets, asunto); VerticalLayout buttonLayout = new VerticalLayout(createButton); buttonLayout.setAlignItems(Alignment.END); buttonLayout.setMargin(true); - VerticalLayout uploadLayout = new VerticalLayout(uploadFile); - add(new H2("Crear nuevo ticket"), firstFields, fieldsLayout, uploadLayout, buttonLayout/*,jsonOutput,responseField*/); + add(new H2("Crear nuevo ticket"), firstFields, fieldsLayout, buttonLayout); } private void handleResponse(String response, TextField asunto, TextArea descripcion, ComboBox tipoTickets) { @@ -122,13 +105,6 @@ public class CrearnuevoTicketView extends VerticalLayout { } } - // Metodo para convertir un Map a json usando la libreria GSON - private static String mapToJson(Map map) { - Gson gson = new Gson(); - return gson.toJson(map); - } - - // Metodo para resetear el formulario private void resetForm(TextField asunto, TextArea descripcion, ComboBox tipoTickets) { asunto.clear(); descripcion.clear(); diff --git a/src/main/java/com/example/application/views/login/LoginView.java b/src/main/java/com/example/application/views/login/LoginView.java index 464e217..4c9581d 100644 --- a/src/main/java/com/example/application/views/login/LoginView.java +++ b/src/main/java/com/example/application/views/login/LoginView.java @@ -31,7 +31,7 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver { i18nFormulario.setTitle("Sistema de Soporte Técnico Gcia. de T.I"); i18nFormulario.setUsername("Usuario"); i18nFormulario.setPassword("Contraseña"); - i18nFormulario.setSubmit("Iniciar sesion"); + i18nFormulario.setSubmit("Iniciar sesión"); i18n.setForm(i18nFormulario); formularioLogin = new LoginForm(); diff --git a/src/main/java/com/example/application/views/tickets/AllTicketsView.java b/src/main/java/com/example/application/views/tickets/AllTicketsView.java index 6b5d463..a46bdca 100644 --- a/src/main/java/com/example/application/views/tickets/AllTicketsView.java +++ b/src/main/java/com/example/application/views/tickets/AllTicketsView.java @@ -13,6 +13,8 @@ import com.vaadin.flow.router.Route; import com.vaadin.flow.server.auth.AnonymousAllowed; import org.springframework.beans.factory.annotation.Autowired; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; @Route(value="tickets", layout = MainLayout.class) @@ -28,14 +30,25 @@ public class AllTicketsView extends VerticalLayout { this.redmineClient = new RedmineClient(prpperties.REDMINE_URL, prpperties.API_KEY); this.grid = new Grid<>(Ticket.class, false); + + // Configuración de columnas del grid grid.addColumn(Ticket::getId).setHeader("No.") - .setWidth("4em").setFlexGrow(0); + .setAutoWidth(true).setFlexGrow(0).setSortable(true); grid.addColumn(Ticket::getSubject).setHeader("Asunto") - .setAutoWidth(true).setFlexGrow(1); + .setWidth("20em").setFlexGrow(0); grid.addColumn(createStatusRender()).setHeader("Estado") - .setWidth("7em").setFlexGrow(1); + .setWidth("8em").setFlexGrow(0); + grid.addColumn(ticket -> { + Date date = ticket.getDateCreate(); + if (date != null) { + SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy"); + return formatter.format(date); + } else { + return ""; + } + }).setHeader("Fecha Creación").setWidth("10em").setFlexGrow(1); grid.addColumn(Ticket::getDescription).setHeader("Descripción") - .setAutoWidth(true).setFlexGrow(1); + .setWidth("45em").setFlexGrow(1); // Ajustar tamaño del Grid y Layout @@ -48,12 +61,22 @@ public class AllTicketsView extends VerticalLayout { add(grid); expand(grid); + // Cargar tickets loadTickets(); } private void loadTickets() { - List tickets = redmineClient.getTickets(); - grid.setItems(tickets); + try { + List tickets = redmineClient.getTickets(); + if (tickets != null && !tickets.isEmpty()) { + grid.setItems(tickets); + } else { + System.out.println("No se pudieron obtener tickets, la lista de tickets es null o está vacía"); + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Ocurrió un error al cargar los tickets: " + e.getMessage()); + } } private ComponentRenderer createStatusRender() { @@ -61,16 +84,16 @@ public class AllTicketsView extends VerticalLayout { Span span = new Span(ticket.getStatus()); switch (ticket.getStatus().toLowerCase()) { case "en curso": - span.getElement().getStyle().set("color","purple"); + span.getElement().getStyle().set("color", "purple"); break; case "comentarios": - span.getElement().getStyle().set("color","orange"); + span.getElement().getStyle().set("color", "orange"); break; case "resuelta": - span.getElement().getStyle().set("color","green"); + span.getElement().getStyle().set("color", "green"); break; default: - span.getElement().getStyle().set("color","blue"); + span.getElement().getStyle().set("color", "blue"); break; } return span; diff --git a/src/main/java/com/example/application/views/tickets/MisTicketsView.java b/src/main/java/com/example/application/views/tickets/MisTicketsView.java index b0ce3ac..31e09c8 100644 --- a/src/main/java/com/example/application/views/tickets/MisTicketsView.java +++ b/src/main/java/com/example/application/views/tickets/MisTicketsView.java @@ -13,6 +13,8 @@ import com.vaadin.flow.router.Route; import com.vaadin.flow.server.auth.AnonymousAllowed; import org.springframework.beans.factory.annotation.Autowired; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; @Route(value="mytickets", layout = MainLayout.class) @@ -28,14 +30,24 @@ public class MisTicketsView extends VerticalLayout { this.redmineClient = new RedmineClient(prpperties.REDMINE_URL, prpperties.API_KEY); this.grid = new Grid<>(Ticket.class, false); + // Configuración de columnas del grid grid.addColumn(Ticket::getId).setHeader("No.") - .setWidth("4em").setFlexGrow(0); + .setAutoWidth(true).setFlexGrow(0).setSortable(true); grid.addColumn(Ticket::getSubject).setHeader("Asunto") - .setAutoWidth(true).setFlexGrow(1); + .setWidth("20em").setFlexGrow(0); grid.addColumn(createStatusRender()).setHeader("Estado") - .setWidth("7em").setFlexGrow(1); + .setWidth("8em").setFlexGrow(0); + grid.addColumn(ticket -> { + Date date = ticket.getDateCreate(); + if (date != null) { + SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy"); + return formatter.format(date); + } else { + return ""; + } + }).setHeader("Fecha Creación").setWidth("10em").setFlexGrow(1); grid.addColumn(Ticket::getDescription).setHeader("Descripción") - .setAutoWidth(true).setFlexGrow(1); + .setWidth("45em").setFlexGrow(1); // Ajustar tamaño del Grid y Layout diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 48c0a48..5ba3a08 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -19,6 +19,10 @@ password: Dr3na%134$4guA -#################################################### +########PRODUCTIVO################################# redmine.url=https://proyman.jumapacelaya.gob.mx/ -redmine.api_key=ad58996f706a48474f1c74368bb35b4614428d90 \ No newline at end of file +redmine.api_key=ad58996f706a48474f1c74368bb35b4614428d90 + +########LOCAL######################################## +#redmine.url=http://localhost:3000/ +#redmine.api_key=cf3be6168e66c99892c6212ea0bc64e8ab1c6848 \ No newline at end of file