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
package/README.md
CHANGED
|
@@ -1,84 +1,64 @@
|
|
|
1
|
-
# Firebase Table KXP
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
# Firebase Table KXP Library
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@adryanmmm/firebase-table-kxp-lib)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Firebase Table KXP Library is a production-ready Angular data table created by **KXP Tech** to power enterprise dashboards backed by Firebase Firestore. It wraps complex Firestore querying, pagination, filtering, and material design styling into a plug-and-play module so teams can ship data-rich experiences faster.
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
- [Why Firebase Table KXP](#why-firebase-table-kxp)
|
|
10
|
+
- [Core Features](#core-features)
|
|
11
|
+
- [Installation & Setup](#installation--setup)
|
|
12
|
+
- [Configuration Overview](#configuration-overview)
|
|
13
|
+
- [API Reference](#api-reference)
|
|
14
|
+
- [TableData](#tabledata)
|
|
15
|
+
- [Column](#column)
|
|
16
|
+
- [Tab & TabData](#tab--tabdata)
|
|
17
|
+
- [FilterableOption](#filterableoption)
|
|
18
|
+
- [Condition](#condition)
|
|
19
|
+
- [Arrange](#arrange)
|
|
20
|
+
- [Image](#image)
|
|
21
|
+
- [Data Export (Download)](#data-export-download)
|
|
22
|
+
- [Powered by KXP Tech](#powered-by-kxp-tech)
|
|
23
|
+
- [Contributing & License](#contributing--license)
|
|
24
|
+
- [Support the Project](#support-the-project)
|
|
25
|
+
|
|
26
|
+
## Why Firebase Table KXP
|
|
27
|
+
|
|
28
|
+
- Built and battle-tested at **KXP Tech** across projects with thousands of active users
|
|
29
|
+
- Eliminates weeks of custom table work; drop it in and configure
|
|
30
|
+
- Handles Firestore quirks (pagination, missing indexes, fallbacks) automatically
|
|
31
|
+
- Provides a strongly typed API that scales with project complexity
|
|
32
|
+
- Plays nicely with Angular Material, Firebase, and Angular Fire
|
|
33
|
+
|
|
34
|
+
## Core Features
|
|
35
|
+
|
|
36
|
+
- Full Firestore integration with server-side and client-side pagination
|
|
37
|
+
- Text, date, and custom filters with automatic index fallback when needed
|
|
38
|
+
- Configurable columns for relations, custom renderers, actions, and exports
|
|
39
|
+
- Built-in CSV export hooks and actionable buttons per row
|
|
40
|
+
- Pagination, sorting, and filter state shared between tabs
|
|
41
|
+
- Optional color theming, tabbed datasets, and custom navigation per row
|
|
42
|
+
- Works seamlessly with Angular 15, Angular Material, Angular Fire, and RxJS
|
|
43
|
+
|
|
44
|
+
## Installation & Setup
|
|
20
45
|
|
|
21
46
|
```bash
|
|
22
|
-
npm install
|
|
47
|
+
npm install @adryanmmm/firebase-table-kxp-lib
|
|
23
48
|
```
|
|
24
49
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
### Se você usa npm 6 ou anterior
|
|
50
|
+
The host application should already depend on:
|
|
28
51
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
npm install ng-firebase-table-kxp
|
|
33
|
-
npm install @angular/fire@^15.0.0 firebase@^9.0.0 @angular/material@^15.0.0 @angular/cdk@^15.0.0 ngx-toastr@^16.0.0 moment@^2.29.0 ngx-mask@^15.0.0
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
**Como verificar sua versão do npm:**
|
|
37
|
-
```bash
|
|
38
|
-
npm --version
|
|
39
|
-
# Se retornar 7.x ou superior, está tudo certo!
|
|
40
|
-
```
|
|
52
|
+
- Angular 15 packages: `@angular/common`, `@angular/core`, `@angular/forms`, `@angular/router`, `@angular/cdk`, `@angular/material`
|
|
53
|
+
- Firebase stack: `@angular/fire` (7.x) and `firebase` (9.x)
|
|
54
|
+
- Utilities: `ngx-toastr` (16.x), `moment` (2.29.x), `ngx-mask` (15.x)
|
|
41
55
|
|
|
42
|
-
## 🔧 Dependências
|
|
43
|
-
|
|
44
|
-
A biblioteca usa as seguintes peer dependencies (instaladas automaticamente):
|
|
45
|
-
|
|
46
|
-
- `@angular/fire` ^15.0.0 - Integração com Firebase
|
|
47
|
-
- `firebase` ^9.0.0 - SDK do Firebase
|
|
48
|
-
- `@angular/material` ^15.0.0 - Componentes Material
|
|
49
|
-
- `@angular/cdk` ^15.0.0 - Component Dev Kit
|
|
50
|
-
- `ngx-toastr` ^16.0.0 - Notificações toast
|
|
51
|
-
- `moment` ^2.29.0 - Manipulação de datas
|
|
52
|
-
- `ngx-mask` ^15.0.0 - Máscaras de input
|
|
53
|
-
|
|
54
|
-
## 🚀 Configuração
|
|
55
|
-
|
|
56
|
-
### 1. Importe o módulo no seu `app.module.ts`:
|
|
57
56
|
|
|
57
|
+
1. **Import the module**
|
|
58
58
|
```typescript
|
|
59
|
-
import {
|
|
60
|
-
import { BrowserModule } from '@angular/platform-browser';
|
|
61
|
-
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|
62
|
-
|
|
63
|
-
// Firebase
|
|
64
|
-
import { AngularFireModule } from '@angular/fire/compat';
|
|
65
|
-
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
|
|
66
|
-
|
|
67
|
-
// Toastr
|
|
68
|
-
import { ToastrModule } from 'ngx-toastr';
|
|
69
|
-
|
|
70
|
-
// Firebase Table KXP Lib
|
|
71
|
-
import { FirebaseTableKxpLibModule } from 'ng-firebase-table-kxp';
|
|
72
|
-
|
|
73
|
-
// Mask
|
|
74
|
-
import { NgxMaskModule } from 'ngx-mask';
|
|
75
|
-
|
|
76
|
-
import { environment } from '../environments/environment';
|
|
59
|
+
import { FirebaseTableKxpLibModule } from '@adryanmmm/firebase-table-kxp-lib';
|
|
77
60
|
|
|
78
61
|
@NgModule({
|
|
79
|
-
declarations: [
|
|
80
|
-
AppComponent
|
|
81
|
-
],
|
|
82
62
|
imports: [
|
|
83
63
|
BrowserModule,
|
|
84
64
|
BrowserAnimationsModule,
|
|
@@ -87,390 +67,529 @@ import { environment } from '../environments/environment';
|
|
|
87
67
|
ToastrModule.forRoot(),
|
|
88
68
|
NgxMaskModule.forRoot(),
|
|
89
69
|
FirebaseTableKxpLibModule
|
|
90
|
-
]
|
|
91
|
-
providers: [],
|
|
92
|
-
bootstrap: [AppComponent]
|
|
70
|
+
]
|
|
93
71
|
})
|
|
94
|
-
export class AppModule {
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 2. Configure o Firebase no `environment.ts`:
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
export const environment = {
|
|
101
|
-
production: false,
|
|
102
|
-
firebase: {
|
|
103
|
-
apiKey: "sua-api-key",
|
|
104
|
-
authDomain: "seu-projeto.firebaseapp.com",
|
|
105
|
-
projectId: "seu-projeto-id",
|
|
106
|
-
storageBucket: "seu-projeto.appspot.com",
|
|
107
|
-
messagingSenderId: "123456789",
|
|
108
|
-
appId: "seu-app-id"
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### 3. Importe os estilos do Angular Material no `styles.scss`:
|
|
114
|
-
|
|
115
|
-
```scss
|
|
116
|
-
@import '@angular/material/prebuilt-themes/indigo-pink.css';
|
|
117
|
-
@import 'ngx-toastr/toastr';
|
|
118
|
-
|
|
119
|
-
// Se estiver usando Tailwind CSS, adicione também:
|
|
120
|
-
@tailwind base;
|
|
121
|
-
@tailwind components;
|
|
122
|
-
@tailwind utilities;
|
|
72
|
+
export class AppModule {}
|
|
123
73
|
```
|
|
124
74
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
### No seu componente TypeScript:
|
|
128
|
-
|
|
75
|
+
2. **Drop the component**
|
|
129
76
|
```typescript
|
|
130
|
-
import { Component, OnInit } from '@angular/core';
|
|
131
|
-
import { AngularFirestore } from '@angular/fire/compat/firestore';
|
|
132
|
-
import { TableData, Column } from 'ng-firebase-table-kxp';
|
|
133
|
-
|
|
134
77
|
@Component({
|
|
135
78
|
selector: 'app-users',
|
|
136
79
|
template: `
|
|
137
|
-
<lib-table
|
|
138
|
-
[data]="
|
|
80
|
+
<lib-table
|
|
81
|
+
[data]="table"
|
|
139
82
|
[downloadTable]="downloadTable">
|
|
140
83
|
</lib-table>
|
|
141
84
|
`
|
|
142
85
|
})
|
|
143
|
-
export class UsersComponent
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
download: true,
|
|
155
|
-
sortBy: {
|
|
156
|
-
field: 'createdAt',
|
|
157
|
-
order: 'desc'
|
|
158
|
-
},
|
|
159
|
-
displayedColumns: [
|
|
160
|
-
{
|
|
161
|
-
property: 'name',
|
|
162
|
-
title: 'Nome',
|
|
163
|
-
isFilterable: true,
|
|
164
|
-
isSortable: true
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
property: 'email',
|
|
168
|
-
title: 'E-mail',
|
|
169
|
-
isFilterable: true
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
property: 'createdAt',
|
|
173
|
-
title: 'Data de Criação',
|
|
174
|
-
isFilterableByDate: true,
|
|
175
|
-
isSortable: true,
|
|
176
|
-
pipe: new DatePipe('pt-BR')
|
|
177
|
-
}
|
|
178
|
-
]
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
downloadTable = (arrange, conditions) => {
|
|
183
|
-
// Lógica para exportar a tabela
|
|
184
|
-
console.log('Exportando tabela...', arrange, conditions);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## 📖 API Completa
|
|
190
|
-
|
|
191
|
-
### TableData Interface
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
interface TableData {
|
|
195
|
-
// Configurações básicas
|
|
196
|
-
name: string; // Nome da tabela
|
|
197
|
-
collection: string; // Nome da collection no Firestore
|
|
198
|
-
collectionRef: CollectionReference; // Referência da collection
|
|
199
|
-
pagination: boolean; // Habilitar paginação
|
|
200
|
-
download: boolean; // Habilitar exportação
|
|
201
|
-
|
|
202
|
-
// Colunas
|
|
203
|
-
displayedColumns: Column[]; // Array de colunas a exibir
|
|
204
|
-
|
|
205
|
-
// Ordenação
|
|
206
|
-
sortBy?: {
|
|
207
|
-
field: string;
|
|
208
|
-
order: 'asc' | 'desc';
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// Filtros e condições
|
|
212
|
-
conditions?: Condition[]; // Condições where do Firestore
|
|
213
|
-
filterFn?: (item: any) => boolean; // Função de filtro customizada
|
|
214
|
-
filterableOptions?: FilterableOption[]; // Opções de filtro personalizadas
|
|
215
|
-
|
|
216
|
-
// Aparência
|
|
217
|
-
color?: {
|
|
218
|
-
bg: string; // Classe CSS para fundo
|
|
219
|
-
text: string; // Classe CSS para texto
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
// Navegação
|
|
223
|
-
isNotClickable?: boolean; // Desabilitar clique nas linhas
|
|
224
|
-
url?: string; // URL base para navegação
|
|
225
|
-
|
|
226
|
-
// Totalizadores
|
|
227
|
-
totalRef?: {
|
|
228
|
-
ref: DocumentReference;
|
|
229
|
-
field: string;
|
|
230
|
-
}[];
|
|
231
|
-
|
|
232
|
-
// Botão de ação
|
|
233
|
-
actionButton?: {
|
|
234
|
-
label: string;
|
|
235
|
-
routerLink: string;
|
|
236
|
-
icon?: string;
|
|
237
|
-
colorClass?: string;
|
|
238
|
-
method?: (row: any, event?: any) => any;
|
|
239
|
-
condition?: (row: any) => boolean;
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
// Tabs
|
|
243
|
-
tabs?: {
|
|
244
|
-
method: (tab: any, event?: any) => any;
|
|
245
|
-
tabsData: TabData[];
|
|
86
|
+
export class UsersComponent {
|
|
87
|
+
table: TableData = {
|
|
88
|
+
name: 'users',
|
|
89
|
+
collection: 'users',
|
|
90
|
+
collectionRef: this.firestore.collection('users').ref,
|
|
91
|
+
pagination: true,
|
|
92
|
+
download: true,
|
|
93
|
+
displayedColumns: [
|
|
94
|
+
{ property: 'name', title: 'Name', isFilterable: true, isSortable: true },
|
|
95
|
+
{ property: 'email', title: 'Email', isFilterable: true }
|
|
96
|
+
]
|
|
246
97
|
};
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Column Interface
|
|
251
98
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
property: string; // Propriedade do objeto
|
|
255
|
-
title?: string; // Título da coluna
|
|
256
|
-
charLimit?: number; // Limite de caracteres (com tooltip)
|
|
257
|
-
|
|
258
|
-
// Formatação
|
|
259
|
-
pipe?: PipeTransform; // Pipe do Angular para formatação
|
|
260
|
-
calculateValue?: (row: any) => any; // Função para calcular valor
|
|
261
|
-
|
|
262
|
-
// Filtros e ordenação
|
|
263
|
-
isFilterable?: boolean; // Permite filtrar por texto
|
|
264
|
-
isSortable?: boolean; // Permite ordenação
|
|
265
|
-
isFilterableByDate?: boolean; // Permite filtro por data
|
|
266
|
-
filterPredicates?: string[]; // Predicados customizados
|
|
267
|
-
|
|
268
|
-
// Links e downloads
|
|
269
|
-
hasLink?: boolean | string; // Tornar valor um link
|
|
270
|
-
hasDownload?: boolean | string; // Habilitar download
|
|
271
|
-
|
|
272
|
-
// Imagens
|
|
273
|
-
image?: {
|
|
274
|
-
class: string;
|
|
275
|
-
path?: string;
|
|
276
|
-
url?: boolean;
|
|
277
|
-
default?: string;
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// Ícones e botões
|
|
281
|
-
iconClass?: {
|
|
282
|
-
text?: string;
|
|
283
|
-
class?: string;
|
|
284
|
-
condition?: (row: any) => any;
|
|
285
|
-
buttonMethod?: (row: any, event?: any) => any;
|
|
286
|
-
}[];
|
|
287
|
-
|
|
288
|
-
// Relações com outras collections
|
|
289
|
-
relation?: {
|
|
290
|
-
collection: string;
|
|
291
|
-
property: string;
|
|
292
|
-
newProperty: string;
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
// Query lengths
|
|
296
|
-
queryLength?: {
|
|
297
|
-
collection: string;
|
|
298
|
-
property: string;
|
|
299
|
-
operator: WhereFilterOp;
|
|
300
|
-
value: string;
|
|
99
|
+
downloadTable = (arrange: any, conditions: any[]) => {
|
|
100
|
+
// export logic
|
|
301
101
|
};
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
arrayField?: string; // Campo para exibir de arrays de objetos
|
|
305
|
-
|
|
306
|
-
// Métodos customizados
|
|
307
|
-
method?: (row: any, event?: any) => any;
|
|
102
|
+
|
|
103
|
+
constructor(private firestore: AngularFirestore) {}
|
|
308
104
|
}
|
|
309
105
|
```
|
|
310
106
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
107
|
+
3. **Make sure global styles include**
|
|
108
|
+
```scss
|
|
109
|
+
@import '@angular/material/prebuilt-themes/indigo-pink.css';
|
|
110
|
+
@import 'ngx-toastr/toastr';
|
|
111
|
+
```
|
|
314
112
|
|
|
113
|
+
## Configuration Overview
|
|
114
|
+
|
|
115
|
+
- `TableData` defines the table behavior: source collection, pagination, sorting, tabs, exported totals, and custom navigation.
|
|
116
|
+
- `Column` describes each displayed column: filters, relations, custom templates, icon actions, download buttons, and data formatting.
|
|
117
|
+
- `Condition` lets you predefine Firestore `where` clauses applied before any user filters.
|
|
118
|
+
- Optional hooks like `filterFn`, `actionButton`, and `tabs` enable richer UX without duplicating logic.
|
|
119
|
+
- Automatic client-side fallback when Firestore reports missing composite indexes, plus logging of the index link to a `missingIndexes` collection.
|
|
120
|
+
- Support for joining related collections, counting related documents per row, and managing dialog-driven actions via `iconClass` or `actionButton` handlers.
|
|
121
|
+
- Compatible with SSR, lazy-loaded modules, and Angular standalone components.
|
|
122
|
+
|
|
123
|
+
## API Reference
|
|
124
|
+
|
|
125
|
+
This section provides detailed documentation for all configuration interfaces available in the library.
|
|
126
|
+
|
|
127
|
+
### TableData
|
|
128
|
+
|
|
129
|
+
The main configuration object that defines the entire table behavior.
|
|
130
|
+
|
|
131
|
+
| Property | Type | Required | Description |
|
|
132
|
+
|----------|------|----------|-------------|
|
|
133
|
+
| `name` | `string` | ✅ | Unique identifier for the table. Used internally for caching and state management. |
|
|
134
|
+
| `collection` | `string` | ✅ | Name of the Firestore collection to query. |
|
|
135
|
+
| `collectionRef` | `CollectionReference` | ✅ | Reference to the Firestore collection. Use `firestore.collection('name').ref`. |
|
|
136
|
+
| `displayedColumns` | `Column[]` | ✅ | Array of column definitions. Defines what data to display and how. See [Column](#column). |
|
|
137
|
+
| `pagination` | `boolean` | ✅ | Enable or disable pagination. When `true`, data is loaded in batches. |
|
|
138
|
+
| `download` | `boolean` | ✅ | Enable or disable the export/download functionality. |
|
|
139
|
+
| `sortBy` | `{field: string, order: OrderByDirection}` | ❌ | Default sorting configuration. `order` can be `'asc'` or `'desc'`. |
|
|
140
|
+
| `conditions` | `Condition[]` | ❌ | Array of Firestore where clauses applied before user filters. See [Condition](#condition). |
|
|
141
|
+
| `filterFn` | `(item: any) => boolean` | ❌ | Custom client-side filter function. Applied after Firestore queries. |
|
|
142
|
+
| `filterableOptions` | `FilterableOption[]` | ❌ | Predefined filter options displayed as buttons. See [FilterableOption](#filterableoption). |
|
|
143
|
+
| `url` | `string` | ❌ | Base URL for row navigation. Row clicks navigate to `{url}/{row.id}`. |
|
|
144
|
+
| `isNotClickable` | `boolean` | ❌ | When `true`, disables row click navigation. |
|
|
145
|
+
| `color` | `{bg: string, text: string}` | ❌ | CSS classes for custom table theming. `bg` for background, `text` for text color. |
|
|
146
|
+
| `totalRef` | `{ref: DocumentReference, field: string}[]` | ❌ | References to documents containing total counts. Displayed in the table footer. |
|
|
147
|
+
| `actionButton` | `ActionButton` | ❌ | Configuration for a primary action button (e.g., "Add New"). |
|
|
148
|
+
| `tabs` | `Tab` | ❌ | Configuration for tabbed data views. See [Tab & TabData](#tab--tabdata). |
|
|
149
|
+
|
|
150
|
+
**ActionButton Properties:**
|
|
151
|
+
- `label` (string): Button text
|
|
152
|
+
- `routerLink` (string): Navigation route
|
|
153
|
+
- `icon` (string): CSS class for icon (e.g., FontAwesome)
|
|
154
|
+
- `colorClass` (string): CSS class for button styling
|
|
155
|
+
- `method` ((row, event) => any): Custom click handler
|
|
156
|
+
- `condition` ((row) => boolean): Function to conditionally show the button
|
|
157
|
+
|
|
158
|
+
**Example:**
|
|
315
159
|
```typescript
|
|
316
|
-
|
|
160
|
+
const tableData: TableData = {
|
|
317
161
|
name: 'products',
|
|
318
162
|
collection: 'products',
|
|
319
163
|
collectionRef: this.firestore.collection('products').ref,
|
|
320
164
|
pagination: true,
|
|
321
165
|
download: true,
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
title: 'Produto',
|
|
326
|
-
isFilterable: true
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
property: 'price',
|
|
330
|
-
title: 'Preço',
|
|
331
|
-
pipe: new CurrencyPipe('pt-BR'),
|
|
332
|
-
isSortable: true
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
property: 'category',
|
|
336
|
-
title: 'Categoria',
|
|
337
|
-
isFilterable: true
|
|
338
|
-
}
|
|
166
|
+
sortBy: { field: 'createdAt', order: 'desc' },
|
|
167
|
+
conditions: [
|
|
168
|
+
{ operator: '==', firestoreProperty: 'active', dashProperty: true }
|
|
339
169
|
],
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
]
|
|
170
|
+
displayedColumns: [/* ... */],
|
|
171
|
+
actionButton: {
|
|
172
|
+
label: 'Add Product',
|
|
173
|
+
routerLink: '/products/new',
|
|
174
|
+
icon: 'fa fa-plus',
|
|
175
|
+
colorClass: 'bg-blue-500'
|
|
176
|
+
}
|
|
349
177
|
};
|
|
350
178
|
```
|
|
351
179
|
|
|
352
|
-
|
|
353
|
-
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Column
|
|
183
|
+
|
|
184
|
+
Defines how each column is displayed, filtered, and interacted with.
|
|
185
|
+
|
|
186
|
+
| Property | Type | Required | Description |
|
|
187
|
+
|----------|------|----------|-------------|
|
|
188
|
+
| `property` | `string` | ✅ | Name of the property in the data object to display. |
|
|
189
|
+
| `title` | `string` | ❌ | Column header text. Defaults to `property` if not provided. |
|
|
190
|
+
| `charLimit` | `number` | ❌ | Maximum characters to display. Shows tooltip with full text on hover. |
|
|
191
|
+
| `pipe` | `PipeTransform` | ❌ | Angular pipe for formatting values (e.g., `DatePipe`, `CurrencyPipe`). |
|
|
192
|
+
| `calculateValue` | `(row: any) => any` | ❌ | Function to compute the display value from the row data. |
|
|
193
|
+
| `isSortable` | `boolean` | ❌ | Enable sorting for this column. |
|
|
194
|
+
| `isFilterable` | `boolean` | ❌ | Enable text-based filtering for this column. |
|
|
195
|
+
| `isFilterableByDate` | `boolean` | ❌ | Enable date range filtering. Shows date picker inputs. |
|
|
196
|
+
| `filterPredicates` | `string[]` | ❌ | Additional properties to include when filtering this column. |
|
|
197
|
+
| `hasLink` | `boolean \| string` | ❌ | Make the cell value a clickable link. If string, uses as the navigation path. |
|
|
198
|
+
| `hasDownload` | `boolean \| string` | ❌ | Add download icon to the cell. If string, uses as the download URL property. |
|
|
199
|
+
| `arrayField` | `string` | ❌ | When data is an array of objects, specify which field to display. |
|
|
200
|
+
| `method` | `(row: any, event?: any) => any` | ❌ | Custom click handler for the cell. |
|
|
201
|
+
| `image` | `Image` | ❌ | Configuration for displaying images. See [Image](#image). |
|
|
202
|
+
| `iconClass` | `IconClass[]` | ❌ | Array of icon/button configurations for action buttons in the cell. |
|
|
203
|
+
| `relation` | `Relation` | ❌ | Configuration for joining related collection data. |
|
|
204
|
+
| `queryLength` | `QueryLength` | ❌ | Configuration for counting related documents. |
|
|
205
|
+
|
|
206
|
+
**IconClass Properties:**
|
|
207
|
+
- `class` (string): CSS classes for the icon/button
|
|
208
|
+
- `text` (string): Text to display alongside icon
|
|
209
|
+
- `condition` ((row) => any): Function to conditionally show the icon
|
|
210
|
+
- `buttonMethod` ((row, event) => any): Click handler for the icon
|
|
211
|
+
|
|
212
|
+
**Relation Properties:**
|
|
213
|
+
- `collection` (string): Name of the related collection
|
|
214
|
+
- `property` (string): Property in current row containing the related document ID
|
|
215
|
+
- `newProperty` (string): Property from related document to fetch and display
|
|
216
|
+
|
|
217
|
+
**QueryLength Properties:**
|
|
218
|
+
- `collection` (string): Collection to count documents from
|
|
219
|
+
- `property` (string): Field to match in the query
|
|
220
|
+
- `operator` (WhereFilterOp): Firestore operator (e.g., `'=='`, `'>'`)
|
|
221
|
+
- `value` (string): Property from current row to use as query value
|
|
222
|
+
|
|
223
|
+
**Example:**
|
|
354
224
|
```typescript
|
|
355
|
-
|
|
225
|
+
const columns: Column[] = [
|
|
226
|
+
{
|
|
227
|
+
property: 'name',
|
|
228
|
+
title: 'Product Name',
|
|
229
|
+
isFilterable: true,
|
|
230
|
+
isSortable: true,
|
|
231
|
+
charLimit: 50
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
property: 'price',
|
|
235
|
+
title: 'Price',
|
|
236
|
+
pipe: new CurrencyPipe('en-US'),
|
|
237
|
+
isSortable: true
|
|
238
|
+
},
|
|
356
239
|
{
|
|
357
|
-
property: '
|
|
358
|
-
title: '
|
|
240
|
+
property: 'createdAt',
|
|
241
|
+
title: 'Created',
|
|
242
|
+
pipe: new DatePipe('en-US'),
|
|
243
|
+
isFilterableByDate: true
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
property: 'categoryName',
|
|
247
|
+
title: 'Category',
|
|
359
248
|
relation: {
|
|
360
|
-
collection: '
|
|
361
|
-
property: '
|
|
362
|
-
newProperty: 'name'
|
|
249
|
+
collection: 'categories',
|
|
250
|
+
property: 'categoryId',
|
|
251
|
+
newProperty: 'name'
|
|
363
252
|
}
|
|
364
|
-
}
|
|
365
|
-
]
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
### Exemplo com Botões Customizados
|
|
369
|
-
|
|
370
|
-
```typescript
|
|
371
|
-
displayedColumns: [
|
|
253
|
+
},
|
|
372
254
|
{
|
|
373
255
|
property: 'actions',
|
|
374
|
-
title: '
|
|
256
|
+
title: 'Actions',
|
|
375
257
|
iconClass: [
|
|
376
258
|
{
|
|
377
259
|
class: 'fa fa-edit text-blue-500 cursor-pointer',
|
|
378
|
-
buttonMethod: (row
|
|
379
|
-
event.stopPropagation();
|
|
380
|
-
this.editItem(row);
|
|
381
|
-
},
|
|
382
|
-
condition: (row) => row.canEdit === true
|
|
260
|
+
buttonMethod: (row) => this.editProduct(row)
|
|
383
261
|
},
|
|
384
262
|
{
|
|
385
|
-
class: 'fa fa-trash text-red-500 cursor-pointer
|
|
386
|
-
buttonMethod: (row
|
|
387
|
-
|
|
388
|
-
this.deleteItem(row);
|
|
389
|
-
}
|
|
263
|
+
class: 'fa fa-trash text-red-500 cursor-pointer',
|
|
264
|
+
buttonMethod: (row) => this.deleteProduct(row),
|
|
265
|
+
condition: (row) => row.canDelete === true
|
|
390
266
|
}
|
|
391
267
|
]
|
|
392
268
|
}
|
|
393
|
-
]
|
|
269
|
+
];
|
|
394
270
|
```
|
|
395
271
|
|
|
396
|
-
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### Tab & TabData
|
|
275
|
+
|
|
276
|
+
Configure tabbed views for switching between different data sets within the same table.
|
|
397
277
|
|
|
278
|
+
**Tab Interface:**
|
|
279
|
+
|
|
280
|
+
| Property | Type | Required | Description |
|
|
281
|
+
|----------|------|----------|-------------|
|
|
282
|
+
| `method` | `(tab: any, event?: any) => any` | ✅ | Function called when a tab is clicked. Use to switch data or apply filters. |
|
|
283
|
+
| `tabsData` | `TabData[]` | ✅ | Array of tab configurations. |
|
|
284
|
+
|
|
285
|
+
**TabData Interface:**
|
|
286
|
+
|
|
287
|
+
| Property | Type | Required | Description |
|
|
288
|
+
|----------|------|----------|-------------|
|
|
289
|
+
| `label` | `string` | ✅ | Text displayed on the tab. |
|
|
290
|
+
| `counter` | `number` | ❌ | Optional counter badge displayed next to the label. |
|
|
291
|
+
| `counterClass` | `string` | ❌ | CSS class for styling the counter badge. |
|
|
292
|
+
|
|
293
|
+
**Example:**
|
|
398
294
|
```typescript
|
|
399
|
-
|
|
400
|
-
// ...
|
|
401
|
-
|
|
402
|
-
{
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
295
|
+
const tableData: TableData = {
|
|
296
|
+
// ... other config
|
|
297
|
+
tabs: {
|
|
298
|
+
method: (tab, event) => {
|
|
299
|
+
if (tab.label === 'Active') {
|
|
300
|
+
this.loadActiveProducts();
|
|
301
|
+
} else if (tab.label === 'Inactive') {
|
|
302
|
+
this.loadInactiveProducts();
|
|
303
|
+
}
|
|
406
304
|
},
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
305
|
+
tabsData: [
|
|
306
|
+
{ label: 'Active', counter: 42, counterClass: 'bg-green-500' },
|
|
307
|
+
{ label: 'Inactive', counter: 8, counterClass: 'bg-gray-500' },
|
|
308
|
+
{ label: 'All' }
|
|
309
|
+
]
|
|
310
|
+
}
|
|
413
311
|
};
|
|
414
312
|
```
|
|
415
313
|
|
|
416
|
-
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### FilterableOption
|
|
317
|
+
|
|
318
|
+
Predefined filter buttons that users can click to apply common filters.
|
|
319
|
+
|
|
320
|
+
| Property | Type | Required | Description |
|
|
321
|
+
|----------|------|----------|-------------|
|
|
322
|
+
| `title` | `string` | ✅ | Label for the filter group. |
|
|
323
|
+
| `items` | `FilterItem[]` | ✅ | Array of individual filter options. |
|
|
324
|
+
|
|
325
|
+
**FilterItem Properties:**
|
|
326
|
+
- `property` (string): Property to filter on
|
|
327
|
+
- `value` (string \| boolean): Value to filter by
|
|
328
|
+
- `label` (string): Text displayed on the filter button
|
|
329
|
+
|
|
330
|
+
**Example:**
|
|
331
|
+
```typescript
|
|
332
|
+
const filterableOptions: FilterableOption[] = [
|
|
333
|
+
{
|
|
334
|
+
title: 'Status',
|
|
335
|
+
items: [
|
|
336
|
+
{ property: 'status', value: 'active', label: 'Active' },
|
|
337
|
+
{ property: 'status', value: 'pending', label: 'Pending' },
|
|
338
|
+
{ property: 'status', value: 'archived', label: 'Archived' }
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
title: 'Category',
|
|
343
|
+
items: [
|
|
344
|
+
{ property: 'category', value: 'electronics', label: 'Electronics' },
|
|
345
|
+
{ property: 'category', value: 'clothing', label: 'Clothing' }
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
];
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
### Condition
|
|
354
|
+
|
|
355
|
+
Firestore `where` clauses that are always applied to queries, before any user filters.
|
|
356
|
+
|
|
357
|
+
| Property | Type | Required | Description |
|
|
358
|
+
|----------|------|----------|-------------|
|
|
359
|
+
| `operator` | `WhereFilterOp` | ✅ | Firestore comparison operator: `'=='`, `'!='`, `'<'`, `'<='`, `'>'`, `'>='`, `'array-contains'`, `'in'`, `'array-contains-any'`, `'not-in'`. |
|
|
360
|
+
| `firestoreProperty` | `string` | ✅ | Name of the field in Firestore to filter on. |
|
|
361
|
+
| `dashProperty` | `string \| string[]` | ✅ | Value or array of values to compare against. Can be a static value or a property name from your component. |
|
|
362
|
+
|
|
363
|
+
**Example:**
|
|
364
|
+
```typescript
|
|
365
|
+
const conditions: Condition[] = [
|
|
366
|
+
{
|
|
367
|
+
operator: '==',
|
|
368
|
+
firestoreProperty: 'tenantId',
|
|
369
|
+
dashProperty: this.currentTenantId
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
operator: '>=',
|
|
373
|
+
firestoreProperty: 'createdAt',
|
|
374
|
+
dashProperty: this.startDate
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
operator: 'in',
|
|
378
|
+
firestoreProperty: 'status',
|
|
379
|
+
dashProperty: ['active', 'pending']
|
|
380
|
+
}
|
|
381
|
+
];
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
### Arrange
|
|
387
|
+
|
|
388
|
+
Internal interface used for managing sort and filter state. Automatically handled by the table component.
|
|
389
|
+
|
|
390
|
+
| Property | Type | Description |
|
|
391
|
+
|----------|------|-------------|
|
|
392
|
+
| `filters` | `FilterState[]` | Array of active filters per column. |
|
|
393
|
+
| `sortBy` | `{field: string, order: OrderByDirection}` | Current sort configuration. |
|
|
394
|
+
| `elementId` | `{property: string, value: string}` | Optional identifier for highlighting a specific row. |
|
|
395
|
+
|
|
396
|
+
**FilterState Properties:**
|
|
397
|
+
- `arrange` ('ascending' \| 'descending' \| 'filter' \| 'filterByDate' \| 'equals' \| ''): Type of filter/sort applied
|
|
398
|
+
- `filter` ({property: string, filtering: string}): Text filter configuration
|
|
399
|
+
- `dateFilter` ({initial: Date, final: Date}): Date range filter
|
|
417
400
|
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
### Image
|
|
404
|
+
|
|
405
|
+
Configuration for displaying images in table cells.
|
|
406
|
+
|
|
407
|
+
| Property | Type | Required | Description |
|
|
408
|
+
|----------|------|----------|-------------|
|
|
409
|
+
| `class` | `string` | ✅ | CSS classes for styling the image element. |
|
|
410
|
+
| `path` | `string` | ❌ | Property name containing the image path/URL in the row data. |
|
|
411
|
+
| `url` | `boolean` | ❌ | If `true`, treats the value as a complete URL. Otherwise, constructs path from storage. |
|
|
412
|
+
| `default` | `string` | ❌ | Default image URL if the actual image is missing or fails to load. |
|
|
413
|
+
|
|
414
|
+
**Example:**
|
|
418
415
|
```typescript
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
416
|
+
const column: Column = {
|
|
417
|
+
property: 'avatar',
|
|
418
|
+
title: 'Photo',
|
|
419
|
+
image: {
|
|
420
|
+
class: 'w-10 h-10 rounded-full object-cover',
|
|
421
|
+
path: 'photoURL',
|
|
422
|
+
url: true,
|
|
423
|
+
default: '/assets/default-avatar.png'
|
|
424
424
|
}
|
|
425
425
|
};
|
|
426
426
|
```
|
|
427
427
|
|
|
428
|
-
|
|
428
|
+
---
|
|
429
429
|
|
|
430
|
-
|
|
430
|
+
### Data Export (Download)
|
|
431
431
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
432
|
+
The library provides a flexible export system that allows you to download table data with the currently applied filters, sorting, and conditions.
|
|
433
|
+
|
|
434
|
+
#### How It Works
|
|
435
|
+
|
|
436
|
+
1. **Pass a download handler** to the table component via the `[downloadTable]` input
|
|
437
|
+
2. **User clicks** the download button in the table UI
|
|
438
|
+
3. **Library calls your handler** with current `arrange` (filters/sort state) and `conditions` (where clauses)
|
|
439
|
+
4. **Your handler** fetches all data using `TableService.getItemsData()`, transforms it, and generates the file
|
|
440
|
+
|
|
441
|
+
#### Basic Example
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
import { TableService } from '@adryanmmm/firebase-table-kxp-lib';
|
|
445
|
+
import * as XLSX from 'xlsx';
|
|
446
|
+
import moment from 'moment';
|
|
447
|
+
|
|
448
|
+
export class UsersComponent {
|
|
449
|
+
constructor(private tableService: TableService) {}
|
|
450
|
+
|
|
451
|
+
// Pass this method to [downloadTable]
|
|
452
|
+
downloadTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
453
|
+
// 1. Fetch all data with current filters
|
|
454
|
+
const rawData = await this.tableService.getItemsData(
|
|
455
|
+
'users', // collection name
|
|
456
|
+
arrange, // current sort/filter state
|
|
457
|
+
conditions // optional where conditions
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
// 2. Transform data to desired format
|
|
461
|
+
const xlsxData = rawData.map(user => ({
|
|
462
|
+
'Name': user.fullName,
|
|
463
|
+
'Email': user.email,
|
|
464
|
+
'Phone': user.phoneNumber,
|
|
465
|
+
'Created': new Date(user.createdAt.seconds * 1000).toLocaleDateString()
|
|
466
|
+
}));
|
|
467
|
+
|
|
468
|
+
// 3. Generate Excel file
|
|
469
|
+
const worksheet = XLSX.utils.json_to_sheet(xlsxData);
|
|
470
|
+
const workbook = XLSX.utils.book_new();
|
|
471
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Users');
|
|
472
|
+
|
|
473
|
+
// 4. Download file
|
|
474
|
+
const fileName = `Users_${moment().format('DD-MM-YYYY-HH-mm-ss')}.xlsx`;
|
|
475
|
+
XLSX.writeFile(workbook, fileName);
|
|
476
|
+
}
|
|
436
477
|
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
#### Advanced Example with Totals
|
|
437
481
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
482
|
+
```typescript
|
|
483
|
+
downloadOrdersTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
484
|
+
const rawData = await this.tableService.getItemsData(
|
|
485
|
+
'orders',
|
|
486
|
+
arrange,
|
|
487
|
+
conditions
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
let totalAmount = 0;
|
|
491
|
+
|
|
492
|
+
// Map data and calculate totals
|
|
493
|
+
const xlsxData = rawData.map(order => {
|
|
494
|
+
totalAmount += order.amount;
|
|
495
|
+
return {
|
|
496
|
+
'Order ID': order.id,
|
|
497
|
+
'Customer': order.customerName,
|
|
498
|
+
'Amount': order.amount,
|
|
499
|
+
'Date': new Date(order.createdAt.seconds * 1000).toLocaleDateString(),
|
|
500
|
+
'Status': order.status
|
|
501
|
+
};
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Add totals row
|
|
505
|
+
xlsxData.push({
|
|
506
|
+
'Order ID': '',
|
|
507
|
+
'Customer': '',
|
|
508
|
+
'Amount': totalAmount,
|
|
509
|
+
'Date': 'TOTAL',
|
|
510
|
+
'Status': ''
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
const worksheet = XLSX.utils.json_to_sheet(xlsxData);
|
|
514
|
+
const workbook = XLSX.utils.book_new();
|
|
515
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Orders');
|
|
516
|
+
|
|
517
|
+
const fileName = `Orders_${moment().format('DD-MM-YYYY-HH-mm-ss')}.xlsx`;
|
|
518
|
+
XLSX.writeFile(workbook, fileName);
|
|
441
519
|
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
#### Filtering Data Before Export
|
|
523
|
+
|
|
524
|
+
You can apply additional client-side filters based on user permissions:
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
downloadTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
528
|
+
const rawData = await this.tableService.getItemsData('orders', arrange, conditions);
|
|
529
|
+
|
|
530
|
+
// Filter based on user permissions
|
|
531
|
+
const filteredData = rawData.filter(order => {
|
|
532
|
+
if (this.currentUser.role === 'Admin') return true;
|
|
533
|
+
if (this.currentUser.storeId === order.storeId) return true;
|
|
534
|
+
return false;
|
|
535
|
+
});
|
|
442
536
|
|
|
443
|
-
//
|
|
444
|
-
.mat-mdc-paginator {
|
|
445
|
-
background-color: #f9fafb;
|
|
537
|
+
// ... continue with export
|
|
446
538
|
}
|
|
447
539
|
```
|
|
448
540
|
|
|
449
|
-
|
|
541
|
+
#### Using Custom Pipes for Formatting
|
|
450
542
|
|
|
451
|
-
|
|
543
|
+
Apply Angular pipes to format data in the export:
|
|
452
544
|
|
|
453
|
-
|
|
545
|
+
```typescript
|
|
546
|
+
import { DatePipe, CurrencyPipe } from '@angular/common';
|
|
547
|
+
|
|
548
|
+
downloadTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
549
|
+
const rawData = await this.tableService.getItemsData('products', arrange);
|
|
550
|
+
|
|
551
|
+
const datePipe = new DatePipe('en-US');
|
|
552
|
+
const currencyPipe = new CurrencyPipe('en-US');
|
|
454
553
|
|
|
455
|
-
|
|
554
|
+
const xlsxData = rawData.map(product => ({
|
|
555
|
+
'Product': product.name,
|
|
556
|
+
'Price': currencyPipe.transform(product.price),
|
|
557
|
+
'Created': datePipe.transform(product.createdAt.toDate(), 'short'),
|
|
558
|
+
'Status': product.active ? 'Active' : 'Inactive'
|
|
559
|
+
}));
|
|
560
|
+
|
|
561
|
+
// ... continue with export
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
#### CSV vs XLSX Format
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
// Export as XLSX (default)
|
|
569
|
+
XLSX.writeFile(workbook, 'data.xlsx');
|
|
570
|
+
|
|
571
|
+
// Export as CSV
|
|
572
|
+
XLSX.writeFile(workbook, 'data.csv', { bookType: 'csv', type: 'binary' });
|
|
573
|
+
```
|
|
456
574
|
|
|
457
|
-
|
|
458
|
-
- Link direto para criar o índice no Firebase Console
|
|
459
|
-
- Instruções passo a passo
|
|
460
|
-
- Estrutura da query
|
|
575
|
+
#### Important Notes
|
|
461
576
|
|
|
462
|
-
|
|
577
|
+
- The `download` property in `TableData` must be `true` to show the download button
|
|
578
|
+
- `TableService.getItemsData()` returns **all** data matching filters (ignores pagination)
|
|
579
|
+
- The method is `async` because it fetches data from Firestore
|
|
580
|
+
- You control the final file format, column names, and data transformations
|
|
581
|
+
- The library provides the current filter/sort state; you handle the actual file generation
|
|
463
582
|
|
|
464
|
-
|
|
583
|
+
## Powered by KXP Tech
|
|
465
584
|
|
|
466
|
-
|
|
585
|
+
Firebase Table KXP was designed inside **KXP Tech** to accelerate data-heavy application development. If you are looking for expert consultancy on Firebase, Angular, or enterprise dashboards, reach out at [kxptech.com](https://kxptech.com).
|
|
467
586
|
|
|
468
|
-
|
|
587
|
+
## Contributing & License
|
|
469
588
|
|
|
470
|
-
|
|
589
|
+
Contributions, feedback, and feature requests are welcome. Fork the repository, open a pull request, or start a discussion on GitHub.
|
|
471
590
|
|
|
472
|
-
|
|
591
|
+
This project is released under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
473
592
|
|
|
474
|
-
##
|
|
593
|
+
## Support the Project
|
|
475
594
|
|
|
476
|
-
|
|
595
|
+
If this library saves you time, please star the repository and share it with your team.
|