@ -22,8 +22,10 @@ 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.page.Push ;
import com.vaadin.flow.component.popover.Popover ;
import com.vaadin.flow.component.popover.PopoverPosition ;
import com.vaadin.flow.component.progressbar.ProgressBar ;
import com.vaadin.flow.component.richtexteditor.RichTextEditor ;
import com.vaadin.flow.data.renderer.ComponentRenderer ;
import com.vaadin.flow.function.SerializableBiConsumer ;
@ -91,7 +93,8 @@ public class ActDiariaView extends VerticalLayout {
/ / Layout de opciones
opcionesLyt = new HorizontalLayout ( ) ;
opcionesLyt . setWidthFull ( ) ;
opcionesLyt . setMargin ( false ) ;
opcionesLyt . setAlignItems ( Alignment . BASELINE ) ;
/ / opcionesLyt . setMargin ( false ) ;
opcionesLyt . getStyle ( )
. set ( "box-shadow" , "0 4px 8px rgba(0,0,0,0.2)" )
. set ( "border-radius" , "10px" )
@ -103,18 +106,20 @@ public class ActDiariaView extends VerticalLayout {
chkSoloAbiertos = new Checkbox ( "Ver solo abiertos" ) ;
chkSoloAbiertos . setValue ( false ) ; / / inicial : mostrar cerrados
fechaDesde = new DatePicker ( "Fecha desde:" ) ;
fechaHasta = new DatePicker ( "Fecha hasta:" ) ;
btnBuscar = new Button ( "Buscar" ) ;
fechaDesde = new DatePicker ( ) ;
fechaDesde . setPlaceholder ( "Fecha desde:" ) ;
fechaHasta = new DatePicker ( ) ;
fechaHasta . setPlaceholder ( "Fecha hasta:" ) ;
btnBuscar = new Button ( "Buscar" , VaadinIcon . SEARCH . create ( ) ) ;
/ / Listener del checkbox
chkSoloAbiertos . addValueChangeListener ( e - > {
boolean soloAbiertos = e . getValue ( ) ; / / marcado = abiertos
loadTickets ( soloAbiertos ) ;
fechaDesde . setEnabled ( ! soloAbiertos ) ;
fechaHasta . setEnabled ( ! soloAbiertos ) ;
btnBuscar . setEnabled ( ! soloAbiertos ) ;
grid . getDataProvider ( ) . refreshAll ( ) ;
} ) ;
/ / Estado inicial
@ -128,6 +133,30 @@ public class ActDiariaView extends VerticalLayout {
opcionesLyt . add ( chkSoloAbiertos , fechaDesde , fechaHasta , btnBuscar ) ;
grid . setItems (
query - > {
try {
return redmineClient . getTickets (
userService . getRedmineUser ( ) ,
chkSoloAbiertos . getValue ( ) ,
query . getOffset ( ) ,
query . getLimit ( ) ,
fechaDesde . getValue ( ) ,
fechaHasta . getValue ( )
) . stream ( ) ;
} catch ( Exception e ) {
log . error ( "Error al cargar tickets" , e ) ;
return Stream . empty ( ) ;
}
} ,
query - > redmineClient . getTotalTickets (
userService . getRedmineUser ( ) ,
chkSoloAbiertos . getValue ( ) ,
fechaDesde . getValue ( ) ,
fechaHasta . getValue ( )
)
) ;
/ / Configuración de columnas del grid
grid . addColumn ( Ticket : : getId ) . setHeader ( "No." )
. setAutoWidth ( true )
@ -148,6 +177,7 @@ public class ActDiariaView extends VerticalLayout {
return fecha ! = null ? fecha . format ( DateTimeFormatter . ofPattern ( "dd/MM/yyyy HH:mm:ss" ) ) : "" ;
} ) . setHeader ( "Fecha creación" )
. setAutoWidth ( true )
. setSortable ( true )
. setKey ( "fechaCreacion" ) ;
grid . addColumn ( ticket - > {
@ -155,6 +185,7 @@ public class ActDiariaView extends VerticalLayout {
return fecha ! = null ? fecha . format ( DateTimeFormatter . ofPattern ( "dd/MM/yyyy HH:mm:ss" ) ) : "" ;
} ) . setHeader ( "Fecha cierre" )
. setAutoWidth ( true )
. setSortable ( true )
. setKey ( "fechaCierre" ) ;
grid . addColumn ( ticket - > {
@ -198,7 +229,7 @@ public class ActDiariaView extends VerticalLayout {
} else {
return "" ;
}
} ) . setHeader ( "Duración" ) . setAutoWidth ( true ) . setKey ( "duracion" ) ;
} ) . setHeader ( "Duración" ) . setAutoWidth ( true ) . setSortable ( true ) . set Key ( "duracion" ) ;
grid . addColumn ( ticket - >
@ -229,6 +260,7 @@ public class ActDiariaView extends VerticalLayout {
btnColumns = new Button ( VaadinIcon . GRID_H . create ( ) ) ;
/ / Botón para exportar
btnExport = new Button ( "Exportar" , LineAwesomeIcon . FILE_EXCEL . create ( ) ) ;
btnExport . addThemeVariants ( ButtonVariant . LUMO_PRIMARY ) ;
btnExport . addClickListener ( e - > exportTicketsCerrados ( ) ) ;
showColumnsLyt = new HorizontalLayout ( btnExport , btnColumns ) ;
showColumnsLyt . setJustifyContentMode ( JustifyContentMode . END ) ;
@ -281,30 +313,6 @@ public class ActDiariaView extends VerticalLayout {
/ / Invertir parámetro para que coincida con RedmineClient
boolean paramCliente = ! soloAbiertos ;
grid . setItems (
query - > {
try {
return redmineClient . getTickets (
user ,
chkSoloAbiertos . getValue ( ) ,
query . getOffset ( ) ,
query . getLimit ( ) ,
fechaDesde . getValue ( ) ,
fechaHasta . getValue ( )
) . stream ( ) ;
} catch ( Exception e ) {
e . printStackTrace ( ) ;
return Stream . empty ( ) ;
}
} ,
query - > redmineClient . getTotalTickets (
user ,
chkSoloAbiertos . getValue ( ) ,
fechaDesde . getValue ( ) ,
fechaHasta . getValue ( )
)
) ;
}
@ -399,85 +407,131 @@ public class ActDiariaView extends VerticalLayout {
/ / Metodo para exportar los tickets cerrados a un archivo de Excel
private void exportTicketsCerrados ( ) {
try {
RedmineUser user = userService . getRedmineUser ( ) ;
/ / Traer TODOS los tickets cerrados , respetando fechas si están seleccionadas
List < Ticket > ticketsCerrados = redmineClient . getAllTickets (
user ,
false , / / false = cerrados
fechaDesde . getValue ( ) ,
fechaHasta . getValue ( )
) ;
Workbook workbook = new XSSFWorkbook ( ) ;
Sheet sheet = workbook . createSheet ( "Tickets cerrados" ) ;
String [ ] headers = { "ID" , "Tipo" , "Tiempo estimado" , "Fecha creación" , "Fecha cierre" ,
"Fecha actualización" , "Situación" , "Estado" , "Duración" , "Autor" , "Asunto" } ;
Row headerRow = sheet . createRow ( 0 ) ;
for ( int i = 0 ; i < headers . length ; i + + ) {
Cell cell = headerRow . createCell ( i ) ;
cell . setCellValue ( headers [ i ] ) ;
}
final UI ui = UI . getCurrent ( ) ;
final RedmineUser user = userService . getRedmineUser ( ) ;
final LocalDate desde = fechaDesde . getValue ( ) ;
final LocalDate hasta = fechaHasta . getValue ( ) ;
int rowNum = 1 ;
DateTimeFormatter formatter = DateTimeFormatter . ofPattern ( "dd/MM/yyyy HH:mm:ss" ) ;
for ( Ticket ticket : ticketsCerrados ) {
Row row = sheet . createRow ( rowNum + + ) ;
row . createCell ( 0 ) . setCellValue ( ticket . getId ( ) ) ;
row . createCell ( 1 ) . setCellValue ( ticket . getType ( ) ) ;
row . createCell ( 2 ) . setCellValue ( ticket . getEstimatedHrs ( ) + " horas" ) ;
row . createCell ( 3 ) . setCellValue ( ticket . getDateCreate ( ) ! = null ? ticket . getDateCreate ( ) . format ( formatter ) : "" ) ;
row . createCell ( 4 ) . setCellValue ( ticket . getDateClose ( ) ! = null ? ticket . getDateClose ( ) . format ( formatter ) : "" ) ;
row . createCell ( 5 ) . setCellValue ( ticket . getUpdateOn ( ) ! = null ? ticket . getUpdateOn ( ) . format ( formatter ) : "" ) ;
row . createCell ( 7 ) . setCellValue ( ticket . getStatus ( ) ) ;
row . createCell ( 9 ) . setCellValue ( ticket . getAuthor ( ) ! = null ? ticket . getAuthor ( ) . getUsername ( ) : "" ) ;
row . createCell ( 10 ) . setCellValue ( ticket . getSubject ( ) ) ;
if ( ticket . getDateCreateLocal ( ) ! = null & & ticket . getDateCloseLocal ( ) ! = null ) {
long dias = ChronoUnit . DAYS . between ( ticket . getDateCreateLocal ( ) , ticket . getDateCloseLocal ( ) ) ;
row . createCell ( 8 ) . setCellValue ( dias + " días" ) ;
} else {
row . createCell ( 8 ) . setCellValue ( "" ) ;
}
if ( ui = = null ) return ;
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" ) ;
Dialog loadingDialog = createLoadingDialog ( "Generando archivo por favor espere..." ) ;
loadingDialog . open ( ) ;
new Thread ( ( ) - > {
try {
/ / Traer TODOS los tickets cerrados , respetando fechas si están seleccionadas
List < Ticket > ticketsCerrados = redmineClient . getAllTickets (
user ,
false , / / false = cerrados
desde ,
hasta
) ;
Workbook workbook = new XSSFWorkbook ( ) ;
Sheet sheet = workbook . createSheet ( "Tickets cerrados" ) ;
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 ] ) ;
}
}
for ( int i = 0 ; i < headers . length ; i + + ) {
sheet . autoSizeColumn ( 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 ( "" ) ;
}
ByteArrayOutputStream out = new ByteArrayOutputStream ( ) ;
workbook . write ( out ) ;
workbook . close ( ) ;
if ( ticket . getEstimatedHrs ( ) ! = null & & ticket . getDateCreateLocal ( ) ! = null & & ticket . getDateCloseLocal ( ) ! = null ) {
long horasReales = ChronoUnit . HOURS . between ( ticket . getDateCreateLocal ( ) , ticket . getDateCloseLocal ( ) ) ;
row . createCell ( 6 ) . setCellValue ( horasReales < = ticket . getEstimatedHrs ( ) ? "En tiempo" : "Excedido" ) ;
} else {
row . createCell ( 6 ) . setCellValue ( "Sin estimación" ) ;
}
}
StreamResource resource = new StreamResource ( "tickets_cerrados.xlsx" ,
( ) - > new ByteArrayInputStream ( out . toByteArray ( ) ) ) ;
for ( int i = 0 ; i < headers . length ; i + + ) {
sheet . autoSizeColumn ( i ) ;
}
StreamRegistration registration = UI . getCurrent ( ) . getSession ( )
. getResourceRegistry ( ) . registerResource ( resource ) ;
UI . getCurrent ( ) . getPage ( ) . executeJs (
"window.open('" + registration . getResourceUri ( ) . toString ( ) + "','_blank')"
) ;
ByteArrayOutputStream out = new ByteArrayOutputStream ( ) ;
workbook . write ( out ) ;
workbook . close ( ) ;
} catch ( Exception e ) {
log . error ( "Error exportando tickets" , e ) ;
Notification . show ( "Error al exportar los tickets" , 5000 , Notification . Position . MIDDLE )
. addThemeVariants ( NotificationVariant . LUMO_ERROR ) ;
}
ui . access ( ( ) - > {
try {
StreamResource resource = new StreamResource ( "rptGeneralDeTicketsMsaAyda.xlsx" ,
( ) - > new ByteArrayInputStream ( out . toByteArray ( ) ) ) ;
StreamRegistration registration = ui . getSession ( )
. getResourceRegistry ( ) . registerResource ( resource ) ;
ui . getPage ( ) . executeJs (
"window.open('" + registration . getResourceUri ( ) . toString ( ) + "','_blank')"
) ;
} catch ( Exception ex ) {
log . error ( "Error al registrar recurso" , ex ) ;
} finally {
loadingDialog . close ( ) ;
}
} ) ;
} catch ( Exception e ) {
log . error ( "Error exportando tickets" , e ) ;
ui . access ( ( ) - > {
loadingDialog . close ( ) ;
Notification . show ( "Error al generar el reporte" , 5000 , Notification . Position . MIDDLE )
. addThemeVariants ( NotificationVariant . LUMO_ERROR ) ;
} ) ;
}
} ) . start ( ) ;
}
private void filtrarTickets ( ) {
boolean soloAbiertos = chkSoloAbiertos . getValue ( ) ;
loadTickets ( soloAbiertos ) ;
grid . getDataProvider ( ) . refreshAll ( ) ;
}
private Dialog createLoadingDialog ( String mensaje ) {
Dialog dialog = new Dialog ( ) ;
dialog . setCloseOnEsc ( false ) ;
dialog . setCloseOnOutsideClick ( false ) ;
VerticalLayout layout = new VerticalLayout ( ) ;
layout . setAlignItems ( Alignment . CENTER ) ;
layout . setPadding ( true ) ;
layout . setSpacing ( true ) ;
ProgressBar progressBar = new ProgressBar ( ) ;
progressBar . setIndeterminate ( true ) ;
progressBar . setWidth ( "15rem" ) ;
Span texto = new Span ( mensaje ) ;
texto . getStyle ( ) . set ( "font-weight" , "bold" ) ;
texto . getStyle ( ) . set ( "color" , "var(--lumo-primary-text-color)" ) ;
layout . add ( texto , progressBar ) ;
dialog . add ( layout ) ;
return dialog ;
}
}