Browse Source

Se ajusto la disposicion del componente grid y tambien se le agrego una columna mas que muestra la fecha en que se creo el ticket y ademas se corrigieron algunas faltas de ortografia

pull/1/head
parent
commit
c6231f8f1e
9 changed files with 128 additions and 113 deletions
  1. +7
    -15
      src/main/java/com/example/application/api/ApiRedmine.java
  2. +45
    -45
      src/main/java/com/example/application/api/RedmineClient.java
  3. +12
    -2
      src/main/java/com/example/application/models/Ticket.java
  4. +4
    -6
      src/main/java/com/example/application/views/MainLayout.java
  5. +4
    -28
      src/main/java/com/example/application/views/crearnuevoticket/CrearnuevoTicketView.java
  6. +1
    -1
      src/main/java/com/example/application/views/login/LoginView.java
  7. +33
    -10
      src/main/java/com/example/application/views/tickets/AllTicketsView.java
  8. +16
    -4
      src/main/java/com/example/application/views/tickets/MisTicketsView.java
  9. +6
    -2
      src/main/resources/application.properties

+ 7
- 15
src/main/java/com/example/application/api/ApiRedmine.java View File

@ -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<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
@ -87,12 +86,10 @@ public class ApiRedmine {
private List<String> parseTicketTypes(String json) {
List<String> 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<String, String> issueDetails, String fileUploadToken) {
return createIssue(issueDetails, fileUploadToken);
}
}

+ 45
- 45
src/main/java/com/example/application/api/RedmineClient.java View File

@ -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<Ticket> parseTickets(String json) {
List<Ticket> 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<Ticket> getTicketsCreatedBy(String username) {
List<Ticket> allTickets = getTickets();
List<Ticket> 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<Ticket> isues;
private List<Ticket> issues;
public List<Ticket> getIssues() {
return isues;
return issues;
}
public void setIssues(List<Ticket> issues) {
this.isues = issues;
this.issues = issues;
}
}
}

+ 12
- 2
src/main/java/com/example/application/models/Ticket.java View File

@ -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;


+ 4
- 6
src/main/java/com/example/application/views/MainLayout.java View File

@ -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;
}


+ 4
- 28
src/main/java/com/example/application/views/crearnuevoticket/CrearnuevoTicketView.java View File

@ -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<String> 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<String, String> map) {
Gson gson = new Gson();
return gson.toJson(map);
}
// Metodo para resetear el formulario
private void resetForm(TextField asunto, TextArea descripcion, ComboBox<String> tipoTickets) {
asunto.clear();
descripcion.clear();


+ 1
- 1
src/main/java/com/example/application/views/login/LoginView.java View File

@ -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();


+ 33
- 10
src/main/java/com/example/application/views/tickets/AllTicketsView.java View File

@ -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<Ticket> tickets = redmineClient.getTickets();
grid.setItems(tickets);
try {
List<Ticket> 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<Span, Ticket> 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;


+ 16
- 4
src/main/java/com/example/application/views/tickets/MisTicketsView.java View File

@ -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


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

@ -19,6 +19,10 @@ password: Dr3na%134$4guA
####################################################
########PRODUCTIVO#################################
redmine.url=https://proyman.jumapacelaya.gob.mx/
redmine.api_key=ad58996f706a48474f1c74368bb35b4614428d90
redmine.api_key=ad58996f706a48474f1c74368bb35b4614428d90
########LOCAL########################################
#redmine.url=http://localhost:3000/
#redmine.api_key=cf3be6168e66c99892c6212ea0bc64e8ab1c6848

Loading…
Cancel
Save