UNPKG

@jcbautistam/custom-table

Version:

Librería de tabla reutilizable con acciones, filtros, exportación y más.

671 lines 230 kB
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core'; import { MatPaginator } from '@angular/material/paginator'; import { MatTableDataSource } from '@angular/material/table'; import { MatDialog } from '@angular/material/dialog'; import { fromEvent } from 'rxjs'; import { debounceTime, takeUntil } from 'rxjs/operators'; import { ConfiguracionParametros, ParametrosGenerales } from '../../Models/ParametrosGenerales'; import { ExportTableExcelService } from '../../Services/ExportTableExcel.service'; import { LoadingService } from '../../Services/loading.service'; import { ExportNotificationService } from '../../Services/exportNotification.service'; import { ColumnVisibilityModal2Component } from '../ColumnVisibility/column-visibility-modal2.component'; import * as i0 from "@angular/core"; import * as i1 from "../../Services/ExportTableExcel.service"; import * as i2 from "@angular/material/dialog"; import * as i3 from "../../Models/ParametrosGenerales"; import * as i4 from "../../Services/loading.service"; import * as i5 from "../../Services/exportNotification.service"; import * as i6 from "@angular/common"; import * as i7 from "@angular/forms"; import * as i8 from "@angular/router"; import * as i9 from "@angular/material/paginator"; import * as i10 from "@angular/material/table"; import * as i11 from "@angular/material/button"; import * as i12 from "@angular/material/icon"; import * as i13 from "ngx-bootstrap/datepicker"; import * as i14 from "@angular/material/checkbox"; import * as i15 from "@angular/material/slide-toggle"; import * as i16 from "@angular/material/tooltip"; import * as i17 from "@angular/material/progress-spinner"; import * as i18 from "../../Directives/ToUpperCase.directive"; import * as i19 from "../../Directives/OnlyDigit.directive"; import * as i20 from "../../pipes/onlyDigit.pipe"; import * as i21 from "../../pipes/string-truncate.pipe"; import * as i22 from "../../pipes/image.pipe"; export class CustomTableComponent { get columnKeys() { return Object.keys(this.columnConfigs); } constructor(excelService, dialog, configParams, loadingService, exportNotificationService) { this.excelService = excelService; this.dialog = dialog; this.configParams = configParams; this.loadingService = loadingService; this.exportNotificationService = exportNotificationService; // Objeto para manejar la cancelación de suscripciones de Observable this.destroy$ = new EventEmitter(); // Variables para el control de la exportación this.exportSubscription = null; this.isExportInProgress = false; this.maxDate = new Date(); // Fecha máxima hoy this.minDate = new Date(); // Fecha mínima 30 días atrás this.totalRecords = 0; this.formularioModificado = false; this.isSelectAll = false; this.originalData = []; // Una copia de los datos originales para restablecer después del filtrado this.dataActives = []; // Esto almacenará el filtro actual para cada columna. this.filters = {}; this.dataSource = new MatTableDataSource(); this.displayedColumns = []; this.styleRow = {}; this.isToggleChecked = true; //Se reciben los datos que mostrará la tabla this.data = []; //Se reciben los datos que mostrará en campo editable select // @Input() dataSelect: any[] = []; this.dataSelect = []; this.withHeader = true; //Se reciben configuraciones generales de la tabla como datos de paginacion y fontsize this.tableConfigs = {}; //Se recibe el nombre del menu para aplicar el correcto permiso por usuario this.nombreMenu = ''; //Se recibe el titulo de la tabla this.nombreTabla = ''; //Se recibe la ruta a la que navegara cuando se use el boton [crear] propio de la tabla this.crearRoute = ''; //Se recibe la indicacion sobre si se debe mostrar el boton [crear] normal this.showCreateButton = false; //Se recibe la indicacion sobre si se debe mostrar el boton [crear] normal this.showCreateButtonModal = false; //Se recibe la indicacion sobre si se debe mostrar el boton [exportar a excel] que descarga un archivo excel this.showExportarButton = true; //Se recibe el nombre con el que se debe guardar el archivo excel al exportar la tabla desde el boton [exportar a excel] this.excelFileName = ''; //Indica si esta "cargando" datos, y asi manejar la visualizacion del indicador loader this.isLoading = false; //Se recibe la indicacion sobre si se debe mostrar el boton [crear] cuando es un catalogoGeneral this.showCreateButtonDetGral = false; //Se recibe la indicacion sobre si se debe mostrar el boton [refrescar datos] que refresca los datos de la tabla this.showRefreshButton = true; this.showAddButton = false; //Se recibe la indicacion sobre si se debe mostrar el boton [configuracion de columnas] que abre el modal para elegir que columnas visualizar this.showConfColumnsButton = true; //Se recibe la indicacion sobre si se debe mostrar el boton [incluir inactivos] que incluye los registros que estan en estatus de inactivo this.showFilterInactivos = true; //Se recibe un arreglo de configuracion de boton de accion, permite saber que botons de acciones debe renderizar this.actions = []; this.resaltarSeleccion = false; this.mostrarFiltroGeneralFechas = false; this.showSelectAll = false; // Aquí defines el mínimo de columnas requeridas this.columnsReqMin = 3; this.isReport = false; this.parametros = new ParametrosGenerales(); this.extraParams = {}; // Objeto con parámetros dinámicos //Nueva propiedad para centrar la tabla automáticamente this.centerTable = true; //Nueva propiedad para controlar la altura de la tabla this.tableHeight = 'auto'; // Opción para forzar una altura específica si se necesita //Propiedad para controlar la visibilidad del gran total this.showGranTotal = false; this.widthColumn = '20%'; //Outputs para emitir eventos al padre this.onSelectAll = new EventEmitter(); this.cambiarEstatusEvent = new EventEmitter(); this.createEvent = new EventEmitter(); this.refreshEvent = new EventEmitter(); this.AddEvent = new EventEmitter(); this.onCreate = new EventEmitter(); this.enviarItemEvent = new EventEmitter(); this.filtroFechaGeneralEvent = new EventEmitter(); this.changeData = new EventEmitter(); this.filtroFechaReportEvent = new EventEmitter(); // Definir los rangos de fechas this.predefinedRanges = [ { label: 'Hoy', value: [new Date(), new Date()] }, { label: 'Últimos 7 Días', value: [ new Date(new Date().setDate(new Date().getDate() - 6)), new Date(), ], }, ]; //Definir configuracion inicial para DateRangePicker this.bsConfig = { containerClass: 'theme-orange', dateInputFormat: 'DD/MM/YYYY', isAnimated: true, }; this.applyFilter = (event, column) => { this.parametros.filtrosPorColumna = { ...this.parametros.filtrosPorColumna, [column]: event.target.value, }; this.parametros['noPagina'] = 1; this.parametros['tamanoPagina'] = 10; this.loadData(); // Asegurar que el paginador se sincroniza this.paginator.pageIndex = 0; this.paginator.length = this.totalRecords; }; this.applyFilterActives = (value) => { this.parametros.activos = !value; this.parametros['noPagina'] = 1; this.parametros['tamanoPagina'] = 10; this.loadData(); // Asegurar que el paginador se sincroniza this.paginator.pageIndex = 0; this.paginator.length = this.totalRecords; }; this.updateData = () => { this.isLoading = true; this.loadData(); }; this.refreshTable = () => { this.filterInputs.forEach((input) => { input.nativeElement.value = ''; }); this.parametros.filtrosPorColumna = {}; this.parametros['noPagina'] = 1; this.parametros['tamanoPagina'] = 10; this.loadData(); // Asegurar que el paginador se sincroniza this.paginator.pageIndex = 0; this.paginator.length = this.totalRecords; // Verificar overflow después de que los datos se actualizan setTimeout(() => this.checkForHorizontalOverflow(), 300); }; this.loadData = () => { if (this.formularioModificado) { this.originalData = []; this.dataSource.data = []; this.isLoading = true; const params = this.configParams.configurar(this.parametros); this.fetchDataFunction(params, this.extraParams).subscribe((data) => { const items = data.items; this.data = items; this.totalRecords = data.totalRecords; this.dataSource.data = this.data; this.isLoading = false; // Estilos predeterminados o configuraciones this.styleRow = { height: this.tableConfigs.heightRow, }; // Inicializa originalData de forma segura if (Array.isArray(this.data)) { this.originalData = [...this.data]; } else { this.originalData = []; // O maneja como veas conveniente } if (this.isReport) { this.bsConfigGeneral = { containerClass: 'theme-orange', dateInputFormat: 'DD/MM/YYYY', isAnimated: true, maxDate: this.maxDate, }; } // Recalcular dimensiones después de cargar datos setTimeout(() => { this.checkForHorizontalOverflow(); this.adjustTableHeight(); }, 100); }); } }; this.minDate.setDate(this.minDate.getDate() - 30); this.bsConfigGeneral = { minDate: this.minDate, maxDate: this.maxDate, containerClass: 'theme-orange', dateInputFormat: 'DD/MM/YYYY', isAnimated: true, }; } // En tu componente o servicio getColumnConfigWithDefault(columnKey) { const defaultConfig = { displayName: '', type: 'default', showFilter: true, visible: true, }; const columnConfig = this.columnConfigs[columnKey]; if (columnConfig) { // Combinar la configuración de columna existente con la configuración predeterminada return { ...defaultConfig, ...columnConfig }; } else { // Si la configuración de columna no está definida, devolver la configuración predeterminada return defaultConfig; } } // Función para determinar si se debe bloquear el renglón shouldBlockRow(element) { for (const columnKey of Object.keys(this.columnConfigs)) { const columnConfig = this.getColumnConfigWithDefault(columnKey); if (columnConfig.bloquearSeleccion && columnConfig.bloquearSeleccion(element)) { return true; // Se bloquea el renglón si se cumple la condición en alguna columna } } return false; // No se bloquea el renglón si no se encuentra ninguna condición de bloqueo } //Abre modal para configurar que columnas configure para renderizar openColumnVisibilityDialog() { const columnsArray = Object.keys(this.columnConfigs) .filter((key) => key !== 'activo') .map((key) => ({ key: key, ...this.columnConfigs[key], })); const dialogRef = this.dialog.open(ColumnVisibilityModal2Component, { width: '250px', data: { columns: columnsArray, columnsReqMin: this.columnsReqMin }, // Pasamos el mínimo de columnas }); dialogRef.afterClosed().subscribe((result) => { if (result) { // Actualizar columnConfigs con los resultados result.forEach((column) => { this.columnConfigs[column.key].visible = column.visible; }); this.updateDisplayedColumns(); } }); } //Actualiza el arreglo de columnas a renderizar updateDisplayedColumns() { /* if (this.columnConfigs.activo !== undefined) this.columnConfigs.activo.visible = false; */ const visibleColumns = Object.keys(this.columnConfigs).filter((key) => this.columnConfigs[key].visible); if (this.actions.length === 0) { this.displayedColumns = [...visibleColumns]; } else { this.displayedColumns = [...visibleColumns, 'acciones']; } } onValueChangeDateGeneral(value) { if (value && value.length === 2 && value[0] instanceof Date && value[1] instanceof Date) { this.filtroFechaGeneralEvent.emit(value); } if (value && value[0] instanceof Date && value[1] instanceof Date) { this.parametros.rangoFechas = this.fechaToString(value[0]) + '-' + this.fechaToString(value[1]); } this.parametros['noPagina'] = 1; this.parametros['tamanoPagina'] = 10; this.loadData(); // Asegurar que el paginador se sincroniza this.paginator.pageIndex = 0; this.paginator.length = this.totalRecords; } //Ejecuta al cambiar el valor del DataRangePicker onValueChangeDate(value, column = 'fechaCreacion') { if (value == undefined) { this.resetDateFilter(column); } console.log('fecha ingresasa: ', value, column); if (value && value.length === 2 && value[0] instanceof Date && value[1] instanceof Date) { this.applyDateFilter(value[0], value[1], column); } else { this.resetDateFilter(column); } } ngAfterViewInit() { this.formularioModificado = true; // Cargar datos iniciales this.loadData(); // Ajustar el tamaño de la tabla al espacio disponible this.adjustTableHeight(); // Observar cambios en el tamaño de la ventana fromEvent(window, 'resize') .pipe(debounceTime(200), takeUntil(this.destroy$)) .subscribe(() => { this.checkForHorizontalOverflow(); this.adjustTableHeight(); }); // Verificar si hay overflow horizontal this.checkForHorizontalOverflow(); // Detectar cuando hay espacio vacío después del último registro this.checkEmptySpace(); // Observar cambios en los datos para actualizar el estado de espacio vacío this.dataSource.connect().subscribe(() => { this.checkEmptySpace(); }); } ngOnInit() { this.updateDisplayedColumns(); // Inicialización de filtros this.filters = {}; for (const column of Object.keys(this.columnConfigs)) { this.filters[column] = ''; } // Suscribirse al estado de exportación this.exportSubscription = this.exportNotificationService.exportStatus$ .pipe(takeUntil(this.destroy$)) .subscribe(status => { this.isExportInProgress = status.isExporting; }); } ngOnChanges(changes) { if ((changes['extraParams'] && this.extraParams) || (changes['parametros'] && this.parametros)) { // Cuando itemDefault cambia, asignamos sus valores al form this.loadData(); } } columnMatches(row) { for (const column of Object.keys(this.columnConfigs)) { const columnName = column; let filter = this.filters[columnName]; if (filter === null) { continue; } if (filter !== null && filter !== undefined) { if (typeof filter === 'boolean') { if (row[columnName] !== filter) { return false; } } else { let value = String(row[columnName]).toLowerCase(); if (!value.includes(String(filter).toLowerCase())) { return false; } } } } return true; } applyDateFilter(startDate, endDate, column) { this.parametros.filtrosPorColumna = { ...this.parametros.filtrosPorColumna, [column]: this.fechaToString(startDate) + '-' + this.fechaToString(endDate), }; this.parametros['noPagina'] = 1; this.parametros['tamanoPagina'] = 10; this.loadData(); // Asegurar que el paginador se sincroniza this.paginator.pageIndex = 0; this.paginator.length = this.totalRecords; } resetDateFilter(column) { this.parametros.filtrosPorColumna = { ...this.parametros.filtrosPorColumna, [column]: '', }; this.parametros['noPagina'] = 1; this.parametros['tamanoPagina'] = 10; this.loadData(); // Asegurar que el paginador se sincroniza this.paginator.pageIndex = 0; this.paginator.length = this.totalRecords; } fechaToString(fecha) { const day = String(fecha.getDate()).padStart(2, '0'); const month = String(fecha.getMonth() + 1).padStart(2, '0'); // Los meses en JS son base 0 const year = fecha.getFullYear(); // Formatear la fecha en dd/mm/yyyy const formattedDate = `${day}/${month}/${year}`; return formattedDate; } applyBooleanFilter(value, column) { value ? (this.parametros.filtrosPorColumna = { ...this.parametros.filtrosPorColumna, [column]: 'true', }) : delete this.parametros.filtrosPorColumna[column]; this.parametros['noPagina'] = 1; this.parametros['tamanoPagina'] = 10; this.loadData(); // Asegurar que el paginador se sincroniza this.paginator.pageIndex = 0; this.paginator.length = this.totalRecords; } enviarItem(item) { this.rowSeleccionada = item; this.enviarItemEvent.emit(item); } agregarRegistro() { this.AddEvent.emit(); } cambiarEstatus(item) { this.cambiarEstatusEvent.emit(item); } exportTable() { // Iniciar la notificación de exportación y obtener el ID del trabajo const jobId = this.exportNotificationService.startExport(this.excelFileName); // Preparar parámetros para la consulta completa const exportParams = { ...this.parametros }; // En lugar de usar 0, usamos el número total de registros exportParams['noPagina'] = 1; exportParams['tamanoPagina'] = this.totalRecords > 0 ? this.totalRecords : 10000; // Configurar los parámetros de la consulta const params = this.configParams.configurar(exportParams); this.exportNotificationService.updateProgress(jobId, 25); // Obtener las columnas a exportar (excluyendo 'acciones') let displayColumns = this.actions.length > 0 ? this.displayedColumns.filter(d => d != 'acciones') : this.displayedColumns; // Realizar la consulta al endpoint this.fetchDataFunction(params, this.extraParams).subscribe({ next: (data) => { this.exportNotificationService.updateProgress(jobId, 50); // Transformar datos para usar nombres de visualización como cabeceras const transformedData = data.items.map((item) => { const transformedItem = {}; displayColumns.forEach((title) => { if (this.columnConfigs[title].type != 'image') { transformedItem[this.columnConfigs[title].displayName] = item[title]; } }); return transformedItem; }); this.exportNotificationService.updateProgress(jobId, 75); // Exportar los datos completos this.excelService.exportToExcel('catalogoPrincipal', this.excelFileName, transformedData); // Completar la exportación this.exportNotificationService.completeExport(jobId); }, error: (error) => { console.error('Error al exportar datos:', error); this.exportNotificationService.errorExport(jobId, error); } }); } onValueChangeDateReport(value) { this.filtroFechaReportEvent.emit(value); } create() { this.onCreate.emit(); this.createEvent.emit(); } get isDataEmpty() { return (this.dataSource && this.dataSource.data.length === 0 && !this.isLoading); } //Funcion para agregar estilos a los registros eliminados shouldHideText(row) { // Lógica para decidir si ocultar el texto return row.activo === false; // Por ejemplo, oculta el texto para los registros no activos } onPageChange(event) { this.parametros['noPagina'] = event.pageIndex + 1; this.parametros['tamanoPagina'] = event.pageSize; this.loadData(); // Scroll al inicio de la tabla al cambiar de página if (this.matTableWrapper && this.matTableWrapper.nativeElement) { const tableContent = this.matTableWrapper.nativeElement.querySelector('.table-v2-content'); if (tableContent) { tableContent.scrollTop = 0; } } } onCheckSelectAll(event) { this.isSelectAll = event.checked; this.onSelectAll.emit(event); } /** Opciones de tamaño de página transformadas */ transformedPageSizeOptions(options) { return options.map((size) => (size === 5 ? 10 : size)); } /** * Verifica si hay overflow horizontal en la tabla y aplica clase correspondiente */ checkForHorizontalOverflow() { if (!this.matTableWrapper) return; setTimeout(() => { const tableContent = this.matTableWrapper.nativeElement.querySelector('.table-v2-content'); if (!tableContent) return; const hasOverflow = tableContent.scrollWidth > tableContent.clientWidth; if (hasOverflow) { tableContent.classList.add('has-overflow'); } else { tableContent.classList.remove('has-overflow'); } }, 300); } // Método para ajustar la altura de la tabla dinámicamente adjustTableHeight() { if (!this.matTableWrapper) return; setTimeout(() => { try { // Reiniciar overflow en el body document.body.style.overflowX = 'hidden'; document.documentElement.style.overflowX = 'hidden'; // Referencias a los elementos relevantes const tableContainer = this.matTableWrapper.nativeElement.closest('.table-v2-container'); if (!tableContainer) return; // Obtener el elemento del footer global de la aplicación const appFooter = document.querySelector('footer') || document.querySelector('.app-footer') || document.querySelector('.main-footer'); // Dimensiones del viewport const viewportHeight = window.innerHeight; const viewportWidth = window.innerWidth; // Posición del contenedor de la tabla const tableRect = tableContainer.getBoundingClientRect(); const tableTop = tableRect.top; // Calcular altura disponible según la posición del footer o el viewport let availableHeight; if (appFooter) { // Si existe un footer, calcular basado en su posición const footerRect = appFooter.getBoundingClientRect(); const footerTop = footerRect.top; // Calcular la distancia entre la parte superior de la tabla y el footer // y restar un margen de seguridad availableHeight = footerTop - tableTop - 40; } else { // Si no hay footer, usar el viewport menos un margen para la parte inferior availableHeight = viewportHeight - tableTop - 40; } // Garantizar una altura mínima para la tabla const minHeight = 400; const finalHeight = Math.max(availableHeight, minHeight); // Aplicar la altura calculada al contenedor de la tabla tableContainer.style.height = `${finalHeight}px`; // Ajustar el contenido de la tabla según headers y footers const tableHeader = tableContainer.querySelector('.toolbar'); const tableFooter = tableContainer.querySelector('.sticky-footer'); // Calcular las alturas de los elementos const headerHeight = tableHeader ? tableHeader.offsetHeight : 0; const footerHeight = tableFooter ? tableFooter.offsetHeight : 0; // Obtener referencia al contenido de la tabla const tableContent = tableContainer.querySelector('.table-v2-content'); if (tableContent) { // Calcular el espacio disponible para el contenido const contentHeight = finalHeight - headerHeight - footerHeight; tableContent.style.height = `${contentHeight}px`; // Ajustar la tabla interior para manejar scroll horizontal setTimeout(() => { const table = tableContent.querySelector('table.mat-table'); if (table) { // Verificar si se necesita scroll horizontal const tableWidth = table.scrollWidth; const contentWidth = tableContent.clientWidth; if (tableWidth > contentWidth) { // La tabla es más ancha que el contenedor, activar indicador visual de scroll tableContent.classList.add('has-overflow'); tableContent.style.overflowX = 'scroll'; // Asegurar que la tabla sea lo suficientemente ancha table.style.width = 'max-content'; table.style.minWidth = '100%'; } else { // No se necesita scroll horizontal tableContent.classList.remove('has-overflow'); // La tabla cabe en el contenedor, ajustar al 100% table.style.width = '100%'; } } }, 100); } // Asegurar que el footer sea visible if (tableFooter) { tableFooter.style.position = 'sticky'; tableFooter.style.bottom = '0'; tableFooter.style.zIndex = '100'; } } catch (error) { console.error('Error al ajustar dimensiones de tabla:', error); } }, 200); } /** * Verifica si hay espacio vacío después del último registro y aplica la clase correspondiente */ checkEmptySpace() { setTimeout(() => { if (!this.matTableWrapper) return; const tableElement = this.matTableWrapper.nativeElement; const tableHeight = tableElement.offsetHeight; const tableContentHeight = this.calculateContentHeight(); const tableContainer = tableElement.closest('.table-v2-content'); if (tableContainer && tableHeight < tableContainer.offsetHeight * 0.8) { // Si el contenido de la tabla ocupa menos del 80% del contenedor, hay espacio vacío tableElement.classList.add('has-empty-space'); } else { tableElement.classList.remove('has-empty-space'); } }); } /** * Calcula la altura del contenido de la tabla (filas) */ calculateContentHeight() { if (!this.matTableWrapper) return 0; const rows = this.matTableWrapper.nativeElement.querySelectorAll('tr.mat-row'); let totalHeight = 0; rows.forEach((row) => { totalHeight += row.offsetHeight; }); return totalHeight; } ngOnDestroy() { // Emitir evento para cancelar todas las suscripciones this.destroy$.emit(); this.destroy$.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: CustomTableComponent, deps: [{ token: i1.ExportTableExcelService }, { token: i2.MatDialog }, { token: i3.ConfiguracionParametros }, { token: i4.LoadingService }, { token: i5.ExportNotificationService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.2", type: CustomTableComponent, selector: "custom-table", inputs: { data: "data", dataSelect: "dataSelect", columnConfigs: "columnConfigs", withHeader: "withHeader", tableConfigs: "tableConfigs", nombreMenu: "nombreMenu", nombreTabla: "nombreTabla", crearRoute: "crearRoute", showCreateButton: "showCreateButton", showCreateButtonModal: "showCreateButtonModal", showExportarButton: "showExportarButton", excelFileName: "excelFileName", showCreateButtonDetGral: "showCreateButtonDetGral", showRefreshButton: "showRefreshButton", showAddButton: "showAddButton", showConfColumnsButton: "showConfColumnsButton", showFilterInactivos: "showFilterInactivos", actions: "actions", resaltarSeleccion: "resaltarSeleccion", mostrarFiltroGeneralFechas: "mostrarFiltroGeneralFechas", showSelectAll: "showSelectAll", columnsReqMin: "columnsReqMin", isReport: "isReport", fetchDataFunction: "fetchDataFunction", parametros: "parametros", extraParams: "extraParams", centerTable: "centerTable", tableHeight: "tableHeight", showGranTotal: "showGranTotal", widthColumn: "widthColumn" }, outputs: { onSelectAll: "onSelectAll", cambiarEstatusEvent: "cambiarEstatusEvent", createEvent: "createEvent", refreshEvent: "refreshEvent", AddEvent: "AddEvent", onCreate: "onCreate", enviarItemEvent: "enviarItemEvent", filtroFechaGeneralEvent: "filtroFechaGeneralEvent", changeData: "changeData", filtroFechaReportEvent: "filtroFechaReportEvent" }, viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "matTableWrapper", first: true, predicate: ["matTableWrapper"], descendants: true }, { propertyName: "filterInputs", predicate: ["filterInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"table-v2-container\" [ngClass]=\"{ 'table-v2-centered': centerTable }\">\r\n <div *ngIf=\"withHeader\" class=\"toolbar\">\r\n <div class=\"left-buttons\">\r\n <button *ngIf=\"showCreateButton\" mat-raised-button [routerLink]=\"crearRoute\" class=\"btn-tabla-centrado\"\r\n matTooltip=\"Nuevo\" matTooltipPosition=\"above\"\r\n matTooltipShowDelay=\"150\">\r\n Crear\r\n </button>\r\n <button *ngIf=\"showCreateButtonDetGral\" mat-raised-button (click)=\"create()\" class=\"btn-tabla-centrado\"\r\n matTooltip=\"Nuevo\" matTooltipPosition=\"above\"\r\n matTooltipShowDelay=\"150\">\r\n Crear\r\n </button>\r\n\r\n <button *ngIf=\"showCreateButtonModal\" mat-raised-button (click)=\"tableConfigs.createCallback()\"\r\n class=\"btn-tabla-centrado\" matTooltip=\"Nuevo\"\r\n matTooltipPosition=\"above\" matTooltipShowDelay=\"150\">\r\n Crear\r\n </button>\r\n\r\n <input *ngIf=\"mostrarFiltroGeneralFechas\" #filterInput class=\"form-control-date\" bsDaterangepicker\r\n placeholder=\"Filtrar por fecha\" type=\"text\" [bsConfig]=\"bsConfigGeneral\"\r\n (bsValueChange)=\"onValueChangeDateGeneral($event)\" />\r\n\r\n <input *ngIf=\"isReport\" #filterInput class=\"form-control-date\" bsDatepicker placeholder=\"Filtrar por fecha\"\r\n type=\"text\" [bsConfig]=\"bsConfigGeneral\" (bsValueChange)=\"onValueChangeDateReport($event)\" />\r\n\r\n <div *ngIf=\"showSelectAll\" class=\"checkbox-container\">\r\n <mat-checkbox [disabled]=\"false\" (change)=\"onCheckSelectAll($event)\">Todos</mat-checkbox>\r\n </div>\r\n </div>\r\n\r\n <div class=\"table-title\">\r\n <span>{{ nombreTabla }}</span>\r\n </div>\r\n\r\n <div class=\"right-buttons\">\r\n <style>\r\n .toggle-color-amarillo.mat-mdc-slide-toggle.mat-mdc-slide-toggle-checked .mdc-switch__thumb {\r\n background-color: var(--color-select-toggle) !important;\r\n }\r\n\r\n .toggle-color-amarillo.mat-mdc-slide-toggle.mat-mdc-slide-toggle-checked .mdc-switch__track {\r\n background-color: var(--color-select-toggle-transparente) !important;\r\n }\r\n\r\n .toggle-color-amarillo.mat-slide-toggle.mat-checked .mat-slide-toggle-thumb {\r\n background-color: var(--color-select-toggle) !important;\r\n }\r\n\r\n .toggle-color-amarillo.mat-slide-toggle.mat-checked .mat-slide-toggle-bar {\r\n background-color: var(--color-select-toggle-transparente) !important;\r\n }\r\n </style>\r\n <mat-slide-toggle *ngIf=\"showFilterInactivos\" (change)=\"applyFilterActives($event.checked)\"\r\n matTooltip=\"Incluir eliminados\" matTooltipPosition=\"above\" matTooltipShowDelay=\"150\"\r\n class=\"toggle-color-amarillo\">\r\n </mat-slide-toggle>\r\n <button *ngIf=\"showExportarButton\" mat-raised-button class=\"btn btn-secondary btn-tabla-centrado\"\r\n (click)=\"exportTable()\" [disabled]=\"isExportInProgress\"\r\n matTooltip=\"{{isExportInProgress ? 'Exportaci\u00F3n en progreso...' : 'Exportar a excel'}}\"\r\n matTooltipPosition=\"above\" matTooltipShowDelay=\"150\">\r\n <mat-icon>{{isExportInProgress ? 'hourglass_empty' : 'file_download'}}</mat-icon>\r\n </button>\r\n\r\n <button *ngIf=\"showRefreshButton\" mat-raised-button class=\"refresh-button btn-tabla-centrado\"\r\n (click)=\"refreshTable()\" matTooltip=\"Refrescar tabla\" matTooltipPosition=\"above\"\r\n matTooltipShowDelay=\"150\">\r\n <mat-icon>refresh</mat-icon>\r\n </button>\r\n\r\n <button *ngIf=\"showAddButton\" mat-raised-button class=\"refresh-button btn-tabla-centrado\"\r\n (click)=\"agregarRegistro()\" matTooltip=\"Agregar registro\" matTooltipPosition=\"above\"\r\n matTooltipShowDelay=\"150\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n\r\n <button *ngIf=\"showConfColumnsButton\" (click)=\"openColumnVisibilityDialog()\" mat-raised-button\r\n class=\"btn btn-secondary settings-button btn-tabla-centrado\" matTooltip=\"Configurar columnas\"\r\n matTooltipPosition=\"above\" matTooltipShowDelay=\"150\">\r\n <mat-icon>settings</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div class=\"table-v2-wrapper\" #matTableWrapper>\r\n <div class=\"table-v2-content\">\r\n <mat-table id=\"catalogoPrincipal\" mat-table [dataSource]=\"dataSource\" class=\"mat-elevation-z0\">\r\n <!-- Columnas Din\u00E1micas -->\r\n <ng-container *ngFor=\"let column of columnKeys\" [matColumnDef]=\"column\">\r\n <th mat-header-cell *matHeaderCellDef sticky\r\n [ngClass]=\"{'columna-id': column === 'id' || column === 'Id'}\"\r\n [ngStyle]=\"{ width: widthColumn }\">\r\n <div style=\"display: flex; flex-direction: column;\">\r\n <div>{{ columnConfigs[column]?.displayName || column }}</div>\r\n <ng-container *ngIf=\"columnConfigs[column]?.showFilter\">\r\n <ng-container *ngIf=\"\r\n columnConfigs[column]?.type == 'default' ||\r\n columnConfigs[column]?.type == 'default-truncate'\r\n \">\r\n <input #filterInput class=\"form-control form-control-sm\"\r\n [ngClass]=\"{'filtro-width': column === 'id' || column === 'Id'}\" type=\"text\"\r\n (change)=\"applyFilter($event, column)\" placeholder=\"Filtrar\" />\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type == 'number'\">\r\n <input #filterInput class=\"form-control form-control-sm\"\r\n [ngClass]=\"{'filtro-width': column === 'id' || column === 'Id'}\" type=\"text\"\r\n (change)=\"applyFilter($event, column)\" placeholder=\"Filtrar\" />\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type == 'date'\">\r\n <input #filterInput style=\"width: 100%\" bsDaterangepicker placeholder=\"Filtrar\"\r\n class=\"form-control form-control-sm\" type=\"text\" [bsConfig]=\"bsConfig\"\r\n (bsValueChange)=\"onValueChangeDate($event, column)\" />\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'boolean'\">\r\n <mat-slide-toggle [checked]=\"columnConfigs[column]?.iniFilterBool\"\r\n (change)=\"applyBooleanFilter($event.checked ? true : null, column)\">\r\n </mat-slide-toggle>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </th>\r\n\r\n <td mat-cell *matCellDef=\"let element\" [ngClass]=\"{\r\n 'disabled-record': shouldBlockRow(element),\r\n registroEliminado: shouldHideText(element)\r\n }\" (click)=\"!shouldBlockRow(element) ? enviarItem(element) : null\">\r\n <div *ngIf=\"!isLoading && !isDataEmpty\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'boolean'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.editable; else elseBlock\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.event; else elseEvent\">\r\n <mat-checkbox [(ngModel)]=\"element[column]\"\r\n (change)=\"columnConfigs[column]?.functionEvent(element)\">\r\n </mat-checkbox>\r\n </ng-container>\r\n\r\n <ng-template #elseEvent>\r\n <mat-checkbox [(ngModel)]=\"element[column]\"> </mat-checkbox>\r\n </ng-template>\r\n </ng-container>\r\n <ng-template #elseBlock>\r\n {{\r\n element[column]\r\n ? columnConfigs[column].trueValue\r\n : columnConfigs[column].falseValue\r\n }}\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'date'\">\r\n <span class=\"text-uppercase\">{{ element[column] | date : \"dd/MMM/yyyy\" }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'date-time'\">\r\n <span class=\"text-uppercase\">{{ element[column] | date : \"dd/MMM/yyyy HH:MM\" }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'default'\">\r\n <ng-container *ngIf=\"\r\n columnConfigs[column]?.customRender;\r\n else elseBlockDefault\r\n \">\r\n <span\r\n [ngClass]=\"{'text-uppercase': column === 'fechaCita' || columnConfigs[column]?.type === 'date'}\">{{\r\n columnConfigs[column]?.customRender(element)\r\n }}</span>\r\n </ng-container>\r\n <ng-template #elseBlockDefault>\r\n <ng-container\r\n *ngIf=\"columnConfigs[column]?.editable; else elseBlockDefaultEditable\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.event; else elseEventDefault\">\r\n <input type=\"text\" [(ngModel)]=\"element[column]\" style=\"width: 100%\"\r\n toUpperCase (blur)=\"columnConfigs[column]?.functionEvent(element)\" />\r\n </ng-container>\r\n <ng-template #elseEventDefault>\r\n <input type=\"text\" [(ngModel)]=\"element[column]\" style=\"width: 100%\"\r\n toUpperCase />\r\n </ng-template>\r\n </ng-container>\r\n <ng-template #elseBlockDefaultEditable>\r\n {{ element[column] }}\r\n </ng-template>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'number'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.editable; else elseBlock\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.event; else elseEvent\">\r\n <input type=\"text\" [(ngModel)]=\"element[column]\"\r\n style=\"width: 100%; text-align: right\" (keydown.enter)=\"\r\n columnConfigs[column]?.functionEvent(element)\r\n \" onlyDigit=\"{{ columnConfigs[column]?.char }}\" />\r\n </ng-container>\r\n <ng-template #elseEvent>\r\n <input type=\"text\" [(ngModel)]=\"element[column]\"\r\n style=\"width: 100%; text-align: right\"\r\n onlyDigit=\"{{ columnConfigs[column]?.char }}\" />\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-template #elseBlock>\r\n <p class=\"text-align-right\" *ngIf=\"!element.isEditing\">\r\n {{\r\n element[column] | onlyDigit : columnConfigs[column]?.char\r\n }}\r\n </p>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'default-truncate'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.customRender; else elseBlock\">\r\n <span matTooltip=\"{{ element[column] }}\">{{\r\n columnConfigs[column]?.customRender(element) | truncate : 10\r\n }}</span>\r\n </ng-container>\r\n <ng-template #elseBlock>\r\n <span matTooltip=\"{{ element[column] }}\">{{\r\n element[column] | truncate : 10\r\n }}</span>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'percent'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.customRender; else elseBlock\">\r\n <span matTooltip=\"{{ element[column] | percent }}\">{{\r\n columnConfigs[column]?.customRender(element) | percent\r\n }}</span>\r\n </ng-container>\r\n <ng-template #elseBlock>\r\n <span matTooltip=\"{{ element[column] | percent }}\">{{\r\n element[column] | percent\r\n }}</span>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'image'\">\r\n <img [src]=\"element[column] | image\" alt=\"Foto gafete\" style=\"width:100px !important\">\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'select'\">\r\n <select [(ngModel)]=\"element[column]\" style=\"width: 100%; height: 25px\"\r\n disabled=\"{{ !columnConfigs[column]?.editable }}\">\r\n <option *ngFor=\"\r\n let item of dataSelect[\r\n columnConfigs[column]?.positionDataSelect\r\n ]\r\n \" [value]=\"item.id\">\r\n {{ item.nombre }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!columnConfigs[column]\">\r\n {{ element[column] }}\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'dinamic'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.editable; else elseBlock\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.event; else elseEvent\">\r\n <input type=\"text\" [(ngModel)]=\"element[column]\"\r\n style=\"width: 100%; text-align: right\" (keydown.enter)=\"\r\n columnConfigs[column]?.functionEvent(element)\r\n \" onlyDigit=\"{{ columnConfigs[column]?.char }}\" />\r\n </ng-container>\r\n