ng-firebase-table-kxp 1.0.14 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,8 +26,6 @@ import * as i15 from '@angular/material/icon';
26
26
  import { MatIconModule } from '@angular/material/icon';
27
27
  import * as i2 from '@angular/material/dialog';
28
28
  import { MatDialogModule } from '@angular/material/dialog';
29
- import * as i16 from 'ngx-mask';
30
- import { NgxMaskDirective, NgxMaskPipe, provideNgxMask } from 'ngx-mask';
31
29
  import { firstValueFrom, Subject, debounceTime } from 'rxjs';
32
30
  import firebase from 'firebase/compat/app';
33
31
  import * as moment from 'moment';
@@ -1142,12 +1140,9 @@ class TableService {
1142
1140
  }
1143
1141
  }
1144
1142
  TableService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TableService, deps: [{ token: i1.AngularFirestore, optional: true }, { token: i2.MatDialog, optional: true }, { token: i3.ToastrService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
1145
- TableService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TableService, providedIn: 'root' });
1143
+ TableService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TableService });
1146
1144
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TableService, decorators: [{
1147
- type: Injectable,
1148
- args: [{
1149
- providedIn: 'root',
1150
- }]
1145
+ type: Injectable
1151
1146
  }], ctorParameters: function () { return [{ type: i1.AngularFirestore, decorators: [{
1152
1147
  type: Optional
1153
1148
  }] }, { type: i2.MatDialog, decorators: [{
@@ -1158,10 +1153,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
1158
1153
 
1159
1154
  class TableComponent {
1160
1155
  // CONSTRUCTOR
1161
- constructor(router, tableService, firestore) {
1156
+ constructor(router, tableService, firestore, el) {
1162
1157
  this.router = router;
1163
1158
  this.tableService = tableService;
1164
1159
  this.firestore = firestore;
1160
+ this.el = el;
1165
1161
  this.arrange = null;
1166
1162
  this.currentPageNumber = 1;
1167
1163
  this.currentClientPageIndex = 0;
@@ -1193,24 +1189,23 @@ class TableComponent {
1193
1189
  this.showTooltip = false;
1194
1190
  this.tooltipContent = '';
1195
1191
  this.tooltipPosition = { x: 0, y: 0 };
1196
- // Validador de data armazenado como propriedade para compatibilidade com ngx-mask
1197
- this.dateValidator = this.tableService.dateFormatValidator();
1198
1192
  this.filtersForm = new FormArray([this.createFilterGroup()]);
1199
1193
  }
1200
1194
  createFilterGroup() {
1195
+ // Criar validador diretamente aqui para evitar problemas de contexto de injeção
1196
+ const dateValidator = this.tableService.dateFormatValidator();
1201
1197
  return new FormGroup({
1202
1198
  selectFilter: new FormControl(''),
1203
1199
  typeFilter: new FormControl(''),
1204
1200
  selectItem: new FormControl(''),
1205
- initialDate: new FormControl('', [this.dateValidator]),
1206
- finalDate: new FormControl('', [this.dateValidator]),
1201
+ initialDate: new FormControl('', [dateValidator]),
1202
+ finalDate: new FormControl('', [dateValidator]),
1207
1203
  });
1208
1204
  }
1209
1205
  addFilter(filterData) {
1210
1206
  if (this.data.pagination === true &&
1211
1207
  this.hasActiveDateFilter() &&
1212
1208
  filterData?.selectFilter?.arrange === 'filterByDate') {
1213
- // Não permitir adicionar novo filtro de data quando já existe um ativo
1214
1209
  return;
1215
1210
  }
1216
1211
  const newFilterGroup = this.createFilterGroup();
@@ -1270,9 +1265,44 @@ class TableComponent {
1270
1265
  this.resetFilter();
1271
1266
  }
1272
1267
  // METHODS
1268
+ ngOnChanges(changes) {
1269
+ if (changes['data']) {
1270
+ // Aplicar cores quando o data for definido ou mudar
1271
+ // Usar setTimeout para garantir que a view esteja pronta
1272
+ setTimeout(() => this.applyCustomColors(), 0);
1273
+ }
1274
+ }
1275
+ ngAfterViewInit() {
1276
+ // Garantir que as cores sejam aplicadas após a view estar pronta
1277
+ this.applyCustomColors();
1278
+ }
1279
+ applyCustomColors() {
1280
+ const hostElement = this.el?.nativeElement;
1281
+ if (!hostElement || !this.data)
1282
+ return;
1283
+ if (this.data.color?.bg) {
1284
+ hostElement.style.setProperty('--table-color', this.data.color.bg);
1285
+ hostElement.style.setProperty('--table-header-bg', this.data.color.bg);
1286
+ }
1287
+ if (this.data.color?.text) {
1288
+ hostElement.style.setProperty('--table-text-color', this.data.color.text);
1289
+ hostElement.style.setProperty('--table-header-text', this.data.color.text);
1290
+ }
1291
+ setTimeout(() => {
1292
+ const headerCells = hostElement.querySelectorAll('.mat-header-cell, .mat-mdc-header-cell, th.mat-header-cell, th.mat-mdc-header-cell');
1293
+ headerCells.forEach((cell) => {
1294
+ if (this.data.color?.bg) {
1295
+ cell.style.backgroundColor = this.data.color.bg;
1296
+ }
1297
+ if (this.data.color?.text) {
1298
+ cell.style.color = this.data.color.text;
1299
+ }
1300
+ });
1301
+ }, 100);
1302
+ }
1273
1303
  async ngOnInit() {
1274
1304
  if (!this.data.color)
1275
- this.data.color = { bg: 'bg-primary', text: 'text-black' };
1305
+ this.data.color = { bg: '#1976d2', text: '#ffffff' };
1276
1306
  this.columnProperties = this.data.displayedColumns.map((column) => {
1277
1307
  return column.property;
1278
1308
  });
@@ -1590,6 +1620,29 @@ class TableComponent {
1590
1620
  this.applyFiltersToDataSource();
1591
1621
  }
1592
1622
  }
1623
+ // Aplicar máscara de data (DD/MM/AAAA) manualmente
1624
+ onDateInput(event, controlName, filterIndex) {
1625
+ const input = event.target;
1626
+ let value = input.value.replace(/\D/g, ''); // Remove tudo que não é dígito
1627
+ // Limitar a 8 dígitos (DDMMAAAA)
1628
+ if (value.length > 8) {
1629
+ value = value.slice(0, 8);
1630
+ }
1631
+ // Formatar: DD/MM/AAAA
1632
+ if (value.length <= 2) {
1633
+ value = value;
1634
+ }
1635
+ else if (value.length <= 4) {
1636
+ value = value.slice(0, 2) + '/' + value.slice(2);
1637
+ }
1638
+ else {
1639
+ value =
1640
+ value.slice(0, 2) + '/' + value.slice(2, 4) + '/' + value.slice(4, 8);
1641
+ }
1642
+ // Atualizar o FormControl
1643
+ const filterGroup = this.filtersForm.at(filterIndex);
1644
+ filterGroup.get(controlName)?.setValue(value, { emitEvent: false });
1645
+ }
1593
1646
  // Aplicar filtros ao dataSource quando não há paginação
1594
1647
  applyFiltersToDataSource() {
1595
1648
  if (!this.dataSource)
@@ -1855,9 +1908,9 @@ class TableComponent {
1855
1908
  .flatMap((col) => this.items.map(async (item) => {
1856
1909
  if (col.relation) {
1857
1910
  item[col.property] = await this.getRelation({
1858
- id: item[col.relation.property],
1859
- collection: col.relation.collection,
1860
- newProperty: col.relation.newProperty,
1911
+ id: item[col.relation.sourceProperty],
1912
+ collection: col.relation.targetedCollection,
1913
+ newProperty: col.relation.targetedProperty || col.property,
1861
1914
  });
1862
1915
  }
1863
1916
  }));
@@ -2208,12 +2261,12 @@ class TableComponent {
2208
2261
  }
2209
2262
  }
2210
2263
  }
2211
- TableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TableComponent, deps: [{ token: i1$1.Router }, { token: TableService }, { token: i1.AngularFirestore }], target: i0.ɵɵFactoryTarget.Component });
2212
- TableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: TableComponent, selector: "lib-table", inputs: { data: "data", downloadTable: "downloadTable" }, viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<div *ngIf=\"data\" class=\"card-body\">\n <div class=\"flex flex-col justify-between gap-6\">\n <!-- UNIFIED CONTROL PANEL: FILTERS, SORT & ACTIONS -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"\n data.pagination === true &&\n (dropdownItems.length > 0 ||\n sortableDropdownItems.length > 0 ||\n data.actionButton)\n \"\n >\n <!-- PANEL HEADER: Title, Custom Action, and Global Actions -->\n <div\n class=\"mb-4 flex flex-col items-start justify-between gap-4 border-b-2 border-gray-200 pb-4 md:flex-row md:items-center\"\n >\n <!-- Left Side: Title & Main Action Button -->\n <div class=\"flex flex-wrap items-center gap-4\">\n <div class=\"flex items-center gap-2\">\n <i class=\"fa fa-filter text-xl text-blue-500\"></i>\n <span class=\"text-lg font-semibold text-gray-700\"\n >Filtros e A\u00E7\u00F5es</span\n >\n </div>\n <button\n *ngIf=\"data.actionButton && data.actionButton.condition\"\n [ngClass]=\"\n (data.actionButton.colorClass || 'bg-blue-500') +\n ' flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white hover:opacity-70'\n \"\n [routerLink]=\"data.actionButton.routerLink\"\n (click)=\"\n data.actionButton.method ? data.actionButton.method($event) : null\n \"\n >\n <i\n *ngIf=\"data.actionButton.icon\"\n [class]=\"data.actionButton.icon\"\n ></i>\n {{ data.actionButton.label }}\n </button>\n </div>\n\n <!-- Right Side: Search, Reset, Export -->\n <div\n class=\"flex flex-wrap gap-3\"\n *ngIf=\"\n this.hasFilterableColumn === true || this.hasSortableColumn === true\n \"\n >\n <button\n (click)=\"search()\"\n type=\"button\"\n class=\"flex items-center gap-2 rounded-lg bg-green-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700\"\n matTooltip=\"Aplicar filtros\"\n >\n <i class=\"fa fa-search\"></i>\n Pesquisar\n </button>\n\n <button\n (click)=\"resetFilter()\"\n class=\"flex items-center gap-2 rounded-lg bg-red-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600\"\n matTooltip=\"Limpar filtros\"\n >\n <i class=\"fas fa-redo-alt\"></i>\n Resetar\n </button>\n\n <button\n *ngIf=\"data.download !== false && downloadTable\"\n class=\"flex items-center gap-2 rounded-lg bg-orange-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600\"\n matTooltipPosition=\"above\"\n matTooltip=\"Exportar Tabela\"\n [disabled]=\"\n this.dataSource && this.dataSource.filteredData.length <= 0\n \"\n (click)=\"\n $any(arrange) && downloadTable !== undefined\n ? downloadTable($any(arrange), data.conditions || [])\n : null\n \"\n >\n <i class=\"fa fa-download\"></i>\n Exportar\n </button>\n </div>\n </div>\n\n <!-- FILTERS CONTENT (WITH REFINEMENTS) -->\n <div class=\"mb-4 space-y-3\" *ngIf=\"filtersForm.controls.length > 0\">\n <div\n [formGroup]=\"$any(filterGroup)\"\n *ngFor=\"let filterGroup of filtersForm.controls; let i = index\"\n class=\"flex flex-wrap items-center gap-3 rounded-lg border border-gray-200 p-2\"\n >\n <!-- FILTER TYPE SELECTOR -->\n <div class=\"min-w-[200px] flex-1\" *ngIf=\"dropdownItems.length > 0\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Tipo de filtro</mat-label>\n <mat-select\n placeholder=\"Selecione o tipo...\"\n formControlName=\"selectFilter\"\n (selectionChange)=\"onSelectFilterChange()\"\n >\n <mat-option\n *ngFor=\"let item of getAvailableFilterOptions()\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i\n [class]=\"item.icon || 'fa fa-filter'\"\n class=\"text-sm text-blue-500\"\n ></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- TEXT FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange === 'filter'\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-gray-400\"></i>\n <span>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Filtrar\"\n }}</span>\n </mat-label>\n <input\n (keyup.enter)=\"search($event)\"\n formControlName=\"typeFilter\"\n matInput\n placeholder=\"Digite para filtrar...\"\n #input\n />\n </mat-form-field>\n </div>\n\n <!-- DROPDOWN FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value &&\n $any(filterGroup)\n .get('selectFilter')\n ?.value.hasOwnProperty('items')\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Selecione\"\n }}</mat-label>\n <mat-select\n placeholder=\"Selecione...\"\n formControlName=\"selectItem\"\n multiple\n >\n <mat-option\n *ngFor=\"\n let item of $any(filterGroup).get('selectFilter')?.value\n .items\n \"\n [value]=\"item\"\n >\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- DATE FILTER -->\n <div\n class=\"min-w-[340px] flex-auto\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange ===\n 'filterByDate'\n \"\n >\n <div\n class=\"flex flex-col items-stretch gap-3 sm:flex-row sm:items-center\"\n >\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Inicial</span>\n </mat-label>\n <input\n type=\"text\"\n matInput\n (keydown.enter)=\"search($event)\"\n formControlName=\"initialDate\"\n [dropSpecialCharacters]=\"false\"\n mask=\"d0/M0/0000\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Final</span>\n </mat-label>\n <input\n type=\"text\"\n (keydown.enter)=\"search($event)\"\n matInput\n formControlName=\"finalDate\"\n [dropSpecialCharacters]=\"false\"\n mask=\"d0/M0/0000\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <!-- REMOVE FILTER BUTTON -->\n <div *ngIf=\"filtersForm.length > 1\" class=\"ml-auto flex-shrink-0\">\n <button\n (click)=\"removeFilter(i)\"\n class=\"flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-300 hover:bg-red-100\"\n matTooltip=\"Remover filtro\"\n >\n <i class=\"fa fa-trash text-red-500 hover:text-red-600\"></i>\n </button>\n </div>\n </div>\n </div>\n\n <!-- PANEL FOOTER: Add Filter & Sort -->\n <div\n class=\"-mb-2 flex flex-col items-center justify-between gap-4 border-t border-gray-200 pt-4 sm:flex-row\"\n >\n <!-- Add Filter Button -->\n <div *ngIf=\"dropdownItems.length > 0\">\n <button\n (click)=\"addFilter()\"\n class=\"transform rounded-full border-2 border-blue-300 bg-blue-50 px-6 py-2 text-sm font-medium text-blue-600 transition-all duration-300 hover:-translate-y-0.5 hover:border-blue-400 hover:bg-blue-100 hover:shadow-md\"\n matTooltip=\"Adicionar novo filtro\"\n >\n <i class=\"fa fa-plus mr-2\"></i>\n Adicionar Filtro\n </button>\n </div>\n\n <!-- Sort Dropdown -->\n <div\n class=\"w-full sm:w-auto sm:min-w-[250px]\"\n *ngIf=\"sortableDropdownItems.length > 0\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Ordenar por</mat-label>\n <mat-select placeholder=\"Selecione...\" [formControl]=\"selectSort\">\n <mat-option\n *ngFor=\"let item of sortableDropdownItems\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i class=\"fa fa-sort-alpha-down text-cyan-600\"></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </div>\n\n <!-- SIMPLE SEARCH (for non-paginated tables) -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"data.pagination === false && hasFilterableColumn === true\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-blue-500\"></i>\n Buscar\n </mat-label>\n <input\n matInput\n (keyup.enter)=\"search($event)\"\n (keyup)=\"applyFilter(filterInput.value)\"\n placeholder=\"Digite para filtrar...\"\n #filterInput\n />\n <mat-icon matSuffix class=\"text-gray-500\">search</mat-icon>\n </mat-form-field>\n <button\n *ngIf=\"data.actionButton\"\n [ngClass]=\"\n (data.actionButton.colorClass || 'bg-blue-500') +\n ' float-right flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white hover:opacity-70'\n \"\n [routerLink]=\"data.actionButton.routerLink\"\n (click)=\"\n data.actionButton.method ? data.actionButton.method($event) : null\n \"\n >\n <i *ngIf=\"data.actionButton.icon\" [class]=\"data.actionButton.icon\"></i>\n {{ data.actionButton.label }}\n </button>\n </div>\n\n <!-- FILTERS PANEL (for non-paginated tables) -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"data.pagination === false && dropdownItems.length > 0\"\n >\n <!-- FILTERS CONTENT -->\n <div class=\"mb-4 space-y-3\" *ngIf=\"filtersForm.controls.length > 0\">\n <div\n [formGroup]=\"$any(filterGroup)\"\n *ngFor=\"let filterGroup of filtersForm.controls; let i = index\"\n class=\"flex flex-wrap items-center gap-3 rounded-lg border border-gray-200 p-2\"\n >\n <!-- FILTER TYPE SELECTOR -->\n <div class=\"min-w-[200px] flex-1\" *ngIf=\"dropdownItems.length > 0\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Tipo de filtro</mat-label>\n <mat-select\n placeholder=\"Selecione o tipo...\"\n formControlName=\"selectFilter\"\n (selectionChange)=\"onSelectFilterChange()\"\n >\n <mat-option\n *ngFor=\"let item of getAvailableFilterOptions()\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i\n [class]=\"item.icon || 'fa fa-filter'\"\n class=\"text-sm text-blue-500\"\n ></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- TEXT FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange === 'filter'\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-gray-400\"></i>\n <span>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Filtrar\"\n }}</span>\n </mat-label>\n <input\n (keyup.enter)=\"search($event)\"\n formControlName=\"typeFilter\"\n matInput\n placeholder=\"Digite para filtrar...\"\n #input\n />\n </mat-form-field>\n </div>\n\n <!-- DROPDOWN FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value &&\n $any(filterGroup)\n .get('selectFilter')\n ?.value.hasOwnProperty('items')\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Selecione\"\n }}</mat-label>\n <mat-select\n placeholder=\"Selecione...\"\n formControlName=\"selectItem\"\n multiple\n >\n <mat-option\n *ngFor=\"\n let item of $any(filterGroup).get('selectFilter')?.value\n .items\n \"\n [value]=\"item\"\n >\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- DATE FILTER -->\n <div\n class=\"min-w-[340px] flex-auto\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange ===\n 'filterByDate'\n \"\n >\n <div\n class=\"flex flex-col items-stretch gap-3 sm:flex-row sm:items-center\"\n >\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Inicial</span>\n </mat-label>\n <input\n type=\"text\"\n matInput\n (keydown.enter)=\"search($event)\"\n (blur)=\"onDateFilterChange()\"\n formControlName=\"initialDate\"\n [dropSpecialCharacters]=\"false\"\n mask=\"d0/M0/0000\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Final</span>\n </mat-label>\n <input\n type=\"text\"\n (keydown.enter)=\"search($event)\"\n (blur)=\"onDateFilterChange()\"\n matInput\n formControlName=\"finalDate\"\n [dropSpecialCharacters]=\"false\"\n mask=\"d0/M0/0000\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <!-- REMOVE FILTER BUTTON -->\n <div *ngIf=\"filtersForm.length > 1\" class=\"ml-auto flex-shrink-0\">\n <button\n (click)=\"removeFilter(i)\"\n class=\"flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-300 hover:bg-red-100\"\n matTooltip=\"Remover filtro\"\n >\n <i class=\"fa fa-trash text-red-500 hover:text-red-600\"></i>\n </button>\n </div>\n </div>\n </div>\n\n <!-- PANEL FOOTER: Add Filter & Actions -->\n <div\n class=\"-mb-2 flex flex-col items-center justify-between gap-4 border-t border-gray-200 pt-4 sm:flex-row\"\n >\n <!-- Add Filter Button -->\n <div *ngIf=\"dropdownItems.length > 0\">\n <button\n (click)=\"addFilter()\"\n class=\"transform rounded-full border-2 border-blue-300 bg-blue-50 px-6 py-2 text-sm font-medium text-blue-600 transition-all duration-300 hover:-translate-y-0.5 hover:border-blue-400 hover:bg-blue-100 hover:shadow-md\"\n matTooltip=\"Adicionar novo filtro\"\n >\n <i class=\"fa fa-plus mr-2\"></i>\n Adicionar Filtro\n </button>\n </div>\n\n <!-- Action Buttons -->\n <div class=\"flex flex-wrap gap-3\">\n <button\n (click)=\"search()\"\n type=\"button\"\n class=\"flex items-center gap-2 rounded-lg bg-green-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700\"\n matTooltip=\"Aplicar filtros\"\n >\n <i class=\"fa fa-search\"></i>\n Aplicar\n </button>\n\n <button\n (click)=\"resetFilter()\"\n class=\"flex items-center gap-2 rounded-lg bg-red-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600\"\n matTooltip=\"Limpar filtros\"\n >\n <i class=\"fas fa-redo-alt\"></i>\n Resetar\n </button>\n\n <button\n *ngIf=\"data.download !== false && downloadTable\"\n class=\"flex items-center gap-2 rounded-lg bg-orange-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600\"\n matTooltipPosition=\"above\"\n matTooltip=\"Exportar Tabela\"\n [disabled]=\"\n this.dataSource && this.dataSource.filteredData.length <= 0\n \"\n (click)=\"handleDownload()\"\n >\n <i class=\"fa fa-download\"></i>\n Exportar\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"flex flex-col\">\n <div\n class=\"mx-auto flex flex-col\"\n *ngIf=\"data.tabs && data.tabs.tabsData && data.tabs.tabsData.length > 0\"\n >\n <!-- Calcular quantos grupos de 6 tabs existem -->\n <ng-container\n *ngFor=\"\n let groupIndex of getTabGroups(data.tabs.tabsData);\n let i = index\n \"\n >\n <div class=\"mx-auto flex flex-row\">\n <ng-container\n *ngFor=\"\n let tab of getTabGroup(data.tabs.tabsData, groupIndex);\n let j = index\n \"\n >\n <button\n class=\"border-2 border-gray-300 bg-gray-200 px-4 py-2 font-medium transition hover:brightness-95\"\n [ngClass]=\"\n isTabSelected(getRealTabIndex(i, j))\n ? 'border-b-0 brightness-110'\n : ''\n \"\n (click)=\"onTableSelected(i, j)\"\n >\n {{ tab.label }}\n <span\n *ngIf=\"tab.counter !== undefined\"\n class=\"ml-2 text-xs font-bold\"\n [ngClass]=\"tab.counterClass\"\n >\n {{ tab.counter }}\n </span>\n </button>\n </ng-container>\n </div>\n </ng-container>\n </div>\n <div class=\"mat-elevation-z8 w-full overflow-x-auto rounded-xl\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n #sort=\"matSort\"\n matSortActive=\"createdAt\"\n matSortDirection=\"desc\"\n >\n <ng-container\n *ngFor=\"let col of data.displayedColumns\"\n matColumnDef=\"{{ col.property }}\"\n >\n <ng-container *matHeaderCellDef>\n <!-- IF THE COLUMN IS NOT SORTABLE, THEN DON'T SHOW THE SORT BUTTONS -->\n <th\n *ngIf=\"!col.isSortable || data.pagination === true\"\n mat-header-cell\n [ngClass]=\"\n (data.color?.bg ? ' ' + $any(data.color).bg : '') +\n (data.color?.text ? ' ' + $any(data.color).text : '')\n \"\n >\n {{ col.title }}\n </th>\n <!-- IF THE COLUMN IS SORTABLE, THEN SHOW THE SORT BUTTONS -->\n <th\n *ngIf=\"col.isSortable && data.pagination === false\"\n mat-header-cell\n mat-sort-header\n [ngClass]=\"\n (data.color?.bg ? ' ' + $any(data.color).bg : '') +\n (data.color?.text ? ' ' + $any(data.color).text : '')\n \"\n >\n {{ col.title }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"col.method ? col.method(row) : null\"\n (mouseenter)=\"onCellMouseEnter($event, row, col)\"\n (mouseleave)=\"onCellMouseLeave()\"\n (mousemove)=\"onCellMouseMove($event)\"\n >\n <!-- CHECK IF THE COLUMN MUST BE DISPLAYED -->\n <span *ngIf=\"!col.image && !col.iconClass && !col.method\">\n <ng-container>\n <span\n *ngIf=\"\n col.charLimit &&\n row[col.property] &&\n row[col.property].length > col.charLimit;\n else withinLimit\n \"\n >\n <a\n *ngIf=\"col.hasLink === true\"\n [href]=\"row[col.property]\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row) }}\n </a>\n <a\n *ngIf=\"col.hasLink && isString(col.hasLink)\"\n [href]=\"col.hasLink\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row) }}\n </a>\n <span\n *ngIf=\"col.hasLink !== true && !isString(col.hasLink)\"\n >\n {{ getDisplayValue(col, row) }}\n </span>\n </span>\n </ng-container>\n <ng-template #withinLimit>\n <a\n *ngIf=\"col.hasLink === true\"\n [href]=\"row[col.property]\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row, true) }}\n </a>\n <a\n *ngIf=\"col.hasLink && isString(col.hasLink)\"\n [href]=\"col.hasLink\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row, true) }}\n </a>\n <span\n *ngIf=\"col.hasLink !== true && !isString(col.hasLink)\"\n >\n {{ getDisplayValue(col, row, true) }}\n </span>\n </ng-template>\n </span>\n <!------------------- IMAGE ------------------>\n <img\n *ngIf=\"\n col.image && col.image.path && !col.iconClass && !col.method\n \"\n [src]=\"col.image.path + '/' + row[col.property]\"\n [ngClass]=\"col.image.class\"\n alt=\"Imagem\"\n />\n <img\n *ngIf=\"\n col.image && col.image.url && !col.iconClass && !col.method\n \"\n [src]=\"row[col.property]\"\n [ngClass]=\"col.image.class\"\n alt=\"Imagem\"\n />\n <ng-container *ngIf=\"col.iconClass\">\n <button\n *ngFor=\"let iconClass of col.iconClass\"\n (click)=\"\n iconClass.buttonMethod\n ? iconClass.buttonMethod(row, $event)\n : $event.stopPropagation()\n \"\n >\n <span\n [ngClass]=\"iconClass.class\"\n *ngIf=\"\n iconClass.condition === undefined ||\n (iconClass.condition !== undefined &&\n $any(iconClass.condition)(row))\n \"\n >{{ iconClass.text }}</span\n >\n </button>\n </ng-container>\n </td>\n </ng-container>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"columnProperties\"></tr>\n <tr\n [ngClass]=\"{\n 'example-element-row': data.isNotClickable === true,\n 'example-element-row cursor-pointer': !data.isNotClickable\n }\"\n mat-row\n *matRowDef=\"let row; columns: columnProperties\"\n (click)=\"goToDetails(row)\"\n ></tr>\n\n <!-- ROW SHOWN WHEN THERE IS NO MATCHING DATA. -->\n <tr class=\"mat-row\" *matNoDataRow>\n <td *ngIf=\"!isLoading\" class=\"mat-cell p-4\" colspan=\"4\">\n Nenhum resultado encontrado para a busca\n </td>\n </tr>\n </table>\n\n <div class=\"flex justify-center\" *ngIf=\"isLoading\">\n <mat-spinner></mat-spinner>\n </div>\n\n <div class=\"paginator-container\">\n <mat-paginator\n #paginator\n [pageSizeOptions]=\"[25, 50, 100]\"\n [pageSize]=\"pageSize\"\n [length]=\"totalItems\"\n showFirstLastButtons\n aria-label=\"Select page of periodic elements\"\n (page)=\"onPageChange($event)\"\n [ngClass]=\"{\n 'hide-length':\n ['filter', 'filterByDate', 'equals'].includes(\n this.currentArrange\n ) || this.data.filterFn,\n 'hide-next-button': !hasNextPage && data.pagination === true,\n 'hide-last-button':\n (!hasNextPage && data.pagination === true) || this.data.filterFn\n }\"\n >\n </mat-paginator>\n <div\n *ngIf=\"\n !isLoading &&\n dataSource?.data &&\n dataSource.data.length > 0 &&\n data?.filterFn\n \"\n class=\"page-number-display\"\n >\n {{ currentPageNumber }}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- TOOLTIP PERSONALIZADO -->\n <div\n *ngIf=\"showTooltip\"\n class=\"fixed z-50 max-w-md break-words rounded-lg bg-gray-800 px-3 py-2 text-sm text-white shadow-lg\"\n [style.left.px]=\"tooltipPosition.x\"\n [style.top.px]=\"tooltipPosition.y\"\n [style.pointer-events]=\"'none'\"\n >\n {{ tooltipContent }}\n </div>\n</div>\n", styles: ["::ng-deep .hide-length .mat-mdc-paginator-range-label{display:none}::ng-deep .hide-next-button .mat-mdc-tooltip-trigger.mat-mdc-paginator-navigation-next.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base{visibility:hidden}::ng-deep .hide-next-button .mat-mdc-tooltip-trigger.mat-mdc-paginator-navigation-last.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base.ng-star-inserted{visibility:hidden}::ng-deep .mat-mdc-text-field-wrapper.mdc-text-field.ng-tns-c162-1.mdc-text-field--filled{width:25dvw}::ng-deep .custom-filter .mat-mdc-text-field-wrapper{width:20dvw;max-width:300px}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i5.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6.MatNoDataRow, selector: "ng-template[matNoDataRow]" }, { kind: "component", type: i7.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "directive", type: i8.MatSort, selector: "[matSort]", inputs: ["matSortDisabled", "matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i8.MatSortHeader, selector: "[mat-sort-header]", inputs: ["disabled", "mat-sort-header", "arrowPosition", "start", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i9.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i9.MatLabel, selector: "mat-label" }, { kind: "directive", type: i9.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i10.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i11.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator"], exportAs: ["matSelect"] }, { kind: "component", type: i12.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { kind: "directive", type: i13.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { kind: "component", type: i14.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: i15.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i16.NgxMaskDirective, selector: "input[mask], textarea[mask]", inputs: ["mask", "specialCharacters", "patterns", "prefix", "suffix", "thousandSeparator", "decimalMarker", "dropSpecialCharacters", "hiddenInput", "showMaskTyped", "placeHolderCharacter", "shownMaskExpression", "showTemplate", "clearIfNotMatch", "validation", "separatorLimit", "allowNegativeNumbers", "leadZeroDateTime", "leadZero", "triggerOnMaskChange", "apm", "inputTransformFn", "outputTransformFn", "keepCharacterPositions"], outputs: ["maskFilled"], exportAs: ["mask", "ngxMask"] }] });
2264
+ TableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TableComponent, deps: [{ token: i1$1.Router }, { token: TableService }, { token: i1.AngularFirestore }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
2265
+ TableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: TableComponent, selector: "lib-table", inputs: { data: "data", downloadTable: "downloadTable" }, viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"data\" class=\"card-body\">\n <div class=\"flex flex-col justify-between gap-6\">\n <!-- UNIFIED CONTROL PANEL: FILTERS, SORT & ACTIONS -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"\n data.pagination === true &&\n (dropdownItems.length > 0 ||\n sortableDropdownItems.length > 0 ||\n data.actionButton)\n \"\n >\n <!-- PANEL HEADER: Title, Custom Action, and Global Actions -->\n <div\n class=\"mb-4 flex flex-col items-start justify-between gap-4 border-b-2 border-gray-200 pb-4 md:flex-row md:items-center\"\n >\n <!-- Left Side: Title & Main Action Button -->\n <div class=\"flex flex-wrap items-center gap-4\">\n <div class=\"flex items-center gap-2\">\n <i class=\"fa fa-filter text-xl text-blue-500\"></i>\n <span class=\"text-lg font-semibold text-gray-700\"\n >Filtros e A\u00E7\u00F5es</span\n >\n </div>\n <button\n *ngIf=\"data.actionButton && data.actionButton.condition\"\n [ngClass]=\"\n (data.actionButton.colorClass || 'bg-blue-500') +\n ' flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white hover:opacity-70'\n \"\n [routerLink]=\"data.actionButton.routerLink\"\n (click)=\"\n data.actionButton.method ? data.actionButton.method($event) : null\n \"\n >\n <i\n *ngIf=\"data.actionButton.icon\"\n [class]=\"data.actionButton.icon\"\n ></i>\n {{ data.actionButton.label }}\n </button>\n </div>\n\n <!-- Right Side: Search, Reset, Export -->\n <div\n class=\"flex flex-wrap gap-3\"\n *ngIf=\"\n this.hasFilterableColumn === true || this.hasSortableColumn === true\n \"\n >\n <button\n (click)=\"search()\"\n type=\"button\"\n class=\"flex items-center gap-2 rounded-lg bg-green-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700\"\n matTooltip=\"Aplicar filtros\"\n >\n <i class=\"fa fa-search\"></i>\n Pesquisar\n </button>\n\n <button\n (click)=\"resetFilter()\"\n class=\"flex items-center gap-2 rounded-lg bg-red-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600\"\n matTooltip=\"Limpar filtros\"\n >\n <i class=\"fas fa-redo-alt\"></i>\n Resetar\n </button>\n\n <button\n *ngIf=\"data.download !== false && downloadTable\"\n class=\"flex items-center gap-2 rounded-lg bg-orange-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600\"\n matTooltipPosition=\"above\"\n matTooltip=\"Exportar Tabela\"\n [disabled]=\"\n this.dataSource && this.dataSource.filteredData.length <= 0\n \"\n (click)=\"\n $any(arrange) && downloadTable !== undefined\n ? downloadTable($any(arrange), data.conditions || [])\n : null\n \"\n >\n <i class=\"fa fa-download\"></i>\n Exportar\n </button>\n </div>\n </div>\n\n <!-- FILTERS CONTENT (WITH REFINEMENTS) -->\n <div class=\"mb-4 space-y-3\" *ngIf=\"filtersForm.controls.length > 0\">\n <div\n [formGroup]=\"$any(filterGroup)\"\n *ngFor=\"let filterGroup of filtersForm.controls; let i = index\"\n class=\"flex flex-wrap items-center gap-3 rounded-lg border border-gray-200 p-2\"\n >\n <!-- FILTER TYPE SELECTOR -->\n <div class=\"min-w-[200px] flex-1\" *ngIf=\"dropdownItems.length > 0\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Tipo de filtro</mat-label>\n <mat-select\n placeholder=\"Selecione o tipo...\"\n formControlName=\"selectFilter\"\n (selectionChange)=\"onSelectFilterChange()\"\n >\n <mat-option\n *ngFor=\"let item of getAvailableFilterOptions()\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i\n [class]=\"item.icon || 'fa fa-filter'\"\n class=\"text-sm text-blue-500\"\n ></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- TEXT FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange === 'filter'\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-gray-400\"></i>\n <span>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Filtrar\"\n }}</span>\n </mat-label>\n <input\n (keyup.enter)=\"search($event)\"\n formControlName=\"typeFilter\"\n matInput\n placeholder=\"Digite para filtrar...\"\n #input\n />\n </mat-form-field>\n </div>\n\n <!-- DROPDOWN FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value &&\n $any(filterGroup)\n .get('selectFilter')\n ?.value.hasOwnProperty('items')\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Selecione\"\n }}</mat-label>\n <mat-select\n placeholder=\"Selecione...\"\n formControlName=\"selectItem\"\n multiple\n >\n <mat-option\n *ngFor=\"\n let item of $any(filterGroup).get('selectFilter')?.value\n .items\n \"\n [value]=\"item\"\n >\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- DATE FILTER -->\n <div\n class=\"min-w-[340px] flex-auto\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange ===\n 'filterByDate'\n \"\n >\n <div\n class=\"flex flex-col items-stretch gap-3 sm:flex-row sm:items-center\"\n >\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Inicial</span>\n </mat-label>\n <input\n type=\"text\"\n matInput\n (keydown.enter)=\"search($event)\"\n (input)=\"onDateInput($event, 'initialDate', i)\"\n formControlName=\"initialDate\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Final</span>\n </mat-label>\n <input\n type=\"text\"\n (keydown.enter)=\"search($event)\"\n (input)=\"onDateInput($event, 'finalDate', i)\"\n matInput\n formControlName=\"finalDate\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <!-- REMOVE FILTER BUTTON -->\n <div *ngIf=\"filtersForm.length > 1\" class=\"ml-auto flex-shrink-0\">\n <button\n (click)=\"removeFilter(i)\"\n class=\"flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-300 hover:bg-red-100\"\n matTooltip=\"Remover filtro\"\n >\n <i class=\"fa fa-trash text-red-500 hover:text-red-600\"></i>\n </button>\n </div>\n </div>\n </div>\n\n <!-- PANEL FOOTER: Add Filter & Sort -->\n <div\n class=\"-mb-2 flex flex-col items-center justify-between gap-4 border-t border-gray-200 pt-4 sm:flex-row\"\n >\n <!-- Add Filter Button -->\n <div *ngIf=\"dropdownItems.length > 0\">\n <button\n (click)=\"addFilter()\"\n class=\"transform rounded-full border-2 border-blue-300 bg-blue-50 px-6 py-2 text-sm font-medium text-blue-600 transition-all duration-300 hover:-translate-y-0.5 hover:border-blue-400 hover:bg-blue-100 hover:shadow-md\"\n matTooltip=\"Adicionar novo filtro\"\n >\n <i class=\"fa fa-plus mr-2\"></i>\n Adicionar Filtro\n </button>\n </div>\n\n <!-- Sort Dropdown -->\n <div\n class=\"w-full sm:w-auto sm:min-w-[250px]\"\n *ngIf=\"sortableDropdownItems.length > 0\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Ordenar por</mat-label>\n <mat-select placeholder=\"Selecione...\" [formControl]=\"selectSort\">\n <mat-option\n *ngFor=\"let item of sortableDropdownItems\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i class=\"fa fa-sort-alpha-down text-cyan-600\"></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </div>\n\n <!-- SIMPLE SEARCH (for non-paginated tables) -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"data.pagination === false && hasFilterableColumn === true\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-blue-500\"></i>\n Buscar\n </mat-label>\n <input\n matInput\n (keyup.enter)=\"search($event)\"\n (keyup)=\"applyFilter(filterInput.value)\"\n placeholder=\"Digite para filtrar...\"\n #filterInput\n />\n <mat-icon matSuffix class=\"text-gray-500\">search</mat-icon>\n </mat-form-field>\n <button\n *ngIf=\"data.actionButton\"\n [ngClass]=\"\n (data.actionButton.colorClass || 'bg-blue-500') +\n ' float-right flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white hover:opacity-70'\n \"\n [routerLink]=\"data.actionButton.routerLink\"\n (click)=\"\n data.actionButton.method ? data.actionButton.method($event) : null\n \"\n >\n <i *ngIf=\"data.actionButton.icon\" [class]=\"data.actionButton.icon\"></i>\n {{ data.actionButton.label }}\n </button>\n </div>\n\n <!-- FILTERS PANEL (for non-paginated tables) -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"data.pagination === false && dropdownItems.length > 0\"\n >\n <!-- FILTERS CONTENT -->\n <div class=\"mb-4 space-y-3\" *ngIf=\"filtersForm.controls.length > 0\">\n <div\n [formGroup]=\"$any(filterGroup)\"\n *ngFor=\"let filterGroup of filtersForm.controls; let i = index\"\n class=\"flex flex-wrap items-center gap-3 rounded-lg border border-gray-200 p-2\"\n >\n <!-- FILTER TYPE SELECTOR -->\n <div class=\"min-w-[200px] flex-1\" *ngIf=\"dropdownItems.length > 0\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Tipo de filtro</mat-label>\n <mat-select\n placeholder=\"Selecione o tipo...\"\n formControlName=\"selectFilter\"\n (selectionChange)=\"onSelectFilterChange()\"\n >\n <mat-option\n *ngFor=\"let item of getAvailableFilterOptions()\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i\n [class]=\"item.icon || 'fa fa-filter'\"\n class=\"text-sm text-blue-500\"\n ></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- TEXT FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange === 'filter'\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-gray-400\"></i>\n <span>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Filtrar\"\n }}</span>\n </mat-label>\n <input\n (keyup.enter)=\"search($event)\"\n formControlName=\"typeFilter\"\n matInput\n placeholder=\"Digite para filtrar...\"\n #input\n />\n </mat-form-field>\n </div>\n\n <!-- DROPDOWN FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value &&\n $any(filterGroup)\n .get('selectFilter')\n ?.value.hasOwnProperty('items')\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Selecione\"\n }}</mat-label>\n <mat-select\n placeholder=\"Selecione...\"\n formControlName=\"selectItem\"\n multiple\n >\n <mat-option\n *ngFor=\"\n let item of $any(filterGroup).get('selectFilter')?.value\n .items\n \"\n [value]=\"item\"\n >\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- DATE FILTER -->\n <div\n class=\"min-w-[340px] flex-auto\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange ===\n 'filterByDate'\n \"\n >\n <div\n class=\"flex flex-col items-stretch gap-3 sm:flex-row sm:items-center\"\n >\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Inicial</span>\n </mat-label>\n <input\n type=\"text\"\n matInput\n (keydown.enter)=\"search($event)\"\n (input)=\"onDateInput($event, 'initialDate', i)\"\n (blur)=\"onDateFilterChange()\"\n formControlName=\"initialDate\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Final</span>\n </mat-label>\n <input\n type=\"text\"\n (keydown.enter)=\"search($event)\"\n (input)=\"onDateInput($event, 'finalDate', i)\"\n (blur)=\"onDateFilterChange()\"\n matInput\n formControlName=\"finalDate\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <!-- REMOVE FILTER BUTTON -->\n <div *ngIf=\"filtersForm.length > 1\" class=\"ml-auto flex-shrink-0\">\n <button\n (click)=\"removeFilter(i)\"\n class=\"flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-300 hover:bg-red-100\"\n matTooltip=\"Remover filtro\"\n >\n <i class=\"fa fa-trash text-red-500 hover:text-red-600\"></i>\n </button>\n </div>\n </div>\n </div>\n\n <!-- PANEL FOOTER: Add Filter & Actions -->\n <div\n class=\"-mb-2 flex flex-col items-center justify-between gap-4 border-t border-gray-200 pt-4 sm:flex-row\"\n >\n <!-- Add Filter Button -->\n <div *ngIf=\"dropdownItems.length > 0\">\n <button\n (click)=\"addFilter()\"\n class=\"transform rounded-full border-2 border-blue-300 bg-blue-50 px-6 py-2 text-sm font-medium text-blue-600 transition-all duration-300 hover:-translate-y-0.5 hover:border-blue-400 hover:bg-blue-100 hover:shadow-md\"\n matTooltip=\"Adicionar novo filtro\"\n >\n <i class=\"fa fa-plus mr-2\"></i>\n Adicionar Filtro\n </button>\n </div>\n\n <!-- Action Buttons -->\n <div class=\"flex flex-wrap gap-3\">\n <button\n (click)=\"search()\"\n type=\"button\"\n class=\"flex items-center gap-2 rounded-lg bg-green-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700\"\n matTooltip=\"Aplicar filtros\"\n >\n <i class=\"fa fa-search\"></i>\n Aplicar\n </button>\n\n <button\n (click)=\"resetFilter()\"\n class=\"flex items-center gap-2 rounded-lg bg-red-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600\"\n matTooltip=\"Limpar filtros\"\n >\n <i class=\"fas fa-redo-alt\"></i>\n Resetar\n </button>\n\n <button\n *ngIf=\"data.download !== false && downloadTable\"\n class=\"flex items-center gap-2 rounded-lg bg-orange-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600\"\n matTooltipPosition=\"above\"\n matTooltip=\"Exportar Tabela\"\n [disabled]=\"\n this.dataSource && this.dataSource.filteredData.length <= 0\n \"\n (click)=\"handleDownload()\"\n >\n <i class=\"fa fa-download\"></i>\n Exportar\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"flex flex-col\">\n <div\n class=\"mx-auto flex flex-col\"\n *ngIf=\"data.tabs && data.tabs.tabsData && data.tabs.tabsData.length > 0\"\n >\n <!-- Calcular quantos grupos de 6 tabs existem -->\n <ng-container\n *ngFor=\"\n let groupIndex of getTabGroups(data.tabs.tabsData);\n let i = index\n \"\n >\n <div class=\"mx-auto flex flex-row\">\n <ng-container\n *ngFor=\"\n let tab of getTabGroup(data.tabs.tabsData, groupIndex);\n let j = index\n \"\n >\n <button\n class=\"border-2 border-gray-300 bg-gray-200 px-4 py-2 font-medium transition hover:brightness-95\"\n [ngClass]=\"\n isTabSelected(getRealTabIndex(i, j))\n ? 'border-b-0 brightness-110'\n : ''\n \"\n (click)=\"onTableSelected(i, j)\"\n >\n {{ tab.label }}\n <span\n *ngIf=\"tab.counter !== undefined\"\n class=\"ml-2 text-xs font-bold\"\n [ngClass]=\"tab.counterClass\"\n >\n {{ tab.counter }}\n </span>\n </button>\n </ng-container>\n </div>\n </ng-container>\n </div>\n <div\n class=\"mat-elevation-z8 w-full overflow-x-auto rounded-xl table-container\"\n >\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n #sort=\"matSort\"\n matSortActive=\"createdAt\"\n matSortDirection=\"desc\"\n >\n <ng-container\n *ngFor=\"let col of data.displayedColumns\"\n matColumnDef=\"{{ col.property }}\"\n >\n <ng-container *matHeaderCellDef>\n <!-- IF THE COLUMN IS NOT SORTABLE, THEN DON'T SHOW THE SORT BUTTONS -->\n <th\n *ngIf=\"!col.isSortable || data.pagination === true\"\n mat-header-cell\n >\n {{ col.title }}\n </th>\n <!-- IF THE COLUMN IS SORTABLE, THEN SHOW THE SORT BUTTONS -->\n <th\n *ngIf=\"col.isSortable && data.pagination === false\"\n mat-header-cell\n mat-sort-header\n >\n {{ col.title }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"col.method ? col.method(row) : null\"\n (mouseenter)=\"onCellMouseEnter($event, row, col)\"\n (mouseleave)=\"onCellMouseLeave()\"\n (mousemove)=\"onCellMouseMove($event)\"\n >\n <!-- CHECK IF THE COLUMN MUST BE DISPLAYED -->\n <span *ngIf=\"!col.image && !col.iconClass && !col.method\">\n <ng-container>\n <span\n *ngIf=\"\n col.charLimit &&\n row[col.property] &&\n row[col.property].length > col.charLimit;\n else withinLimit\n \"\n >\n <a\n *ngIf=\"col.hasLink === true\"\n [href]=\"row[col.property]\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row) }}\n </a>\n <a\n *ngIf=\"col.hasLink && isString(col.hasLink)\"\n [href]=\"col.hasLink\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row) }}\n </a>\n <span\n *ngIf=\"col.hasLink !== true && !isString(col.hasLink)\"\n >\n {{ getDisplayValue(col, row) }}\n </span>\n </span>\n </ng-container>\n <ng-template #withinLimit>\n <a\n *ngIf=\"col.hasLink === true\"\n [href]=\"row[col.property]\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row, true) }}\n </a>\n <a\n *ngIf=\"col.hasLink && isString(col.hasLink)\"\n [href]=\"col.hasLink\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row, true) }}\n </a>\n <span\n *ngIf=\"col.hasLink !== true && !isString(col.hasLink)\"\n >\n {{ getDisplayValue(col, row, true) }}\n </span>\n </ng-template>\n </span>\n <!------------------- IMAGE ------------------>\n <img\n *ngIf=\"\n col.image && col.image.path && !col.iconClass && !col.method\n \"\n [src]=\"col.image.path + '/' + row[col.property]\"\n [ngClass]=\"col.image.class\"\n alt=\"Imagem\"\n />\n <img\n *ngIf=\"\n col.image && col.image.url && !col.iconClass && !col.method\n \"\n [src]=\"row[col.property]\"\n [ngClass]=\"col.image.class\"\n alt=\"Imagem\"\n />\n <ng-container *ngIf=\"col.iconClass\">\n <button\n *ngFor=\"let iconClass of col.iconClass\"\n (click)=\"\n iconClass.buttonMethod\n ? iconClass.buttonMethod(row, $event)\n : $event.stopPropagation()\n \"\n >\n <span\n [ngClass]=\"iconClass.class\"\n *ngIf=\"\n iconClass.condition === undefined ||\n (iconClass.condition !== undefined &&\n $any(iconClass.condition)(row))\n \"\n >{{ iconClass.text }}</span\n >\n </button>\n </ng-container>\n </td>\n </ng-container>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"columnProperties\"></tr>\n <tr\n [ngClass]=\"{\n 'example-element-row': data.isNotClickable === true,\n 'example-element-row cursor-pointer': !data.isNotClickable\n }\"\n mat-row\n *matRowDef=\"let row; columns: columnProperties\"\n (click)=\"goToDetails(row)\"\n ></tr>\n\n <!-- ROW SHOWN WHEN THERE IS NO MATCHING DATA. -->\n <tr class=\"mat-row\" *matNoDataRow>\n <td *ngIf=\"!isLoading\" class=\"mat-cell p-4\" colspan=\"4\">\n Nenhum resultado encontrado para a busca\n </td>\n </tr>\n </table>\n\n <div class=\"flex justify-center\" *ngIf=\"isLoading\">\n <mat-spinner></mat-spinner>\n </div>\n\n <div class=\"paginator-container\">\n <mat-paginator\n #paginator\n [pageSizeOptions]=\"[25, 50, 100]\"\n [pageSize]=\"pageSize\"\n [length]=\"totalItems\"\n showFirstLastButtons\n aria-label=\"Select page of periodic elements\"\n (page)=\"onPageChange($event)\"\n [ngClass]=\"{\n 'hide-length':\n ['filter', 'filterByDate', 'equals'].includes(\n this.currentArrange\n ) || this.data.filterFn,\n 'hide-next-button': !hasNextPage && data.pagination === true,\n 'hide-last-button':\n (!hasNextPage && data.pagination === true) || this.data.filterFn\n }\"\n >\n </mat-paginator>\n <div\n *ngIf=\"\n !isLoading &&\n dataSource?.data &&\n dataSource.data.length > 0 &&\n data?.filterFn\n \"\n class=\"page-number-display\"\n >\n {{ currentPageNumber }}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- TOOLTIP PERSONALIZADO -->\n <div\n *ngIf=\"showTooltip\"\n class=\"fixed z-50 max-w-md break-words rounded-lg bg-gray-800 px-3 py-2 text-sm text-white shadow-lg\"\n [style.left.px]=\"tooltipPosition.x\"\n [style.top.px]=\"tooltipPosition.y\"\n [style.pointer-events]=\"'none'\"\n >\n {{ tooltipContent }}\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--table-color: #1976d2;--table-text-color: white;--table-header-bg: #1976d2;--table-header-text: white;--table-scrollbar-bg: #f5f5f5;--table-scrollbar-thumb: var(--table-color)}.table-container::-webkit-scrollbar{width:10px;height:10px;background-color:var(--table-scrollbar-bg, #f5f5f5)}.table-container::-webkit-scrollbar-thumb{background-color:var(--table-scrollbar-thumb, var(--table-color, #1976d2));border-radius:5px}.table-container::-webkit-scrollbar-thumb:hover{background-color:var(--table-scrollbar-thumb, var(--table-color, #1976d2));opacity:.8}.overflow-x-auto::-webkit-scrollbar,.overflow-y-auto::-webkit-scrollbar,.overflow-auto::-webkit-scrollbar{width:10px;height:10px;background-color:var(--table-scrollbar-bg, #f5f5f5)}.overflow-x-auto::-webkit-scrollbar-thumb,.overflow-y-auto::-webkit-scrollbar-thumb,.overflow-auto::-webkit-scrollbar-thumb{background-color:var(--table-scrollbar-thumb, var(--table-color, #1976d2));border-radius:5px}.overflow-x-auto::-webkit-scrollbar-thumb:hover,.overflow-y-auto::-webkit-scrollbar-thumb:hover,.overflow-auto::-webkit-scrollbar-thumb:hover{background-color:var(--table-scrollbar-thumb, var(--table-color, #1976d2));opacity:.8}:host ::ng-deep .mat-table .mat-header-cell,:host ::ng-deep .mat-header-cell,:host ::ng-deep th.mat-header-cell,:host ::ng-deep .mat-mdc-table .mat-mdc-header-cell,:host ::ng-deep .mat-mdc-header-cell,:host ::ng-deep th.mat-mdc-header-cell{background-color:var(--table-header-bg, #1976d2)!important;color:var(--table-header-text, white)!important;font-weight:600}::ng-deep .fa-filter,::ng-deep i.fa-filter{color:var(--table-color, #1976d2)!important}::ng-deep .rounded-xl.border{border-color:var(--table-color, #1976d2)!important}::ng-deep .border-gray-200.rounded-xl{border-color:var(--table-color, #1976d2)!important}::ng-deep .mdc-notched-outline__notch{border-right:none!important}.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix{padding-bottom:0}::ng-deep .mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix{padding-bottom:0}.mat-mdc-text-field-wrapper.mdc-text-field--outlined.mdc-text-field--disabled{background:rgba(0,0,0,.15);background-size:1px 100%!important;background-repeat:repeat-x!important}.mdc-text-field--disabled .mdc-text-field__input{color:#525252}button:not([matTooltip=\"Adicionar novo filtro\"]):not([matTooltip=\"Aplicar filtros\"]):not([matTooltip=\"Limpar filtros\"]):not([matTooltip=\"Exportar Tabela\"]):not(:has(i.fa-plus)):not(:has(i.fa-search)):not(:has(i.fas.fa-redo-alt)):not(:has(i.fa-download)){transition:all .2s ease;cursor:pointer}button:not([matTooltip=\"Adicionar novo filtro\"]):not([matTooltip=\"Aplicar filtros\"]):not([matTooltip=\"Limpar filtros\"]):not([matTooltip=\"Exportar Tabela\"]):not(:has(i.fa-plus)):not(:has(i.fa-search)):not(:has(i.fas.fa-redo-alt)):not(:has(i.fa-download)):hover:not(:disabled){opacity:.9;transform:scale(1.02)}button:not([matTooltip=\"Adicionar novo filtro\"]):not([matTooltip=\"Aplicar filtros\"]):not([matTooltip=\"Limpar filtros\"]):not([matTooltip=\"Exportar Tabela\"]):not(:has(i.fa-plus)):not(:has(i.fa-search)):not(:has(i.fas.fa-redo-alt)):not(:has(i.fa-download)):disabled{opacity:.5;cursor:not-allowed;transform:none}::ng-deep .hide-length .mat-mdc-paginator-range-label{display:none}::ng-deep .hide-next-button .mat-mdc-tooltip-trigger.mat-mdc-paginator-navigation-next.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base{visibility:hidden}::ng-deep .hide-next-button .mat-mdc-tooltip-trigger.mat-mdc-paginator-navigation-last.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base.ng-star-inserted{visibility:hidden}::ng-deep .mat-mdc-text-field-wrapper.mdc-text-field.ng-tns-c162-1.mdc-text-field--filled{width:25dvw}::ng-deep .custom-filter .mat-mdc-text-field-wrapper{width:20dvw;max-width:300px}button[matTooltip=\"Adicionar novo filtro\"],button:has(i.fa-plus){display:inline-flex;align-items:center;gap:.5rem;border-radius:9999px;border:2px solid #93c5fd;background-color:#dbeafe;padding:.5rem 1.5rem;font-size:.875rem;font-weight:500;color:#2563eb;transition:all .3s;cursor:pointer;transform:translateY(0)}button[matTooltip=\"Adicionar novo filtro\"]:hover,button:has(i.fa-plus):hover{transform:translateY(-2px);border-color:#60a5fa;background-color:#bfdbfe;box-shadow:0 4px 6px #0000001a}button[matTooltip=\"Aplicar filtros\"],button:has(i.fa-search){display:inline-flex;align-items:center;gap:.5rem;border-radius:.5rem;background-color:#16a34a;padding:.5rem 1.25rem;font-size:.875rem;font-weight:500;color:#fff;transition:background-color .2s;cursor:pointer;border:none}button[matTooltip=\"Aplicar filtros\"]:hover,button:has(i.fa-search):hover{background-color:#15803d}button[matTooltip=\"Aplicar filtros\"]:disabled,button:has(i.fa-search):disabled{opacity:.5;cursor:not-allowed}button[matTooltip=\"Limpar filtros\"],button:has(i.fas.fa-redo-alt){display:inline-flex;align-items:center;gap:.5rem;border-radius:.5rem;background-color:#ef4444;padding:.5rem 1.25rem;font-size:.875rem;font-weight:500;color:#fff;transition:background-color .2s;cursor:pointer;border:none}button[matTooltip=\"Limpar filtros\"]:hover,button:has(i.fas.fa-redo-alt):hover{background-color:#dc2626}button[matTooltip=\"Exportar Tabela\"],button:has(i.fa-download){display:inline-flex;align-items:center;gap:.5rem;border-radius:.5rem;background-color:#f97316;padding:.5rem 1.25rem;font-size:.875rem;font-weight:500;color:#fff;transition:background-color .2s;cursor:pointer;border:none}button[matTooltip=\"Exportar Tabela\"]:hover:not(:disabled),button:has(i.fa-download):hover:not(:disabled){background-color:#ea580c}button[matTooltip=\"Exportar Tabela\"]:disabled,button:has(i.fa-download):disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i5.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6.MatNoDataRow, selector: "ng-template[matNoDataRow]" }, { kind: "component", type: i7.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "directive", type: i8.MatSort, selector: "[matSort]", inputs: ["matSortDisabled", "matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i8.MatSortHeader, selector: "[mat-sort-header]", inputs: ["disabled", "mat-sort-header", "arrowPosition", "start", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i9.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i9.MatLabel, selector: "mat-label" }, { kind: "directive", type: i9.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i10.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i11.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator"], exportAs: ["matSelect"] }, { kind: "component", type: i12.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { kind: "directive", type: i13.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { kind: "component", type: i14.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: i15.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
2213
2266
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TableComponent, decorators: [{
2214
2267
  type: Component,
2215
- args: [{ selector: 'lib-table', template: "<div *ngIf=\"data\" class=\"card-body\">\n <div class=\"flex flex-col justify-between gap-6\">\n <!-- UNIFIED CONTROL PANEL: FILTERS, SORT & ACTIONS -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"\n data.pagination === true &&\n (dropdownItems.length > 0 ||\n sortableDropdownItems.length > 0 ||\n data.actionButton)\n \"\n >\n <!-- PANEL HEADER: Title, Custom Action, and Global Actions -->\n <div\n class=\"mb-4 flex flex-col items-start justify-between gap-4 border-b-2 border-gray-200 pb-4 md:flex-row md:items-center\"\n >\n <!-- Left Side: Title & Main Action Button -->\n <div class=\"flex flex-wrap items-center gap-4\">\n <div class=\"flex items-center gap-2\">\n <i class=\"fa fa-filter text-xl text-blue-500\"></i>\n <span class=\"text-lg font-semibold text-gray-700\"\n >Filtros e A\u00E7\u00F5es</span\n >\n </div>\n <button\n *ngIf=\"data.actionButton && data.actionButton.condition\"\n [ngClass]=\"\n (data.actionButton.colorClass || 'bg-blue-500') +\n ' flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white hover:opacity-70'\n \"\n [routerLink]=\"data.actionButton.routerLink\"\n (click)=\"\n data.actionButton.method ? data.actionButton.method($event) : null\n \"\n >\n <i\n *ngIf=\"data.actionButton.icon\"\n [class]=\"data.actionButton.icon\"\n ></i>\n {{ data.actionButton.label }}\n </button>\n </div>\n\n <!-- Right Side: Search, Reset, Export -->\n <div\n class=\"flex flex-wrap gap-3\"\n *ngIf=\"\n this.hasFilterableColumn === true || this.hasSortableColumn === true\n \"\n >\n <button\n (click)=\"search()\"\n type=\"button\"\n class=\"flex items-center gap-2 rounded-lg bg-green-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700\"\n matTooltip=\"Aplicar filtros\"\n >\n <i class=\"fa fa-search\"></i>\n Pesquisar\n </button>\n\n <button\n (click)=\"resetFilter()\"\n class=\"flex items-center gap-2 rounded-lg bg-red-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600\"\n matTooltip=\"Limpar filtros\"\n >\n <i class=\"fas fa-redo-alt\"></i>\n Resetar\n </button>\n\n <button\n *ngIf=\"data.download !== false && downloadTable\"\n class=\"flex items-center gap-2 rounded-lg bg-orange-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600\"\n matTooltipPosition=\"above\"\n matTooltip=\"Exportar Tabela\"\n [disabled]=\"\n this.dataSource && this.dataSource.filteredData.length <= 0\n \"\n (click)=\"\n $any(arrange) && downloadTable !== undefined\n ? downloadTable($any(arrange), data.conditions || [])\n : null\n \"\n >\n <i class=\"fa fa-download\"></i>\n Exportar\n </button>\n </div>\n </div>\n\n <!-- FILTERS CONTENT (WITH REFINEMENTS) -->\n <div class=\"mb-4 space-y-3\" *ngIf=\"filtersForm.controls.length > 0\">\n <div\n [formGroup]=\"$any(filterGroup)\"\n *ngFor=\"let filterGroup of filtersForm.controls; let i = index\"\n class=\"flex flex-wrap items-center gap-3 rounded-lg border border-gray-200 p-2\"\n >\n <!-- FILTER TYPE SELECTOR -->\n <div class=\"min-w-[200px] flex-1\" *ngIf=\"dropdownItems.length > 0\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Tipo de filtro</mat-label>\n <mat-select\n placeholder=\"Selecione o tipo...\"\n formControlName=\"selectFilter\"\n (selectionChange)=\"onSelectFilterChange()\"\n >\n <mat-option\n *ngFor=\"let item of getAvailableFilterOptions()\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i\n [class]=\"item.icon || 'fa fa-filter'\"\n class=\"text-sm text-blue-500\"\n ></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- TEXT FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange === 'filter'\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-gray-400\"></i>\n <span>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Filtrar\"\n }}</span>\n </mat-label>\n <input\n (keyup.enter)=\"search($event)\"\n formControlName=\"typeFilter\"\n matInput\n placeholder=\"Digite para filtrar...\"\n #input\n />\n </mat-form-field>\n </div>\n\n <!-- DROPDOWN FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value &&\n $any(filterGroup)\n .get('selectFilter')\n ?.value.hasOwnProperty('items')\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Selecione\"\n }}</mat-label>\n <mat-select\n placeholder=\"Selecione...\"\n formControlName=\"selectItem\"\n multiple\n >\n <mat-option\n *ngFor=\"\n let item of $any(filterGroup).get('selectFilter')?.value\n .items\n \"\n [value]=\"item\"\n >\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- DATE FILTER -->\n <div\n class=\"min-w-[340px] flex-auto\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange ===\n 'filterByDate'\n \"\n >\n <div\n class=\"flex flex-col items-stretch gap-3 sm:flex-row sm:items-center\"\n >\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Inicial</span>\n </mat-label>\n <input\n type=\"text\"\n matInput\n (keydown.enter)=\"search($event)\"\n formControlName=\"initialDate\"\n [dropSpecialCharacters]=\"false\"\n mask=\"d0/M0/0000\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Final</span>\n </mat-label>\n <input\n type=\"text\"\n (keydown.enter)=\"search($event)\"\n matInput\n formControlName=\"finalDate\"\n [dropSpecialCharacters]=\"false\"\n mask=\"d0/M0/0000\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <!-- REMOVE FILTER BUTTON -->\n <div *ngIf=\"filtersForm.length > 1\" class=\"ml-auto flex-shrink-0\">\n <button\n (click)=\"removeFilter(i)\"\n class=\"flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-300 hover:bg-red-100\"\n matTooltip=\"Remover filtro\"\n >\n <i class=\"fa fa-trash text-red-500 hover:text-red-600\"></i>\n </button>\n </div>\n </div>\n </div>\n\n <!-- PANEL FOOTER: Add Filter & Sort -->\n <div\n class=\"-mb-2 flex flex-col items-center justify-between gap-4 border-t border-gray-200 pt-4 sm:flex-row\"\n >\n <!-- Add Filter Button -->\n <div *ngIf=\"dropdownItems.length > 0\">\n <button\n (click)=\"addFilter()\"\n class=\"transform rounded-full border-2 border-blue-300 bg-blue-50 px-6 py-2 text-sm font-medium text-blue-600 transition-all duration-300 hover:-translate-y-0.5 hover:border-blue-400 hover:bg-blue-100 hover:shadow-md\"\n matTooltip=\"Adicionar novo filtro\"\n >\n <i class=\"fa fa-plus mr-2\"></i>\n Adicionar Filtro\n </button>\n </div>\n\n <!-- Sort Dropdown -->\n <div\n class=\"w-full sm:w-auto sm:min-w-[250px]\"\n *ngIf=\"sortableDropdownItems.length > 0\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Ordenar por</mat-label>\n <mat-select placeholder=\"Selecione...\" [formControl]=\"selectSort\">\n <mat-option\n *ngFor=\"let item of sortableDropdownItems\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i class=\"fa fa-sort-alpha-down text-cyan-600\"></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </div>\n\n <!-- SIMPLE SEARCH (for non-paginated tables) -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"data.pagination === false && hasFilterableColumn === true\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-blue-500\"></i>\n Buscar\n </mat-label>\n <input\n matInput\n (keyup.enter)=\"search($event)\"\n (keyup)=\"applyFilter(filterInput.value)\"\n placeholder=\"Digite para filtrar...\"\n #filterInput\n />\n <mat-icon matSuffix class=\"text-gray-500\">search</mat-icon>\n </mat-form-field>\n <button\n *ngIf=\"data.actionButton\"\n [ngClass]=\"\n (data.actionButton.colorClass || 'bg-blue-500') +\n ' float-right flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white hover:opacity-70'\n \"\n [routerLink]=\"data.actionButton.routerLink\"\n (click)=\"\n data.actionButton.method ? data.actionButton.method($event) : null\n \"\n >\n <i *ngIf=\"data.actionButton.icon\" [class]=\"data.actionButton.icon\"></i>\n {{ data.actionButton.label }}\n </button>\n </div>\n\n <!-- FILTERS PANEL (for non-paginated tables) -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"data.pagination === false && dropdownItems.length > 0\"\n >\n <!-- FILTERS CONTENT -->\n <div class=\"mb-4 space-y-3\" *ngIf=\"filtersForm.controls.length > 0\">\n <div\n [formGroup]=\"$any(filterGroup)\"\n *ngFor=\"let filterGroup of filtersForm.controls; let i = index\"\n class=\"flex flex-wrap items-center gap-3 rounded-lg border border-gray-200 p-2\"\n >\n <!-- FILTER TYPE SELECTOR -->\n <div class=\"min-w-[200px] flex-1\" *ngIf=\"dropdownItems.length > 0\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Tipo de filtro</mat-label>\n <mat-select\n placeholder=\"Selecione o tipo...\"\n formControlName=\"selectFilter\"\n (selectionChange)=\"onSelectFilterChange()\"\n >\n <mat-option\n *ngFor=\"let item of getAvailableFilterOptions()\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i\n [class]=\"item.icon || 'fa fa-filter'\"\n class=\"text-sm text-blue-500\"\n ></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- TEXT FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange === 'filter'\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-gray-400\"></i>\n <span>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Filtrar\"\n }}</span>\n </mat-label>\n <input\n (keyup.enter)=\"search($event)\"\n formControlName=\"typeFilter\"\n matInput\n placeholder=\"Digite para filtrar...\"\n #input\n />\n </mat-form-field>\n </div>\n\n <!-- DROPDOWN FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value &&\n $any(filterGroup)\n .get('selectFilter')\n ?.value.hasOwnProperty('items')\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Selecione\"\n }}</mat-label>\n <mat-select\n placeholder=\"Selecione...\"\n formControlName=\"selectItem\"\n multiple\n >\n <mat-option\n *ngFor=\"\n let item of $any(filterGroup).get('selectFilter')?.value\n .items\n \"\n [value]=\"item\"\n >\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- DATE FILTER -->\n <div\n class=\"min-w-[340px] flex-auto\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange ===\n 'filterByDate'\n \"\n >\n <div\n class=\"flex flex-col items-stretch gap-3 sm:flex-row sm:items-center\"\n >\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Inicial</span>\n </mat-label>\n <input\n type=\"text\"\n matInput\n (keydown.enter)=\"search($event)\"\n (blur)=\"onDateFilterChange()\"\n formControlName=\"initialDate\"\n [dropSpecialCharacters]=\"false\"\n mask=\"d0/M0/0000\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Final</span>\n </mat-label>\n <input\n type=\"text\"\n (keydown.enter)=\"search($event)\"\n (blur)=\"onDateFilterChange()\"\n matInput\n formControlName=\"finalDate\"\n [dropSpecialCharacters]=\"false\"\n mask=\"d0/M0/0000\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <!-- REMOVE FILTER BUTTON -->\n <div *ngIf=\"filtersForm.length > 1\" class=\"ml-auto flex-shrink-0\">\n <button\n (click)=\"removeFilter(i)\"\n class=\"flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-300 hover:bg-red-100\"\n matTooltip=\"Remover filtro\"\n >\n <i class=\"fa fa-trash text-red-500 hover:text-red-600\"></i>\n </button>\n </div>\n </div>\n </div>\n\n <!-- PANEL FOOTER: Add Filter & Actions -->\n <div\n class=\"-mb-2 flex flex-col items-center justify-between gap-4 border-t border-gray-200 pt-4 sm:flex-row\"\n >\n <!-- Add Filter Button -->\n <div *ngIf=\"dropdownItems.length > 0\">\n <button\n (click)=\"addFilter()\"\n class=\"transform rounded-full border-2 border-blue-300 bg-blue-50 px-6 py-2 text-sm font-medium text-blue-600 transition-all duration-300 hover:-translate-y-0.5 hover:border-blue-400 hover:bg-blue-100 hover:shadow-md\"\n matTooltip=\"Adicionar novo filtro\"\n >\n <i class=\"fa fa-plus mr-2\"></i>\n Adicionar Filtro\n </button>\n </div>\n\n <!-- Action Buttons -->\n <div class=\"flex flex-wrap gap-3\">\n <button\n (click)=\"search()\"\n type=\"button\"\n class=\"flex items-center gap-2 rounded-lg bg-green-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700\"\n matTooltip=\"Aplicar filtros\"\n >\n <i class=\"fa fa-search\"></i>\n Aplicar\n </button>\n\n <button\n (click)=\"resetFilter()\"\n class=\"flex items-center gap-2 rounded-lg bg-red-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600\"\n matTooltip=\"Limpar filtros\"\n >\n <i class=\"fas fa-redo-alt\"></i>\n Resetar\n </button>\n\n <button\n *ngIf=\"data.download !== false && downloadTable\"\n class=\"flex items-center gap-2 rounded-lg bg-orange-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600\"\n matTooltipPosition=\"above\"\n matTooltip=\"Exportar Tabela\"\n [disabled]=\"\n this.dataSource && this.dataSource.filteredData.length <= 0\n \"\n (click)=\"handleDownload()\"\n >\n <i class=\"fa fa-download\"></i>\n Exportar\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"flex flex-col\">\n <div\n class=\"mx-auto flex flex-col\"\n *ngIf=\"data.tabs && data.tabs.tabsData && data.tabs.tabsData.length > 0\"\n >\n <!-- Calcular quantos grupos de 6 tabs existem -->\n <ng-container\n *ngFor=\"\n let groupIndex of getTabGroups(data.tabs.tabsData);\n let i = index\n \"\n >\n <div class=\"mx-auto flex flex-row\">\n <ng-container\n *ngFor=\"\n let tab of getTabGroup(data.tabs.tabsData, groupIndex);\n let j = index\n \"\n >\n <button\n class=\"border-2 border-gray-300 bg-gray-200 px-4 py-2 font-medium transition hover:brightness-95\"\n [ngClass]=\"\n isTabSelected(getRealTabIndex(i, j))\n ? 'border-b-0 brightness-110'\n : ''\n \"\n (click)=\"onTableSelected(i, j)\"\n >\n {{ tab.label }}\n <span\n *ngIf=\"tab.counter !== undefined\"\n class=\"ml-2 text-xs font-bold\"\n [ngClass]=\"tab.counterClass\"\n >\n {{ tab.counter }}\n </span>\n </button>\n </ng-container>\n </div>\n </ng-container>\n </div>\n <div class=\"mat-elevation-z8 w-full overflow-x-auto rounded-xl\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n #sort=\"matSort\"\n matSortActive=\"createdAt\"\n matSortDirection=\"desc\"\n >\n <ng-container\n *ngFor=\"let col of data.displayedColumns\"\n matColumnDef=\"{{ col.property }}\"\n >\n <ng-container *matHeaderCellDef>\n <!-- IF THE COLUMN IS NOT SORTABLE, THEN DON'T SHOW THE SORT BUTTONS -->\n <th\n *ngIf=\"!col.isSortable || data.pagination === true\"\n mat-header-cell\n [ngClass]=\"\n (data.color?.bg ? ' ' + $any(data.color).bg : '') +\n (data.color?.text ? ' ' + $any(data.color).text : '')\n \"\n >\n {{ col.title }}\n </th>\n <!-- IF THE COLUMN IS SORTABLE, THEN SHOW THE SORT BUTTONS -->\n <th\n *ngIf=\"col.isSortable && data.pagination === false\"\n mat-header-cell\n mat-sort-header\n [ngClass]=\"\n (data.color?.bg ? ' ' + $any(data.color).bg : '') +\n (data.color?.text ? ' ' + $any(data.color).text : '')\n \"\n >\n {{ col.title }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"col.method ? col.method(row) : null\"\n (mouseenter)=\"onCellMouseEnter($event, row, col)\"\n (mouseleave)=\"onCellMouseLeave()\"\n (mousemove)=\"onCellMouseMove($event)\"\n >\n <!-- CHECK IF THE COLUMN MUST BE DISPLAYED -->\n <span *ngIf=\"!col.image && !col.iconClass && !col.method\">\n <ng-container>\n <span\n *ngIf=\"\n col.charLimit &&\n row[col.property] &&\n row[col.property].length > col.charLimit;\n else withinLimit\n \"\n >\n <a\n *ngIf=\"col.hasLink === true\"\n [href]=\"row[col.property]\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row) }}\n </a>\n <a\n *ngIf=\"col.hasLink && isString(col.hasLink)\"\n [href]=\"col.hasLink\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row) }}\n </a>\n <span\n *ngIf=\"col.hasLink !== true && !isString(col.hasLink)\"\n >\n {{ getDisplayValue(col, row) }}\n </span>\n </span>\n </ng-container>\n <ng-template #withinLimit>\n <a\n *ngIf=\"col.hasLink === true\"\n [href]=\"row[col.property]\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row, true) }}\n </a>\n <a\n *ngIf=\"col.hasLink && isString(col.hasLink)\"\n [href]=\"col.hasLink\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row, true) }}\n </a>\n <span\n *ngIf=\"col.hasLink !== true && !isString(col.hasLink)\"\n >\n {{ getDisplayValue(col, row, true) }}\n </span>\n </ng-template>\n </span>\n <!------------------- IMAGE ------------------>\n <img\n *ngIf=\"\n col.image && col.image.path && !col.iconClass && !col.method\n \"\n [src]=\"col.image.path + '/' + row[col.property]\"\n [ngClass]=\"col.image.class\"\n alt=\"Imagem\"\n />\n <img\n *ngIf=\"\n col.image && col.image.url && !col.iconClass && !col.method\n \"\n [src]=\"row[col.property]\"\n [ngClass]=\"col.image.class\"\n alt=\"Imagem\"\n />\n <ng-container *ngIf=\"col.iconClass\">\n <button\n *ngFor=\"let iconClass of col.iconClass\"\n (click)=\"\n iconClass.buttonMethod\n ? iconClass.buttonMethod(row, $event)\n : $event.stopPropagation()\n \"\n >\n <span\n [ngClass]=\"iconClass.class\"\n *ngIf=\"\n iconClass.condition === undefined ||\n (iconClass.condition !== undefined &&\n $any(iconClass.condition)(row))\n \"\n >{{ iconClass.text }}</span\n >\n </button>\n </ng-container>\n </td>\n </ng-container>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"columnProperties\"></tr>\n <tr\n [ngClass]=\"{\n 'example-element-row': data.isNotClickable === true,\n 'example-element-row cursor-pointer': !data.isNotClickable\n }\"\n mat-row\n *matRowDef=\"let row; columns: columnProperties\"\n (click)=\"goToDetails(row)\"\n ></tr>\n\n <!-- ROW SHOWN WHEN THERE IS NO MATCHING DATA. -->\n <tr class=\"mat-row\" *matNoDataRow>\n <td *ngIf=\"!isLoading\" class=\"mat-cell p-4\" colspan=\"4\">\n Nenhum resultado encontrado para a busca\n </td>\n </tr>\n </table>\n\n <div class=\"flex justify-center\" *ngIf=\"isLoading\">\n <mat-spinner></mat-spinner>\n </div>\n\n <div class=\"paginator-container\">\n <mat-paginator\n #paginator\n [pageSizeOptions]=\"[25, 50, 100]\"\n [pageSize]=\"pageSize\"\n [length]=\"totalItems\"\n showFirstLastButtons\n aria-label=\"Select page of periodic elements\"\n (page)=\"onPageChange($event)\"\n [ngClass]=\"{\n 'hide-length':\n ['filter', 'filterByDate', 'equals'].includes(\n this.currentArrange\n ) || this.data.filterFn,\n 'hide-next-button': !hasNextPage && data.pagination === true,\n 'hide-last-button':\n (!hasNextPage && data.pagination === true) || this.data.filterFn\n }\"\n >\n </mat-paginator>\n <div\n *ngIf=\"\n !isLoading &&\n dataSource?.data &&\n dataSource.data.length > 0 &&\n data?.filterFn\n \"\n class=\"page-number-display\"\n >\n {{ currentPageNumber }}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- TOOLTIP PERSONALIZADO -->\n <div\n *ngIf=\"showTooltip\"\n class=\"fixed z-50 max-w-md break-words rounded-lg bg-gray-800 px-3 py-2 text-sm text-white shadow-lg\"\n [style.left.px]=\"tooltipPosition.x\"\n [style.top.px]=\"tooltipPosition.y\"\n [style.pointer-events]=\"'none'\"\n >\n {{ tooltipContent }}\n </div>\n</div>\n", styles: ["::ng-deep .hide-length .mat-mdc-paginator-range-label{display:none}::ng-deep .hide-next-button .mat-mdc-tooltip-trigger.mat-mdc-paginator-navigation-next.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base{visibility:hidden}::ng-deep .hide-next-button .mat-mdc-tooltip-trigger.mat-mdc-paginator-navigation-last.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base.ng-star-inserted{visibility:hidden}::ng-deep .mat-mdc-text-field-wrapper.mdc-text-field.ng-tns-c162-1.mdc-text-field--filled{width:25dvw}::ng-deep .custom-filter .mat-mdc-text-field-wrapper{width:20dvw;max-width:300px}\n"] }]
2216
- }], ctorParameters: function () { return [{ type: i1$1.Router }, { type: TableService }, { type: i1.AngularFirestore }]; }, propDecorators: { data: [{
2268
+ args: [{ selector: 'lib-table', template: "<div *ngIf=\"data\" class=\"card-body\">\n <div class=\"flex flex-col justify-between gap-6\">\n <!-- UNIFIED CONTROL PANEL: FILTERS, SORT & ACTIONS -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"\n data.pagination === true &&\n (dropdownItems.length > 0 ||\n sortableDropdownItems.length > 0 ||\n data.actionButton)\n \"\n >\n <!-- PANEL HEADER: Title, Custom Action, and Global Actions -->\n <div\n class=\"mb-4 flex flex-col items-start justify-between gap-4 border-b-2 border-gray-200 pb-4 md:flex-row md:items-center\"\n >\n <!-- Left Side: Title & Main Action Button -->\n <div class=\"flex flex-wrap items-center gap-4\">\n <div class=\"flex items-center gap-2\">\n <i class=\"fa fa-filter text-xl text-blue-500\"></i>\n <span class=\"text-lg font-semibold text-gray-700\"\n >Filtros e A\u00E7\u00F5es</span\n >\n </div>\n <button\n *ngIf=\"data.actionButton && data.actionButton.condition\"\n [ngClass]=\"\n (data.actionButton.colorClass || 'bg-blue-500') +\n ' flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white hover:opacity-70'\n \"\n [routerLink]=\"data.actionButton.routerLink\"\n (click)=\"\n data.actionButton.method ? data.actionButton.method($event) : null\n \"\n >\n <i\n *ngIf=\"data.actionButton.icon\"\n [class]=\"data.actionButton.icon\"\n ></i>\n {{ data.actionButton.label }}\n </button>\n </div>\n\n <!-- Right Side: Search, Reset, Export -->\n <div\n class=\"flex flex-wrap gap-3\"\n *ngIf=\"\n this.hasFilterableColumn === true || this.hasSortableColumn === true\n \"\n >\n <button\n (click)=\"search()\"\n type=\"button\"\n class=\"flex items-center gap-2 rounded-lg bg-green-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700\"\n matTooltip=\"Aplicar filtros\"\n >\n <i class=\"fa fa-search\"></i>\n Pesquisar\n </button>\n\n <button\n (click)=\"resetFilter()\"\n class=\"flex items-center gap-2 rounded-lg bg-red-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600\"\n matTooltip=\"Limpar filtros\"\n >\n <i class=\"fas fa-redo-alt\"></i>\n Resetar\n </button>\n\n <button\n *ngIf=\"data.download !== false && downloadTable\"\n class=\"flex items-center gap-2 rounded-lg bg-orange-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600\"\n matTooltipPosition=\"above\"\n matTooltip=\"Exportar Tabela\"\n [disabled]=\"\n this.dataSource && this.dataSource.filteredData.length <= 0\n \"\n (click)=\"\n $any(arrange) && downloadTable !== undefined\n ? downloadTable($any(arrange), data.conditions || [])\n : null\n \"\n >\n <i class=\"fa fa-download\"></i>\n Exportar\n </button>\n </div>\n </div>\n\n <!-- FILTERS CONTENT (WITH REFINEMENTS) -->\n <div class=\"mb-4 space-y-3\" *ngIf=\"filtersForm.controls.length > 0\">\n <div\n [formGroup]=\"$any(filterGroup)\"\n *ngFor=\"let filterGroup of filtersForm.controls; let i = index\"\n class=\"flex flex-wrap items-center gap-3 rounded-lg border border-gray-200 p-2\"\n >\n <!-- FILTER TYPE SELECTOR -->\n <div class=\"min-w-[200px] flex-1\" *ngIf=\"dropdownItems.length > 0\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Tipo de filtro</mat-label>\n <mat-select\n placeholder=\"Selecione o tipo...\"\n formControlName=\"selectFilter\"\n (selectionChange)=\"onSelectFilterChange()\"\n >\n <mat-option\n *ngFor=\"let item of getAvailableFilterOptions()\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i\n [class]=\"item.icon || 'fa fa-filter'\"\n class=\"text-sm text-blue-500\"\n ></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- TEXT FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange === 'filter'\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-gray-400\"></i>\n <span>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Filtrar\"\n }}</span>\n </mat-label>\n <input\n (keyup.enter)=\"search($event)\"\n formControlName=\"typeFilter\"\n matInput\n placeholder=\"Digite para filtrar...\"\n #input\n />\n </mat-form-field>\n </div>\n\n <!-- DROPDOWN FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value &&\n $any(filterGroup)\n .get('selectFilter')\n ?.value.hasOwnProperty('items')\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Selecione\"\n }}</mat-label>\n <mat-select\n placeholder=\"Selecione...\"\n formControlName=\"selectItem\"\n multiple\n >\n <mat-option\n *ngFor=\"\n let item of $any(filterGroup).get('selectFilter')?.value\n .items\n \"\n [value]=\"item\"\n >\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- DATE FILTER -->\n <div\n class=\"min-w-[340px] flex-auto\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange ===\n 'filterByDate'\n \"\n >\n <div\n class=\"flex flex-col items-stretch gap-3 sm:flex-row sm:items-center\"\n >\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Inicial</span>\n </mat-label>\n <input\n type=\"text\"\n matInput\n (keydown.enter)=\"search($event)\"\n (input)=\"onDateInput($event, 'initialDate', i)\"\n formControlName=\"initialDate\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Final</span>\n </mat-label>\n <input\n type=\"text\"\n (keydown.enter)=\"search($event)\"\n (input)=\"onDateInput($event, 'finalDate', i)\"\n matInput\n formControlName=\"finalDate\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <!-- REMOVE FILTER BUTTON -->\n <div *ngIf=\"filtersForm.length > 1\" class=\"ml-auto flex-shrink-0\">\n <button\n (click)=\"removeFilter(i)\"\n class=\"flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-300 hover:bg-red-100\"\n matTooltip=\"Remover filtro\"\n >\n <i class=\"fa fa-trash text-red-500 hover:text-red-600\"></i>\n </button>\n </div>\n </div>\n </div>\n\n <!-- PANEL FOOTER: Add Filter & Sort -->\n <div\n class=\"-mb-2 flex flex-col items-center justify-between gap-4 border-t border-gray-200 pt-4 sm:flex-row\"\n >\n <!-- Add Filter Button -->\n <div *ngIf=\"dropdownItems.length > 0\">\n <button\n (click)=\"addFilter()\"\n class=\"transform rounded-full border-2 border-blue-300 bg-blue-50 px-6 py-2 text-sm font-medium text-blue-600 transition-all duration-300 hover:-translate-y-0.5 hover:border-blue-400 hover:bg-blue-100 hover:shadow-md\"\n matTooltip=\"Adicionar novo filtro\"\n >\n <i class=\"fa fa-plus mr-2\"></i>\n Adicionar Filtro\n </button>\n </div>\n\n <!-- Sort Dropdown -->\n <div\n class=\"w-full sm:w-auto sm:min-w-[250px]\"\n *ngIf=\"sortableDropdownItems.length > 0\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Ordenar por</mat-label>\n <mat-select placeholder=\"Selecione...\" [formControl]=\"selectSort\">\n <mat-option\n *ngFor=\"let item of sortableDropdownItems\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i class=\"fa fa-sort-alpha-down text-cyan-600\"></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </div>\n\n <!-- SIMPLE SEARCH (for non-paginated tables) -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"data.pagination === false && hasFilterableColumn === true\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-blue-500\"></i>\n Buscar\n </mat-label>\n <input\n matInput\n (keyup.enter)=\"search($event)\"\n (keyup)=\"applyFilter(filterInput.value)\"\n placeholder=\"Digite para filtrar...\"\n #filterInput\n />\n <mat-icon matSuffix class=\"text-gray-500\">search</mat-icon>\n </mat-form-field>\n <button\n *ngIf=\"data.actionButton\"\n [ngClass]=\"\n (data.actionButton.colorClass || 'bg-blue-500') +\n ' float-right flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white hover:opacity-70'\n \"\n [routerLink]=\"data.actionButton.routerLink\"\n (click)=\"\n data.actionButton.method ? data.actionButton.method($event) : null\n \"\n >\n <i *ngIf=\"data.actionButton.icon\" [class]=\"data.actionButton.icon\"></i>\n {{ data.actionButton.label }}\n </button>\n </div>\n\n <!-- FILTERS PANEL (for non-paginated tables) -->\n <div\n class=\"rounded-xl border border-gray-200 bg-white p-4 shadow-lg\"\n *ngIf=\"data.pagination === false && dropdownItems.length > 0\"\n >\n <!-- FILTERS CONTENT -->\n <div class=\"mb-4 space-y-3\" *ngIf=\"filtersForm.controls.length > 0\">\n <div\n [formGroup]=\"$any(filterGroup)\"\n *ngFor=\"let filterGroup of filtersForm.controls; let i = index\"\n class=\"flex flex-wrap items-center gap-3 rounded-lg border border-gray-200 p-2\"\n >\n <!-- FILTER TYPE SELECTOR -->\n <div class=\"min-w-[200px] flex-1\" *ngIf=\"dropdownItems.length > 0\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Tipo de filtro</mat-label>\n <mat-select\n placeholder=\"Selecione o tipo...\"\n formControlName=\"selectFilter\"\n (selectionChange)=\"onSelectFilterChange()\"\n >\n <mat-option\n *ngFor=\"let item of getAvailableFilterOptions()\"\n [value]=\"item\"\n >\n <div class=\"flex items-center gap-2\">\n <i\n [class]=\"item.icon || 'fa fa-filter'\"\n class=\"text-sm text-blue-500\"\n ></i>\n <span>{{ item.title }}</span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- TEXT FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange === 'filter'\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-search text-gray-400\"></i>\n <span>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Filtrar\"\n }}</span>\n </mat-label>\n <input\n (keyup.enter)=\"search($event)\"\n formControlName=\"typeFilter\"\n matInput\n placeholder=\"Digite para filtrar...\"\n #input\n />\n </mat-form-field>\n </div>\n\n <!-- DROPDOWN FILTER -->\n <div\n class=\"min-w-[200px] flex-1\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value &&\n $any(filterGroup)\n .get('selectFilter')\n ?.value.hasOwnProperty('items')\n \"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{\n $any(filterGroup).get(\"selectFilter\")?.value?.title ||\n \"Selecione\"\n }}</mat-label>\n <mat-select\n placeholder=\"Selecione...\"\n formControlName=\"selectItem\"\n multiple\n >\n <mat-option\n *ngFor=\"\n let item of $any(filterGroup).get('selectFilter')?.value\n .items\n \"\n [value]=\"item\"\n >\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- DATE FILTER -->\n <div\n class=\"min-w-[340px] flex-auto\"\n *ngIf=\"\n $any(filterGroup).get('selectFilter')?.value?.arrange ===\n 'filterByDate'\n \"\n >\n <div\n class=\"flex flex-col items-stretch gap-3 sm:flex-row sm:items-center\"\n >\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Inicial</span>\n </mat-label>\n <input\n type=\"text\"\n matInput\n (keydown.enter)=\"search($event)\"\n (input)=\"onDateInput($event, 'initialDate', i)\"\n (blur)=\"onDateFilterChange()\"\n formControlName=\"initialDate\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"flex-1\">\n <mat-label class=\"flex items-center gap-2\">\n <i class=\"fa fa-calendar text-gray-400\"></i>\n <span>Data Final</span>\n </mat-label>\n <input\n type=\"text\"\n (keydown.enter)=\"search($event)\"\n (input)=\"onDateInput($event, 'finalDate', i)\"\n (blur)=\"onDateFilterChange()\"\n matInput\n formControlName=\"finalDate\"\n placeholder=\"DD/MM/AAAA\"\n maxlength=\"10\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <!-- REMOVE FILTER BUTTON -->\n <div *ngIf=\"filtersForm.length > 1\" class=\"ml-auto flex-shrink-0\">\n <button\n (click)=\"removeFilter(i)\"\n class=\"flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-300 hover:bg-red-100\"\n matTooltip=\"Remover filtro\"\n >\n <i class=\"fa fa-trash text-red-500 hover:text-red-600\"></i>\n </button>\n </div>\n </div>\n </div>\n\n <!-- PANEL FOOTER: Add Filter & Actions -->\n <div\n class=\"-mb-2 flex flex-col items-center justify-between gap-4 border-t border-gray-200 pt-4 sm:flex-row\"\n >\n <!-- Add Filter Button -->\n <div *ngIf=\"dropdownItems.length > 0\">\n <button\n (click)=\"addFilter()\"\n class=\"transform rounded-full border-2 border-blue-300 bg-blue-50 px-6 py-2 text-sm font-medium text-blue-600 transition-all duration-300 hover:-translate-y-0.5 hover:border-blue-400 hover:bg-blue-100 hover:shadow-md\"\n matTooltip=\"Adicionar novo filtro\"\n >\n <i class=\"fa fa-plus mr-2\"></i>\n Adicionar Filtro\n </button>\n </div>\n\n <!-- Action Buttons -->\n <div class=\"flex flex-wrap gap-3\">\n <button\n (click)=\"search()\"\n type=\"button\"\n class=\"flex items-center gap-2 rounded-lg bg-green-600 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700\"\n matTooltip=\"Aplicar filtros\"\n >\n <i class=\"fa fa-search\"></i>\n Aplicar\n </button>\n\n <button\n (click)=\"resetFilter()\"\n class=\"flex items-center gap-2 rounded-lg bg-red-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600\"\n matTooltip=\"Limpar filtros\"\n >\n <i class=\"fas fa-redo-alt\"></i>\n Resetar\n </button>\n\n <button\n *ngIf=\"data.download !== false && downloadTable\"\n class=\"flex items-center gap-2 rounded-lg bg-orange-500 px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600\"\n matTooltipPosition=\"above\"\n matTooltip=\"Exportar Tabela\"\n [disabled]=\"\n this.dataSource && this.dataSource.filteredData.length <= 0\n \"\n (click)=\"handleDownload()\"\n >\n <i class=\"fa fa-download\"></i>\n Exportar\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"flex flex-col\">\n <div\n class=\"mx-auto flex flex-col\"\n *ngIf=\"data.tabs && data.tabs.tabsData && data.tabs.tabsData.length > 0\"\n >\n <!-- Calcular quantos grupos de 6 tabs existem -->\n <ng-container\n *ngFor=\"\n let groupIndex of getTabGroups(data.tabs.tabsData);\n let i = index\n \"\n >\n <div class=\"mx-auto flex flex-row\">\n <ng-container\n *ngFor=\"\n let tab of getTabGroup(data.tabs.tabsData, groupIndex);\n let j = index\n \"\n >\n <button\n class=\"border-2 border-gray-300 bg-gray-200 px-4 py-2 font-medium transition hover:brightness-95\"\n [ngClass]=\"\n isTabSelected(getRealTabIndex(i, j))\n ? 'border-b-0 brightness-110'\n : ''\n \"\n (click)=\"onTableSelected(i, j)\"\n >\n {{ tab.label }}\n <span\n *ngIf=\"tab.counter !== undefined\"\n class=\"ml-2 text-xs font-bold\"\n [ngClass]=\"tab.counterClass\"\n >\n {{ tab.counter }}\n </span>\n </button>\n </ng-container>\n </div>\n </ng-container>\n </div>\n <div\n class=\"mat-elevation-z8 w-full overflow-x-auto rounded-xl table-container\"\n >\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n #sort=\"matSort\"\n matSortActive=\"createdAt\"\n matSortDirection=\"desc\"\n >\n <ng-container\n *ngFor=\"let col of data.displayedColumns\"\n matColumnDef=\"{{ col.property }}\"\n >\n <ng-container *matHeaderCellDef>\n <!-- IF THE COLUMN IS NOT SORTABLE, THEN DON'T SHOW THE SORT BUTTONS -->\n <th\n *ngIf=\"!col.isSortable || data.pagination === true\"\n mat-header-cell\n >\n {{ col.title }}\n </th>\n <!-- IF THE COLUMN IS SORTABLE, THEN SHOW THE SORT BUTTONS -->\n <th\n *ngIf=\"col.isSortable && data.pagination === false\"\n mat-header-cell\n mat-sort-header\n >\n {{ col.title }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"col.method ? col.method(row) : null\"\n (mouseenter)=\"onCellMouseEnter($event, row, col)\"\n (mouseleave)=\"onCellMouseLeave()\"\n (mousemove)=\"onCellMouseMove($event)\"\n >\n <!-- CHECK IF THE COLUMN MUST BE DISPLAYED -->\n <span *ngIf=\"!col.image && !col.iconClass && !col.method\">\n <ng-container>\n <span\n *ngIf=\"\n col.charLimit &&\n row[col.property] &&\n row[col.property].length > col.charLimit;\n else withinLimit\n \"\n >\n <a\n *ngIf=\"col.hasLink === true\"\n [href]=\"row[col.property]\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row) }}\n </a>\n <a\n *ngIf=\"col.hasLink && isString(col.hasLink)\"\n [href]=\"col.hasLink\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row) }}\n </a>\n <span\n *ngIf=\"col.hasLink !== true && !isString(col.hasLink)\"\n >\n {{ getDisplayValue(col, row) }}\n </span>\n </span>\n </ng-container>\n <ng-template #withinLimit>\n <a\n *ngIf=\"col.hasLink === true\"\n [href]=\"row[col.property]\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row, true) }}\n </a>\n <a\n *ngIf=\"col.hasLink && isString(col.hasLink)\"\n [href]=\"col.hasLink\"\n target=\"_blank\"\n >\n {{ getDisplayValue(col, row, true) }}\n </a>\n <span\n *ngIf=\"col.hasLink !== true && !isString(col.hasLink)\"\n >\n {{ getDisplayValue(col, row, true) }}\n </span>\n </ng-template>\n </span>\n <!------------------- IMAGE ------------------>\n <img\n *ngIf=\"\n col.image && col.image.path && !col.iconClass && !col.method\n \"\n [src]=\"col.image.path + '/' + row[col.property]\"\n [ngClass]=\"col.image.class\"\n alt=\"Imagem\"\n />\n <img\n *ngIf=\"\n col.image && col.image.url && !col.iconClass && !col.method\n \"\n [src]=\"row[col.property]\"\n [ngClass]=\"col.image.class\"\n alt=\"Imagem\"\n />\n <ng-container *ngIf=\"col.iconClass\">\n <button\n *ngFor=\"let iconClass of col.iconClass\"\n (click)=\"\n iconClass.buttonMethod\n ? iconClass.buttonMethod(row, $event)\n : $event.stopPropagation()\n \"\n >\n <span\n [ngClass]=\"iconClass.class\"\n *ngIf=\"\n iconClass.condition === undefined ||\n (iconClass.condition !== undefined &&\n $any(iconClass.condition)(row))\n \"\n >{{ iconClass.text }}</span\n >\n </button>\n </ng-container>\n </td>\n </ng-container>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"columnProperties\"></tr>\n <tr\n [ngClass]=\"{\n 'example-element-row': data.isNotClickable === true,\n 'example-element-row cursor-pointer': !data.isNotClickable\n }\"\n mat-row\n *matRowDef=\"let row; columns: columnProperties\"\n (click)=\"goToDetails(row)\"\n ></tr>\n\n <!-- ROW SHOWN WHEN THERE IS NO MATCHING DATA. -->\n <tr class=\"mat-row\" *matNoDataRow>\n <td *ngIf=\"!isLoading\" class=\"mat-cell p-4\" colspan=\"4\">\n Nenhum resultado encontrado para a busca\n </td>\n </tr>\n </table>\n\n <div class=\"flex justify-center\" *ngIf=\"isLoading\">\n <mat-spinner></mat-spinner>\n </div>\n\n <div class=\"paginator-container\">\n <mat-paginator\n #paginator\n [pageSizeOptions]=\"[25, 50, 100]\"\n [pageSize]=\"pageSize\"\n [length]=\"totalItems\"\n showFirstLastButtons\n aria-label=\"Select page of periodic elements\"\n (page)=\"onPageChange($event)\"\n [ngClass]=\"{\n 'hide-length':\n ['filter', 'filterByDate', 'equals'].includes(\n this.currentArrange\n ) || this.data.filterFn,\n 'hide-next-button': !hasNextPage && data.pagination === true,\n 'hide-last-button':\n (!hasNextPage && data.pagination === true) || this.data.filterFn\n }\"\n >\n </mat-paginator>\n <div\n *ngIf=\"\n !isLoading &&\n dataSource?.data &&\n dataSource.data.length > 0 &&\n data?.filterFn\n \"\n class=\"page-number-display\"\n >\n {{ currentPageNumber }}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- TOOLTIP PERSONALIZADO -->\n <div\n *ngIf=\"showTooltip\"\n class=\"fixed z-50 max-w-md break-words rounded-lg bg-gray-800 px-3 py-2 text-sm text-white shadow-lg\"\n [style.left.px]=\"tooltipPosition.x\"\n [style.top.px]=\"tooltipPosition.y\"\n [style.pointer-events]=\"'none'\"\n >\n {{ tooltipContent }}\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--table-color: #1976d2;--table-text-color: white;--table-header-bg: #1976d2;--table-header-text: white;--table-scrollbar-bg: #f5f5f5;--table-scrollbar-thumb: var(--table-color)}.table-container::-webkit-scrollbar{width:10px;height:10px;background-color:var(--table-scrollbar-bg, #f5f5f5)}.table-container::-webkit-scrollbar-thumb{background-color:var(--table-scrollbar-thumb, var(--table-color, #1976d2));border-radius:5px}.table-container::-webkit-scrollbar-thumb:hover{background-color:var(--table-scrollbar-thumb, var(--table-color, #1976d2));opacity:.8}.overflow-x-auto::-webkit-scrollbar,.overflow-y-auto::-webkit-scrollbar,.overflow-auto::-webkit-scrollbar{width:10px;height:10px;background-color:var(--table-scrollbar-bg, #f5f5f5)}.overflow-x-auto::-webkit-scrollbar-thumb,.overflow-y-auto::-webkit-scrollbar-thumb,.overflow-auto::-webkit-scrollbar-thumb{background-color:var(--table-scrollbar-thumb, var(--table-color, #1976d2));border-radius:5px}.overflow-x-auto::-webkit-scrollbar-thumb:hover,.overflow-y-auto::-webkit-scrollbar-thumb:hover,.overflow-auto::-webkit-scrollbar-thumb:hover{background-color:var(--table-scrollbar-thumb, var(--table-color, #1976d2));opacity:.8}:host ::ng-deep .mat-table .mat-header-cell,:host ::ng-deep .mat-header-cell,:host ::ng-deep th.mat-header-cell,:host ::ng-deep .mat-mdc-table .mat-mdc-header-cell,:host ::ng-deep .mat-mdc-header-cell,:host ::ng-deep th.mat-mdc-header-cell{background-color:var(--table-header-bg, #1976d2)!important;color:var(--table-header-text, white)!important;font-weight:600}::ng-deep .fa-filter,::ng-deep i.fa-filter{color:var(--table-color, #1976d2)!important}::ng-deep .rounded-xl.border{border-color:var(--table-color, #1976d2)!important}::ng-deep .border-gray-200.rounded-xl{border-color:var(--table-color, #1976d2)!important}::ng-deep .mdc-notched-outline__notch{border-right:none!important}.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix{padding-bottom:0}::ng-deep .mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix{padding-bottom:0}.mat-mdc-text-field-wrapper.mdc-text-field--outlined.mdc-text-field--disabled{background:rgba(0,0,0,.15);background-size:1px 100%!important;background-repeat:repeat-x!important}.mdc-text-field--disabled .mdc-text-field__input{color:#525252}button:not([matTooltip=\"Adicionar novo filtro\"]):not([matTooltip=\"Aplicar filtros\"]):not([matTooltip=\"Limpar filtros\"]):not([matTooltip=\"Exportar Tabela\"]):not(:has(i.fa-plus)):not(:has(i.fa-search)):not(:has(i.fas.fa-redo-alt)):not(:has(i.fa-download)){transition:all .2s ease;cursor:pointer}button:not([matTooltip=\"Adicionar novo filtro\"]):not([matTooltip=\"Aplicar filtros\"]):not([matTooltip=\"Limpar filtros\"]):not([matTooltip=\"Exportar Tabela\"]):not(:has(i.fa-plus)):not(:has(i.fa-search)):not(:has(i.fas.fa-redo-alt)):not(:has(i.fa-download)):hover:not(:disabled){opacity:.9;transform:scale(1.02)}button:not([matTooltip=\"Adicionar novo filtro\"]):not([matTooltip=\"Aplicar filtros\"]):not([matTooltip=\"Limpar filtros\"]):not([matTooltip=\"Exportar Tabela\"]):not(:has(i.fa-plus)):not(:has(i.fa-search)):not(:has(i.fas.fa-redo-alt)):not(:has(i.fa-download)):disabled{opacity:.5;cursor:not-allowed;transform:none}::ng-deep .hide-length .mat-mdc-paginator-range-label{display:none}::ng-deep .hide-next-button .mat-mdc-tooltip-trigger.mat-mdc-paginator-navigation-next.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base{visibility:hidden}::ng-deep .hide-next-button .mat-mdc-tooltip-trigger.mat-mdc-paginator-navigation-last.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base.ng-star-inserted{visibility:hidden}::ng-deep .mat-mdc-text-field-wrapper.mdc-text-field.ng-tns-c162-1.mdc-text-field--filled{width:25dvw}::ng-deep .custom-filter .mat-mdc-text-field-wrapper{width:20dvw;max-width:300px}button[matTooltip=\"Adicionar novo filtro\"],button:has(i.fa-plus){display:inline-flex;align-items:center;gap:.5rem;border-radius:9999px;border:2px solid #93c5fd;background-color:#dbeafe;padding:.5rem 1.5rem;font-size:.875rem;font-weight:500;color:#2563eb;transition:all .3s;cursor:pointer;transform:translateY(0)}button[matTooltip=\"Adicionar novo filtro\"]:hover,button:has(i.fa-plus):hover{transform:translateY(-2px);border-color:#60a5fa;background-color:#bfdbfe;box-shadow:0 4px 6px #0000001a}button[matTooltip=\"Aplicar filtros\"],button:has(i.fa-search){display:inline-flex;align-items:center;gap:.5rem;border-radius:.5rem;background-color:#16a34a;padding:.5rem 1.25rem;font-size:.875rem;font-weight:500;color:#fff;transition:background-color .2s;cursor:pointer;border:none}button[matTooltip=\"Aplicar filtros\"]:hover,button:has(i.fa-search):hover{background-color:#15803d}button[matTooltip=\"Aplicar filtros\"]:disabled,button:has(i.fa-search):disabled{opacity:.5;cursor:not-allowed}button[matTooltip=\"Limpar filtros\"],button:has(i.fas.fa-redo-alt){display:inline-flex;align-items:center;gap:.5rem;border-radius:.5rem;background-color:#ef4444;padding:.5rem 1.25rem;font-size:.875rem;font-weight:500;color:#fff;transition:background-color .2s;cursor:pointer;border:none}button[matTooltip=\"Limpar filtros\"]:hover,button:has(i.fas.fa-redo-alt):hover{background-color:#dc2626}button[matTooltip=\"Exportar Tabela\"],button:has(i.fa-download){display:inline-flex;align-items:center;gap:.5rem;border-radius:.5rem;background-color:#f97316;padding:.5rem 1.25rem;font-size:.875rem;font-weight:500;color:#fff;transition:background-color .2s;cursor:pointer;border:none}button[matTooltip=\"Exportar Tabela\"]:hover:not(:disabled),button:has(i.fa-download):hover:not(:disabled){background-color:#ea580c}button[matTooltip=\"Exportar Tabela\"]:disabled,button:has(i.fa-download):disabled{opacity:.5;cursor:not-allowed}\n"] }]
2269
+ }], ctorParameters: function () { return [{ type: i1$1.Router }, { type: TableService }, { type: i1.AngularFirestore }, { type: i0.ElementRef }]; }, propDecorators: { data: [{
2217
2270
  type: Input
2218
2271
  }], downloadTable: [{
2219
2272
  type: Input
@@ -2241,10 +2294,8 @@ NgFirebaseTableKxpModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0"
2241
2294
  MatTooltipModule,
2242
2295
  MatProgressSpinnerModule,
2243
2296
  MatIconModule,
2244
- MatDialogModule,
2245
- NgxMaskDirective,
2246
- NgxMaskPipe], exports: [NgFirebaseTableKxpComponent, TableComponent] });
2247
- NgFirebaseTableKxpModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NgFirebaseTableKxpModule, providers: [provideNgxMask()], imports: [CommonModule,
2297
+ MatDialogModule], exports: [NgFirebaseTableKxpComponent, TableComponent] });
2298
+ NgFirebaseTableKxpModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NgFirebaseTableKxpModule, providers: [TableService], imports: [CommonModule,
2248
2299
  ReactiveFormsModule,
2249
2300
  FormsModule,
2250
2301
  RouterModule,
@@ -2277,11 +2328,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
2277
2328
  MatProgressSpinnerModule,
2278
2329
  MatIconModule,
2279
2330
  MatDialogModule,
2280
- NgxMaskDirective,
2281
- NgxMaskPipe,
2282
2331
  ],
2283
2332
  exports: [NgFirebaseTableKxpComponent, TableComponent],
2284
- providers: [provideNgxMask()],
2333
+ providers: [TableService],
2285
2334
  }]
2286
2335
  }] });
2287
2336