ng-firebase-table-kxp 1.0.0 → 1.0.2
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.
- package/README.md +500 -381
- package/esm2020/lib/components/table/table.component.mjs +776 -0
- package/esm2020/lib/firebase-table-kxp-lib.component.mjs +19 -0
- package/esm2020/lib/firebase-table-kxp-lib.module.mjs +74 -0
- package/esm2020/lib/firebase-table-kxp-lib.service.mjs +14 -0
- package/esm2020/lib/services/table.service.mjs +908 -0
- package/esm2020/lib/types/Table.mjs +2 -0
- package/esm2020/ng-firebase-table-kxp.mjs +5 -0
- package/esm2020/public-api.mjs +15 -0
- package/fesm2015/ng-firebase-table-kxp.mjs +1811 -0
- package/fesm2015/ng-firebase-table-kxp.mjs.map +1 -0
- package/fesm2020/ng-firebase-table-kxp.mjs +1784 -0
- package/fesm2020/ng-firebase-table-kxp.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/components/table/table.component.d.ts +111 -0
- package/lib/firebase-table-kxp-lib.component.d.ts +5 -0
- package/lib/firebase-table-kxp-lib.module.d.ts +21 -0
- package/lib/firebase-table-kxp-lib.service.d.ts +6 -0
- package/lib/services/table.service.d.ts +70 -0
- package/{src/lib/types/Table.ts → lib/types/Table.d.ts} +28 -31
- package/package.json +23 -3
- package/{src/public-api.ts → public-api.d.ts} +0 -13
- package/CHANGELOG.md +0 -88
- package/ng-package.json +0 -7
- package/src/lib/components/table/table.component.html +0 -555
- package/src/lib/components/table/table.component.scss +0 -22
- package/src/lib/components/table/table.component.spec.ts +0 -24
- package/src/lib/components/table/table.component.ts +0 -917
- package/src/lib/firebase-table-kxp-lib.component.spec.ts +0 -23
- package/src/lib/firebase-table-kxp-lib.component.ts +0 -15
- package/src/lib/firebase-table-kxp-lib.module.ts +0 -45
- package/src/lib/firebase-table-kxp-lib.service.spec.ts +0 -16
- package/src/lib/firebase-table-kxp-lib.service.ts +0 -9
- package/src/lib/services/table.service.spec.ts +0 -16
- package/src/lib/services/table.service.ts +0 -1235
- package/tsconfig.lib.json +0 -14
- package/tsconfig.lib.prod.json +0 -10
- package/tsconfig.spec.json +0 -14
|
@@ -1,917 +0,0 @@
|
|
|
1
|
-
import { Arrange, Condition, Tab, TabData, TableData } from '../../types/Table';
|
|
2
|
-
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
|
3
|
-
import {
|
|
4
|
-
AngularFirestore,
|
|
5
|
-
QueryDocumentSnapshot,
|
|
6
|
-
} from '@angular/fire/compat/firestore';
|
|
7
|
-
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
|
8
|
-
import { MatSort } from '@angular/material/sort';
|
|
9
|
-
import { MatTableDataSource } from '@angular/material/table';
|
|
10
|
-
import { Router } from '@angular/router';
|
|
11
|
-
import { TableService } from '../../services/table.service';
|
|
12
|
-
import { debounceTime, firstValueFrom, Subject } from 'rxjs';
|
|
13
|
-
import { OrderByDirection } from 'firebase/firestore';
|
|
14
|
-
import firebase from 'firebase/compat';
|
|
15
|
-
import WhereFilterOp = firebase.firestore.WhereFilterOp;
|
|
16
|
-
import firestore = firebase.firestore;
|
|
17
|
-
import { FormControl, FormArray, FormGroup } from '@angular/forms';
|
|
18
|
-
|
|
19
|
-
@Component({
|
|
20
|
-
selector: 'lib-table',
|
|
21
|
-
templateUrl: './table.component.html',
|
|
22
|
-
styleUrls: ['./table.component.scss'],
|
|
23
|
-
})
|
|
24
|
-
export class TableComponent implements OnInit {
|
|
25
|
-
// INPUTS
|
|
26
|
-
@Input() data!: TableData;
|
|
27
|
-
@Input() downloadTable?: (arrange: Arrange, conditions: Condition[]) => void;
|
|
28
|
-
arrange: Arrange | null = null;
|
|
29
|
-
// VARIABLES
|
|
30
|
-
dataSource!: MatTableDataSource<any>;
|
|
31
|
-
currentPageNumber = 1;
|
|
32
|
-
currentClientPageIndex = 0;
|
|
33
|
-
|
|
34
|
-
public items: any[] = [];
|
|
35
|
-
public isLoading = false;
|
|
36
|
-
private lastDoc: QueryDocumentSnapshot<any> | null = null;
|
|
37
|
-
private firstDoc: QueryDocumentSnapshot<any> | null = null;
|
|
38
|
-
private sortBy: { field: string; order: OrderByDirection } = {
|
|
39
|
-
field: 'createdAt',
|
|
40
|
-
order: 'desc',
|
|
41
|
-
};
|
|
42
|
-
public columnProperties: string[] = [];
|
|
43
|
-
|
|
44
|
-
filtersForm: FormArray;
|
|
45
|
-
|
|
46
|
-
selectSort: FormControl = new FormControl('');
|
|
47
|
-
currentArrange = '';
|
|
48
|
-
|
|
49
|
-
hasNextPage: boolean = false;
|
|
50
|
-
|
|
51
|
-
dropdownItems: any[] = [];
|
|
52
|
-
sortableDropdownItems: any[] = [];
|
|
53
|
-
pageSize = 25;
|
|
54
|
-
totalItems = 0;
|
|
55
|
-
pageEvent?: PageEvent;
|
|
56
|
-
filterValue: string | null = null;
|
|
57
|
-
|
|
58
|
-
hasFilterableColumn: boolean = false;
|
|
59
|
-
hasSortableColumn: boolean = false;
|
|
60
|
-
filterPredicate: ((data: any, filter: string) => boolean) | undefined;
|
|
61
|
-
private filterSubject = new Subject<string>();
|
|
62
|
-
private readonly debounceTimeMs = 500;
|
|
63
|
-
|
|
64
|
-
selectedTab: number = 0;
|
|
65
|
-
|
|
66
|
-
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
|
67
|
-
@ViewChild(MatSort) sort!: MatSort;
|
|
68
|
-
|
|
69
|
-
// Propriedades para controle do tooltip
|
|
70
|
-
hoveredCell: { row: any; col: any } | null = null;
|
|
71
|
-
showTooltip: boolean = false;
|
|
72
|
-
tooltipTimeout: any;
|
|
73
|
-
tooltipContent: string = '';
|
|
74
|
-
tooltipPosition: { x: number; y: number } = { x: 0, y: 0 };
|
|
75
|
-
|
|
76
|
-
// CONSTRUCTOR
|
|
77
|
-
constructor(
|
|
78
|
-
private router: Router,
|
|
79
|
-
private tableService: TableService,
|
|
80
|
-
private firestore: AngularFirestore
|
|
81
|
-
) {
|
|
82
|
-
this.filtersForm = new FormArray([this.createFilterGroup()]);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
createFilterGroup(): FormGroup {
|
|
86
|
-
return new FormGroup({
|
|
87
|
-
selectFilter: new FormControl(''),
|
|
88
|
-
typeFilter: new FormControl(''),
|
|
89
|
-
selectItem: new FormControl(''),
|
|
90
|
-
initialDate: new FormControl('', this.tableService.dateFormatValidator()),
|
|
91
|
-
finalDate: new FormControl('', this.tableService.dateFormatValidator()),
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
addFilter(filterData?: {
|
|
96
|
-
selectFilter: any;
|
|
97
|
-
typeFilter?: string;
|
|
98
|
-
selectItem?: any[];
|
|
99
|
-
initialDate?: string;
|
|
100
|
-
finalDate?: string;
|
|
101
|
-
}): void {
|
|
102
|
-
const newFilterGroup = this.createFilterGroup();
|
|
103
|
-
|
|
104
|
-
if (filterData) {
|
|
105
|
-
if (filterData.selectFilter) {
|
|
106
|
-
newFilterGroup.get('selectFilter')?.setValue(filterData.selectFilter);
|
|
107
|
-
}
|
|
108
|
-
if (filterData.typeFilter) {
|
|
109
|
-
newFilterGroup.get('typeFilter')?.setValue(filterData.typeFilter);
|
|
110
|
-
}
|
|
111
|
-
if (filterData.selectItem) {
|
|
112
|
-
newFilterGroup.get('selectItem')?.setValue(filterData.selectItem);
|
|
113
|
-
}
|
|
114
|
-
if (filterData.initialDate) {
|
|
115
|
-
newFilterGroup.get('initialDate')?.setValue(filterData.initialDate);
|
|
116
|
-
}
|
|
117
|
-
if (filterData.finalDate) {
|
|
118
|
-
newFilterGroup.get('finalDate')?.setValue(filterData.finalDate);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this.filtersForm.push(newFilterGroup);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
onSelectFilterChange() {
|
|
126
|
-
const lastIndex = this.filtersForm.length - 1;
|
|
127
|
-
const lastFilter = this.filtersForm.at(lastIndex);
|
|
128
|
-
if (lastFilter.get('selectFilter')?.value) {
|
|
129
|
-
this.addFilter();
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
removeFilter(index: number): void {
|
|
134
|
-
this.filtersForm.removeAt(index);
|
|
135
|
-
if (this.filtersForm.length === 0) {
|
|
136
|
-
this.addFilter();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
removeAllFilters(): void {
|
|
141
|
-
this.filtersForm.clear();
|
|
142
|
-
this.addFilter();
|
|
143
|
-
this.resetFilter();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// METHODS
|
|
147
|
-
public async ngOnInit() {
|
|
148
|
-
if (!this.data.color)
|
|
149
|
-
this.data.color = { bg: 'bg-primary', text: 'text-black' };
|
|
150
|
-
this.columnProperties = this.data.displayedColumns.map((column) => {
|
|
151
|
-
return column.property;
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
if (this.data.actionButton && !this.data.actionButton.condition) {
|
|
155
|
-
this.data.actionButton.condition = (_row?: any) => true;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (this.data.pagination) {
|
|
159
|
-
this.data.displayedColumns.forEach((col) => {
|
|
160
|
-
if (col.isFilterable) {
|
|
161
|
-
if (this.hasFilterableColumn === false)
|
|
162
|
-
this.hasFilterableColumn = true;
|
|
163
|
-
|
|
164
|
-
this.dropdownItems.push({
|
|
165
|
-
...col,
|
|
166
|
-
arrange: 'filter',
|
|
167
|
-
title: col.title,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
if (col.isSortable) {
|
|
171
|
-
if (this.hasSortableColumn === false) this.hasSortableColumn = true;
|
|
172
|
-
this.sortableDropdownItems.push({
|
|
173
|
-
...col,
|
|
174
|
-
arrange: 'ascending',
|
|
175
|
-
title: col.title + ': crescente',
|
|
176
|
-
});
|
|
177
|
-
this.sortableDropdownItems.push({
|
|
178
|
-
...col,
|
|
179
|
-
arrange: 'descending',
|
|
180
|
-
title: col.title + ': decrescente',
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
if (col.isFilterableByDate) {
|
|
184
|
-
this.dropdownItems.push({
|
|
185
|
-
...col,
|
|
186
|
-
arrange: 'filterByDate',
|
|
187
|
-
title: col.title + ': filtro por data',
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
if (this.data.filterableOptions) {
|
|
192
|
-
this.data.filterableOptions.forEach((option) =>
|
|
193
|
-
this.dropdownItems.push({ ...option, arrange: 'equals' })
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Sem paginação
|
|
199
|
-
if (this.data.pagination === false) {
|
|
200
|
-
await this.loadItems();
|
|
201
|
-
}
|
|
202
|
-
// Com paginação
|
|
203
|
-
if (this.data.pagination === true) {
|
|
204
|
-
if (this.data.sortBy)
|
|
205
|
-
this.sortBy = {
|
|
206
|
-
field: this.data.sortBy.field,
|
|
207
|
-
order: this.data.sortBy.order,
|
|
208
|
-
};
|
|
209
|
-
this.filterSubject
|
|
210
|
-
.pipe(debounceTime(this.debounceTimeMs))
|
|
211
|
-
.subscribe(() => {
|
|
212
|
-
this.loadItemsPaginated('forward', true);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
this.isLoading = true;
|
|
216
|
-
await this.loadItemsPaginated('forward', true);
|
|
217
|
-
|
|
218
|
-
this.sort.active = 'createdAt';
|
|
219
|
-
this.sort.direction = 'desc';
|
|
220
|
-
this.dataSource.paginator = this.paginator;
|
|
221
|
-
this.dataSource.sort = this.sort;
|
|
222
|
-
this.totalItems = 0;
|
|
223
|
-
if (this.data.totalRef) {
|
|
224
|
-
for (const totalRef of this.data.totalRef) {
|
|
225
|
-
const totalRefDoc = await totalRef.ref.get();
|
|
226
|
-
const docData = totalRefDoc.data() as any;
|
|
227
|
-
if (docData && docData[totalRef.field])
|
|
228
|
-
this.totalItems = (this.totalItems +
|
|
229
|
-
docData[totalRef.field]) as number;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
this.isLoading = false;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
getDisplayValue(col: any, row: any, withinLimit: boolean = false): string {
|
|
237
|
-
let value: string;
|
|
238
|
-
if (row[col.property] === null || row[col.property] === undefined) {
|
|
239
|
-
return '';
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (col.calculateValue) {
|
|
243
|
-
value = String(col.calculateValue(row));
|
|
244
|
-
} else {
|
|
245
|
-
value = this.getNestedValue(row, col.property);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Verifica se é um array e tem arrayField definido
|
|
249
|
-
if (Array.isArray(value) && col.arrayField) {
|
|
250
|
-
value = this.formatArrayValue(value, col.arrayField);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (col.queryLength && row[col.property]) {
|
|
254
|
-
value = row[col.property];
|
|
255
|
-
}
|
|
256
|
-
value = col.pipe ? col.pipe.transform(value) : value;
|
|
257
|
-
return withinLimit ? value : value.substring(0, col.charLimit) + '...';
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
getNestedValue(obj: any, path: string): any {
|
|
261
|
-
if (!path) return undefined;
|
|
262
|
-
const properties = path.split('.');
|
|
263
|
-
return properties.reduce(
|
|
264
|
-
(acc, currentPart) => acc && acc[currentPart],
|
|
265
|
-
obj
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
private formatArrayValue(array: any[], field: string): string {
|
|
270
|
-
if (!Array.isArray(array) || array.length === 0) {
|
|
271
|
-
return '';
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const values = array
|
|
275
|
-
.map((item) => {
|
|
276
|
-
if (typeof item === 'object' && item !== null) {
|
|
277
|
-
return item[field] || '';
|
|
278
|
-
}
|
|
279
|
-
return String(item);
|
|
280
|
-
})
|
|
281
|
-
.filter((value) => value !== '' && value !== null && value !== undefined);
|
|
282
|
-
|
|
283
|
-
return values.join(', ');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Métodos sem paginação
|
|
287
|
-
private async loadItems() {
|
|
288
|
-
this.items = await this.tableService.getItems(this.data.collectionRef);
|
|
289
|
-
if (this.data.conditions) {
|
|
290
|
-
this.filterItems();
|
|
291
|
-
}
|
|
292
|
-
await this.loadRelations();
|
|
293
|
-
await this.loadQueryLengths();
|
|
294
|
-
this.totalItems = this.items.length;
|
|
295
|
-
this.dataSource = new MatTableDataSource(this.items);
|
|
296
|
-
this.dataSource.paginator = this.paginator;
|
|
297
|
-
this.dataSource.sort = this.sort;
|
|
298
|
-
if (this.data.sortBy) {
|
|
299
|
-
this.dataSource.sort.active = this.data.sortBy.field;
|
|
300
|
-
this.dataSource.sort.direction = this.data.sortBy.order;
|
|
301
|
-
this.dataSource.sort.sortChange.emit();
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
this.dataSource.filterPredicate = (data, filter: string) => {
|
|
305
|
-
return this.data.displayedColumns.some((col): boolean => {
|
|
306
|
-
if (col.filterPredicates) {
|
|
307
|
-
return col.filterPredicates.some((predicate): boolean => {
|
|
308
|
-
const propertyValue = data[col.property];
|
|
309
|
-
if (!propertyValue || typeof propertyValue !== 'object') {
|
|
310
|
-
return false;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const predicateValue = propertyValue[predicate];
|
|
314
|
-
if (predicateValue === null || predicateValue === undefined) {
|
|
315
|
-
return false;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
return String(predicateValue)
|
|
319
|
-
.trim()
|
|
320
|
-
.toLocaleLowerCase()
|
|
321
|
-
.includes(filter);
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (col.property && col.isFilterable) {
|
|
326
|
-
const propertyValue = data[col.property];
|
|
327
|
-
if (propertyValue === null || propertyValue === undefined) {
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
return String(propertyValue)
|
|
332
|
-
.trim()
|
|
333
|
-
.toLocaleLowerCase()
|
|
334
|
-
.includes(filter);
|
|
335
|
-
}
|
|
336
|
-
return false;
|
|
337
|
-
});
|
|
338
|
-
};
|
|
339
|
-
this.filterPredicate = this.dataSource.filterPredicate;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Métodos com paginação
|
|
343
|
-
private async loadItemsPaginated(
|
|
344
|
-
navigation: string = 'reload',
|
|
345
|
-
reset: boolean = false
|
|
346
|
-
) {
|
|
347
|
-
if (reset && ['forward', 'reload'].includes(navigation)) {
|
|
348
|
-
this.lastDoc = null;
|
|
349
|
-
}
|
|
350
|
-
if (reset && ['backward', 'reload'].includes(navigation)) {
|
|
351
|
-
this.firstDoc = null;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const activeFilters = this.filtersForm.controls
|
|
355
|
-
.flatMap((control) => {
|
|
356
|
-
const group = control as FormGroup;
|
|
357
|
-
const selectedFilter = group.get('selectFilter')?.value;
|
|
358
|
-
if (!selectedFilter) return [];
|
|
359
|
-
|
|
360
|
-
const arrange = selectedFilter.arrange;
|
|
361
|
-
|
|
362
|
-
if (arrange === 'filter') {
|
|
363
|
-
const filterValue = group.get('typeFilter')?.value;
|
|
364
|
-
if (!filterValue) return [];
|
|
365
|
-
return {
|
|
366
|
-
arrange,
|
|
367
|
-
filter: {
|
|
368
|
-
property: selectedFilter.property,
|
|
369
|
-
filtering: filterValue,
|
|
370
|
-
},
|
|
371
|
-
dateFilter: undefined,
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (arrange === 'filterByDate') {
|
|
376
|
-
const initial = group.get('initialDate')?.value;
|
|
377
|
-
const final = group.get('finalDate')?.value;
|
|
378
|
-
if (initial && final) {
|
|
379
|
-
const [dayI, monthI, yearI] = initial.split('/');
|
|
380
|
-
const initialDate = new Date(`${monthI}/${dayI}/${yearI}`);
|
|
381
|
-
const [dayF, monthF, yearF] = final.split('/');
|
|
382
|
-
const finalDate = new Date(`${monthF}/${dayF}/${yearF}`);
|
|
383
|
-
finalDate.setHours(23, 59, 59);
|
|
384
|
-
return {
|
|
385
|
-
arrange,
|
|
386
|
-
filter: undefined,
|
|
387
|
-
dateFilter: {
|
|
388
|
-
initial: initialDate,
|
|
389
|
-
final: finalDate,
|
|
390
|
-
},
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
return [];
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (selectedFilter.hasOwnProperty('items') && arrange === 'equals') {
|
|
397
|
-
const selectedItems = group.get('selectItem')?.value;
|
|
398
|
-
if (Array.isArray(selectedItems) && selectedItems.length > 0) {
|
|
399
|
-
return selectedItems.map((item) => ({
|
|
400
|
-
arrange,
|
|
401
|
-
filter: {
|
|
402
|
-
property: item.property,
|
|
403
|
-
filtering: item.value,
|
|
404
|
-
},
|
|
405
|
-
dateFilter: undefined,
|
|
406
|
-
}));
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
return [];
|
|
411
|
-
})
|
|
412
|
-
.filter((f) => f && (f.filter?.filtering !== undefined || f.dateFilter));
|
|
413
|
-
|
|
414
|
-
this.arrange = {
|
|
415
|
-
filters: activeFilters,
|
|
416
|
-
sortBy: this.sortBy,
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
const paginated: any = {
|
|
420
|
-
batchSize: this.pageSize,
|
|
421
|
-
collection: this.data.collection,
|
|
422
|
-
doc: { lastDoc: this.lastDoc, firstDoc: this.firstDoc },
|
|
423
|
-
navigation,
|
|
424
|
-
arrange: this.arrange,
|
|
425
|
-
conditions: this.data.conditions,
|
|
426
|
-
size: this.totalItems,
|
|
427
|
-
filterFn: this.data.filterFn,
|
|
428
|
-
clientPageIndex: this.currentClientPageIndex,
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
const result = await this.tableService.getPaginated(paginated);
|
|
432
|
-
this.items = result.items;
|
|
433
|
-
await this.loadRelations();
|
|
434
|
-
await this.loadQueryLengths();
|
|
435
|
-
this.lastDoc = result.lastDoc as any;
|
|
436
|
-
this.firstDoc = result.firstDoc as any;
|
|
437
|
-
|
|
438
|
-
// Atualizar currentClientPageIndex se retornado pelo fallback
|
|
439
|
-
if (result.currentClientPageIndex !== undefined) {
|
|
440
|
-
this.currentClientPageIndex = result.currentClientPageIndex;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
let sum = 0;
|
|
444
|
-
if (this.data.totalRef) {
|
|
445
|
-
for (const totalRef of this.data.totalRef) {
|
|
446
|
-
const totalRefDoc = await totalRef.ref.get();
|
|
447
|
-
const docData = totalRefDoc.data() as any;
|
|
448
|
-
if (docData || result.filterLength) {
|
|
449
|
-
sum =
|
|
450
|
-
result.filterLength ??
|
|
451
|
-
((sum + (docData ? docData[totalRef.field] : 0)) as number);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
this.totalItems = sum;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
this.hasNextPage = result.hasNextPage;
|
|
458
|
-
this.dataSource = new MatTableDataSource(this.items);
|
|
459
|
-
this.filterPredicate = this.dataSource.filterPredicate;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
async onPageChange(event?: PageEvent) {
|
|
463
|
-
if (this.data.pagination === true && event) {
|
|
464
|
-
this.isLoading = true;
|
|
465
|
-
|
|
466
|
-
const previousPageIndex = event.previousPageIndex ?? 0;
|
|
467
|
-
const pageIndex = event.pageIndex;
|
|
468
|
-
const currentComponentPageSize = this.pageSize;
|
|
469
|
-
const eventPageSize = event.pageSize;
|
|
470
|
-
const totalItems = this.totalItems;
|
|
471
|
-
|
|
472
|
-
let navigationDirection: string;
|
|
473
|
-
let resetDocs = false;
|
|
474
|
-
let originalPageSize: number | null = null;
|
|
475
|
-
|
|
476
|
-
const lastPageIndex = Math.max(
|
|
477
|
-
0,
|
|
478
|
-
Math.ceil(totalItems / eventPageSize) - 1
|
|
479
|
-
);
|
|
480
|
-
|
|
481
|
-
// Atualizar currentClientPageIndex sempre para o fallback
|
|
482
|
-
this.currentClientPageIndex = pageIndex;
|
|
483
|
-
|
|
484
|
-
if (previousPageIndex !== undefined && pageIndex > previousPageIndex) {
|
|
485
|
-
this.currentPageNumber++;
|
|
486
|
-
} else if (
|
|
487
|
-
previousPageIndex !== undefined &&
|
|
488
|
-
pageIndex < previousPageIndex
|
|
489
|
-
) {
|
|
490
|
-
this.currentPageNumber = Math.max(1, this.currentPageNumber - 1);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (eventPageSize !== currentComponentPageSize) {
|
|
494
|
-
console.log('Alterou a quantidade de elementos exibidos por página');
|
|
495
|
-
this.pageSize = eventPageSize;
|
|
496
|
-
navigationDirection = 'forward';
|
|
497
|
-
resetDocs = true;
|
|
498
|
-
this.currentClientPageIndex = 0;
|
|
499
|
-
} else if (
|
|
500
|
-
pageIndex === 0 &&
|
|
501
|
-
previousPageIndex !== undefined &&
|
|
502
|
-
pageIndex < previousPageIndex
|
|
503
|
-
) {
|
|
504
|
-
console.log('Pulou para a primeira página');
|
|
505
|
-
navigationDirection = 'forward';
|
|
506
|
-
this.currentPageNumber = 1;
|
|
507
|
-
this.currentClientPageIndex = 0;
|
|
508
|
-
resetDocs = true;
|
|
509
|
-
} else if (
|
|
510
|
-
pageIndex === lastPageIndex &&
|
|
511
|
-
previousPageIndex !== undefined &&
|
|
512
|
-
pageIndex > previousPageIndex &&
|
|
513
|
-
pageIndex - previousPageIndex > 1
|
|
514
|
-
) {
|
|
515
|
-
console.log('Pulou para a ultima página');
|
|
516
|
-
navigationDirection = 'backward';
|
|
517
|
-
resetDocs = true;
|
|
518
|
-
|
|
519
|
-
const itemsExpectedInLastPage =
|
|
520
|
-
totalItems - lastPageIndex * eventPageSize;
|
|
521
|
-
|
|
522
|
-
if (
|
|
523
|
-
itemsExpectedInLastPage > 0 &&
|
|
524
|
-
itemsExpectedInLastPage < eventPageSize
|
|
525
|
-
) {
|
|
526
|
-
originalPageSize = this.pageSize;
|
|
527
|
-
this.pageSize = itemsExpectedInLastPage;
|
|
528
|
-
}
|
|
529
|
-
} else if (
|
|
530
|
-
previousPageIndex !== undefined &&
|
|
531
|
-
pageIndex > previousPageIndex
|
|
532
|
-
) {
|
|
533
|
-
console.log('Procedendo');
|
|
534
|
-
navigationDirection = 'forward';
|
|
535
|
-
resetDocs = false;
|
|
536
|
-
} else if (
|
|
537
|
-
previousPageIndex !== undefined &&
|
|
538
|
-
pageIndex < previousPageIndex
|
|
539
|
-
) {
|
|
540
|
-
console.log('Retrocedendo.');
|
|
541
|
-
navigationDirection = 'backward';
|
|
542
|
-
resetDocs = false;
|
|
543
|
-
} else if (
|
|
544
|
-
previousPageIndex !== undefined &&
|
|
545
|
-
pageIndex === previousPageIndex
|
|
546
|
-
) {
|
|
547
|
-
console.log('Recarregando.');
|
|
548
|
-
navigationDirection = 'reload';
|
|
549
|
-
resetDocs = false;
|
|
550
|
-
} else if (previousPageIndex === undefined && pageIndex === 0) {
|
|
551
|
-
console.log(
|
|
552
|
-
'Evento inicial do paginador para pág 0. ngOnInit carregou.'
|
|
553
|
-
);
|
|
554
|
-
this.isLoading = false;
|
|
555
|
-
if (event) this.pageEvent = event;
|
|
556
|
-
return;
|
|
557
|
-
} else {
|
|
558
|
-
console.warn('INESPERADO! Condição de navegação não tratada:', event);
|
|
559
|
-
this.isLoading = false;
|
|
560
|
-
if (event) this.pageEvent = event;
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (navigationDirection) {
|
|
565
|
-
try {
|
|
566
|
-
await this.loadItemsPaginated(navigationDirection, resetDocs);
|
|
567
|
-
} catch (error) {
|
|
568
|
-
console.error('Erro ao carregar itens paginados:', error);
|
|
569
|
-
} finally {
|
|
570
|
-
if (originalPageSize !== null) {
|
|
571
|
-
this.pageSize = originalPageSize;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
if (event) this.pageEvent = event;
|
|
577
|
-
this.isLoading = false;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// Outros métodos
|
|
582
|
-
|
|
583
|
-
applyFilter(value: string) {
|
|
584
|
-
// Sem paginação
|
|
585
|
-
if (this.data.pagination === false) {
|
|
586
|
-
this.dataSource.filter = String(value).trim().toLowerCase();
|
|
587
|
-
}
|
|
588
|
-
// Com paginação
|
|
589
|
-
if (this.data.pagination === true) {
|
|
590
|
-
this.filterValue = value;
|
|
591
|
-
this.filterSubject.next(this.filterValue);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
goToDetails(row: any): void {
|
|
596
|
-
if (this.data.isNotClickable) {
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
const urlPath = this.data.url || this.data.name;
|
|
600
|
-
const url = this.router.serializeUrl(
|
|
601
|
-
this.router.createUrlTree([`/${urlPath}`, row.id])
|
|
602
|
-
);
|
|
603
|
-
window.open(url, '_blank');
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
async getRelation(params: {
|
|
607
|
-
id: any;
|
|
608
|
-
collection: string;
|
|
609
|
-
newProperty: string;
|
|
610
|
-
}): Promise<string> {
|
|
611
|
-
try {
|
|
612
|
-
let snapshot: firestore.DocumentSnapshot<unknown> | undefined;
|
|
613
|
-
if (
|
|
614
|
-
params.id !== '' &&
|
|
615
|
-
params.id !== undefined &&
|
|
616
|
-
params.collection !== undefined &&
|
|
617
|
-
params.collection !== ''
|
|
618
|
-
) {
|
|
619
|
-
snapshot = await firstValueFrom(
|
|
620
|
-
this.firestore.collection(params.collection).doc(params.id).get()
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
if (snapshot && snapshot.exists) {
|
|
624
|
-
const data = snapshot.data() as any;
|
|
625
|
-
return data?.[params.newProperty] ?? '';
|
|
626
|
-
}
|
|
627
|
-
return '';
|
|
628
|
-
} catch (e) {
|
|
629
|
-
console.log(e);
|
|
630
|
-
return '';
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
private async loadRelations() {
|
|
635
|
-
const relationPromises = this.data.displayedColumns
|
|
636
|
-
.filter((col) => col.relation)
|
|
637
|
-
.flatMap((col) =>
|
|
638
|
-
this.items.map(async (item) => {
|
|
639
|
-
if (col.relation) {
|
|
640
|
-
item[col.property] = await this.getRelation({
|
|
641
|
-
id: item[col.relation.property],
|
|
642
|
-
collection: col.relation.collection,
|
|
643
|
-
newProperty: col.relation.newProperty,
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
})
|
|
647
|
-
);
|
|
648
|
-
|
|
649
|
-
await Promise.all(relationPromises);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
async getQueryLength(params: {
|
|
653
|
-
item: any;
|
|
654
|
-
relation: {
|
|
655
|
-
collection: string;
|
|
656
|
-
property: string;
|
|
657
|
-
operator: WhereFilterOp;
|
|
658
|
-
value: string;
|
|
659
|
-
};
|
|
660
|
-
}) {
|
|
661
|
-
const snapshot = await this.firestore
|
|
662
|
-
.collection(params.relation.collection)
|
|
663
|
-
.ref.where(
|
|
664
|
-
params.relation.property,
|
|
665
|
-
params.relation.operator,
|
|
666
|
-
params.item[params.relation.value]
|
|
667
|
-
)
|
|
668
|
-
.get();
|
|
669
|
-
return snapshot.size;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
private async loadQueryLengths() {
|
|
673
|
-
const lengthPromises = this.data.displayedColumns
|
|
674
|
-
.filter((col) => col.queryLength)
|
|
675
|
-
.flatMap((col) =>
|
|
676
|
-
this.items.map(async (item) => {
|
|
677
|
-
if (col.queryLength) {
|
|
678
|
-
item[col.property] = await this.getQueryLength({
|
|
679
|
-
item: item,
|
|
680
|
-
relation: col.queryLength,
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
})
|
|
684
|
-
);
|
|
685
|
-
await Promise.all(lengthPromises);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
private filterItems() {
|
|
689
|
-
if (this.data.conditions) {
|
|
690
|
-
this.data.conditions.forEach((cond) => {
|
|
691
|
-
this.items = this.items.filter((item) => {
|
|
692
|
-
const operatorFunction = (this.tableService.operators as any)[
|
|
693
|
-
cond.operator
|
|
694
|
-
];
|
|
695
|
-
if (operatorFunction) {
|
|
696
|
-
return operatorFunction(
|
|
697
|
-
item[cond.firestoreProperty],
|
|
698
|
-
cond.dashProperty
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
return false;
|
|
702
|
-
});
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// Filtro de data
|
|
708
|
-
async search() {
|
|
709
|
-
if (this.selectSort.value) {
|
|
710
|
-
if (this.selectSort.value.arrange === 'ascending') {
|
|
711
|
-
this.sortBy = {
|
|
712
|
-
field: this.selectSort.value.property,
|
|
713
|
-
order: 'asc',
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
if (this.selectSort.value.arrange === 'descending') {
|
|
717
|
-
this.sortBy = {
|
|
718
|
-
field: this.selectSort.value.property,
|
|
719
|
-
order: 'desc',
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
await this.loadItemsPaginated('reload', true);
|
|
725
|
-
this.currentArrange =
|
|
726
|
-
this.filtersForm.length > 0
|
|
727
|
-
? this.filtersForm.at(0).get('selectFilter')?.value?.arrange ?? ''
|
|
728
|
-
: '';
|
|
729
|
-
this.paginator.firstPage();
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
async resetFilter() {
|
|
733
|
-
if (!this.data.pagination) {
|
|
734
|
-
this.dataSource.filter = '';
|
|
735
|
-
if (this.filterPredicate) {
|
|
736
|
-
this.dataSource.filterPredicate = this.filterPredicate;
|
|
737
|
-
}
|
|
738
|
-
} else {
|
|
739
|
-
this.dataSource.filter = '';
|
|
740
|
-
if (this.filterPredicate) {
|
|
741
|
-
this.dataSource.filterPredicate = this.filterPredicate;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
this.filtersForm.clear();
|
|
745
|
-
this.addFilter();
|
|
746
|
-
this.selectSort.patchValue('');
|
|
747
|
-
this.sortBy = {
|
|
748
|
-
order: this.data.sortBy ? this.data.sortBy.order : 'desc',
|
|
749
|
-
field: this.data.sortBy ? this.data.sortBy.field : 'createdAt',
|
|
750
|
-
};
|
|
751
|
-
|
|
752
|
-
await this.loadItemsPaginated('reload', true);
|
|
753
|
-
this.currentArrange = '';
|
|
754
|
-
this.paginator.firstPage();
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
// Método público para recarregar a tabela
|
|
758
|
-
async reloadTable() {
|
|
759
|
-
if (this.data.pagination) {
|
|
760
|
-
await this.loadItemsPaginated('reload', true);
|
|
761
|
-
this.paginator.firstPage();
|
|
762
|
-
} else {
|
|
763
|
-
await this.loadItems();
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
updateDisplayedColumns() {
|
|
768
|
-
if (this.dataSource) {
|
|
769
|
-
this.dataSource = new MatTableDataSource<any>([]);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
this.columnProperties = this.data.displayedColumns.map((column) => {
|
|
773
|
-
return column.property;
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
this.dropdownItems = [];
|
|
777
|
-
this.sortableDropdownItems = [];
|
|
778
|
-
|
|
779
|
-
this.data.displayedColumns.forEach((col) => {
|
|
780
|
-
if (col.isFilterable) {
|
|
781
|
-
this.dropdownItems.push({
|
|
782
|
-
...col,
|
|
783
|
-
arrange: 'filter',
|
|
784
|
-
title: col.title,
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
|
-
if (col.isSortable) {
|
|
788
|
-
this.sortableDropdownItems.push({
|
|
789
|
-
...col,
|
|
790
|
-
arrange: 'ascending',
|
|
791
|
-
title: col.title + ': crescente',
|
|
792
|
-
});
|
|
793
|
-
this.sortableDropdownItems.push({
|
|
794
|
-
...col,
|
|
795
|
-
arrange: 'descending',
|
|
796
|
-
title: col.title + ': decrescente',
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
if (col.isFilterableByDate) {
|
|
800
|
-
this.dropdownItems.push({
|
|
801
|
-
...col,
|
|
802
|
-
arrange: 'filterByDate',
|
|
803
|
-
title: col.title + ': filtro por data',
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
});
|
|
807
|
-
|
|
808
|
-
if (this.data.filterableOptions) {
|
|
809
|
-
this.data.filterableOptions.forEach((option) =>
|
|
810
|
-
this.dropdownItems.push({ ...option, arrange: 'equals' })
|
|
811
|
-
);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
isString(value: any): boolean {
|
|
816
|
-
return typeof value === 'string';
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// Métodos para controle do tooltip
|
|
820
|
-
onCellMouseEnter(event: MouseEvent, row: any, col: any): void {
|
|
821
|
-
// Só mostrar tooltip se a coluna tiver charLimit definido
|
|
822
|
-
if (!col.charLimit) {
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
const fullValue = this.getDisplayValue(col, row, true);
|
|
827
|
-
|
|
828
|
-
// Só mostrar tooltip se o valor completo for maior que o limite
|
|
829
|
-
if (fullValue.length <= col.charLimit) {
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
this.hoveredCell = { row, col };
|
|
834
|
-
this.tooltipContent = fullValue;
|
|
835
|
-
|
|
836
|
-
// Definir posição do tooltip
|
|
837
|
-
this.tooltipPosition = {
|
|
838
|
-
x: event.clientX + 10,
|
|
839
|
-
y: event.clientY - 10,
|
|
840
|
-
};
|
|
841
|
-
|
|
842
|
-
// Timeout para mostrar o tooltip
|
|
843
|
-
this.tooltipTimeout = setTimeout(() => {
|
|
844
|
-
if (
|
|
845
|
-
this.hoveredCell &&
|
|
846
|
-
this.hoveredCell.row === row &&
|
|
847
|
-
this.hoveredCell.col === col
|
|
848
|
-
) {
|
|
849
|
-
this.showTooltip = true;
|
|
850
|
-
}
|
|
851
|
-
}, 500);
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
onCellMouseLeave(): void {
|
|
855
|
-
if (this.tooltipTimeout) {
|
|
856
|
-
clearTimeout(this.tooltipTimeout);
|
|
857
|
-
this.tooltipTimeout = null;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
this.showTooltip = false;
|
|
861
|
-
this.hoveredCell = null;
|
|
862
|
-
this.tooltipContent = '';
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
onCellMouseMove(event: MouseEvent): void {
|
|
866
|
-
if (this.showTooltip) {
|
|
867
|
-
this.tooltipPosition = {
|
|
868
|
-
x: event.clientX + 10,
|
|
869
|
-
y: event.clientY - 10,
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// Métodos para inversão vertical dos tabs
|
|
875
|
-
getTabGroups(tabs: any[]): number[] {
|
|
876
|
-
if (!tabs || tabs.length === 0) return [];
|
|
877
|
-
|
|
878
|
-
const totalGroups = Math.ceil(tabs.length / 6);
|
|
879
|
-
const groups: number[] = [];
|
|
880
|
-
|
|
881
|
-
// Criar array de índices invertidos (último grupo primeiro)
|
|
882
|
-
for (let i = totalGroups - 1; i >= 0; i--) {
|
|
883
|
-
groups.push(i);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
return groups;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
getTabGroup(tabs: any[], groupIndex: number): any[] {
|
|
890
|
-
if (!tabs || tabs.length === 0) return [];
|
|
891
|
-
|
|
892
|
-
const startIndex = groupIndex * 6;
|
|
893
|
-
const endIndex = Math.min(startIndex + 6, tabs.length);
|
|
894
|
-
|
|
895
|
-
return tabs.slice(startIndex, endIndex);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
getRealTabIndex(groupIndex: number, tabIndexInGroup: number): number {
|
|
899
|
-
if (!this.data.tabs?.tabsData) return 0;
|
|
900
|
-
const totalGroups = Math.ceil(this.data.tabs.tabsData.length / 6);
|
|
901
|
-
const realGroupIndex = totalGroups - 1 - groupIndex;
|
|
902
|
-
return realGroupIndex * 6 + tabIndexInGroup;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
onTableSelected(i: number, j: number) {
|
|
906
|
-
if (!this.data.tabs?.tabsData || !this.data.tabs.method) return;
|
|
907
|
-
this.selectedTab = this.getRealTabIndex(i, j);
|
|
908
|
-
const tab = this.data.tabs.tabsData[this.selectedTab];
|
|
909
|
-
if (tab) {
|
|
910
|
-
this.data.tabs.method(tab, this.selectedTab);
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
isTabSelected(originalIndex: number): boolean {
|
|
915
|
-
return this.selectedTab === originalIndex;
|
|
916
|
-
}
|
|
917
|
-
}
|