@seniorsistemas/components-ai 0.0.0-master-d4a804fe

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.
Files changed (76) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/CONTRIBUTING.md +317 -0
  3. package/README.md +161 -0
  4. package/docs/API.md +486 -0
  5. package/docs/COMPONENTS.md +272 -0
  6. package/docs/EXAMPLES.md +559 -0
  7. package/docs/MIGRATION.md +367 -0
  8. package/esm2022/lib/angular-components.module.mjs +25 -0
  9. package/esm2022/lib/components/breadcrumb/breadcrumb.component.mjs +121 -0
  10. package/esm2022/lib/components/bulk-delete-dialog/bulk-delete-dialog.component.mjs +176 -0
  11. package/esm2022/lib/components/dynamic-form/dynamic-form.component.mjs +625 -0
  12. package/esm2022/lib/components/dynamic-form/fields/dynamic-field-date.component.mjs +86 -0
  13. package/esm2022/lib/components/dynamic-form/fields/dynamic-field-dropdown.component.mjs +103 -0
  14. package/esm2022/lib/components/dynamic-form/fields/dynamic-field-lookup.component.mjs +599 -0
  15. package/esm2022/lib/components/dynamic-form/fields/dynamic-field-number.component.mjs +92 -0
  16. package/esm2022/lib/components/dynamic-form/fields/dynamic-field-text.component.mjs +163 -0
  17. package/esm2022/lib/components/dynamic-form/fields/dynamic-field-textarea.component.mjs +81 -0
  18. package/esm2022/lib/components/dynamic-form/fields/dynamic-field-wrapper.component.mjs +93 -0
  19. package/esm2022/lib/components/dynamic-form/fields/index.mjs +8 -0
  20. package/esm2022/lib/components/dynamic-form/models/dynamic-form.models.mjs +2 -0
  21. package/esm2022/lib/components/export-dialog/export-dialog.component.mjs +219 -0
  22. package/esm2022/lib/config/translation.config.mjs +70 -0
  23. package/esm2022/lib/directives/cep-mask.directive.mjs +79 -0
  24. package/esm2022/lib/directives/document-mask.directive.mjs +62 -0
  25. package/esm2022/lib/directives/phone-mask.directive.mjs +59 -0
  26. package/esm2022/lib/interceptors/api.interceptor.mjs +55 -0
  27. package/esm2022/lib/models/base-entity.interface.mjs +2 -0
  28. package/esm2022/lib/models/entity-list.config.mjs +2 -0
  29. package/esm2022/lib/pipes/translate.pipe.mjs +74 -0
  30. package/esm2022/lib/services/auth.service.mjs +169 -0
  31. package/esm2022/lib/services/cookie.service.mjs +90 -0
  32. package/esm2022/lib/services/entity.service.mjs +208 -0
  33. package/esm2022/lib/services/mask.service.mjs +121 -0
  34. package/esm2022/lib/services/permission.service.mjs +180 -0
  35. package/esm2022/lib/services/senior-token.service.mjs +209 -0
  36. package/esm2022/lib/services/theme.service.mjs +85 -0
  37. package/esm2022/lib/services/translation.service.mjs +232 -0
  38. package/esm2022/public-api.mjs +90 -0
  39. package/esm2022/seniorsistemas-components-ai.mjs +5 -0
  40. package/fesm2022/seniorsistemas-components-ai.mjs +4006 -0
  41. package/fesm2022/seniorsistemas-components-ai.mjs.map +1 -0
  42. package/index.d.ts +5 -0
  43. package/lib/angular-components.module.d.ts +13 -0
  44. package/lib/components/breadcrumb/breadcrumb.component.d.ts +23 -0
  45. package/lib/components/bulk-delete-dialog/bulk-delete-dialog.component.d.ts +46 -0
  46. package/lib/components/dynamic-form/dynamic-form.component.d.ts +97 -0
  47. package/lib/components/dynamic-form/fields/dynamic-field-date.component.d.ts +16 -0
  48. package/lib/components/dynamic-form/fields/dynamic-field-dropdown.component.d.ts +17 -0
  49. package/lib/components/dynamic-form/fields/dynamic-field-lookup.component.d.ts +52 -0
  50. package/lib/components/dynamic-form/fields/dynamic-field-number.component.d.ts +16 -0
  51. package/lib/components/dynamic-form/fields/dynamic-field-text.component.d.ts +17 -0
  52. package/lib/components/dynamic-form/fields/dynamic-field-textarea.component.d.ts +16 -0
  53. package/lib/components/dynamic-form/fields/dynamic-field-wrapper.component.d.ts +20 -0
  54. package/lib/components/dynamic-form/fields/index.d.ts +7 -0
  55. package/lib/components/dynamic-form/models/dynamic-form.models.d.ts +178 -0
  56. package/lib/components/export-dialog/export-dialog.component.d.ts +56 -0
  57. package/lib/config/translation.config.d.ts +24 -0
  58. package/lib/directives/cep-mask.directive.d.ts +13 -0
  59. package/lib/directives/document-mask.directive.d.ts +19 -0
  60. package/lib/directives/phone-mask.directive.d.ts +11 -0
  61. package/lib/interceptors/api.interceptor.d.ts +2 -0
  62. package/lib/models/base-entity.interface.d.ts +7 -0
  63. package/lib/models/entity-list.config.d.ts +161 -0
  64. package/lib/pipes/translate.pipe.d.ts +21 -0
  65. package/lib/services/auth.service.d.ts +82 -0
  66. package/lib/services/cookie.service.d.ts +31 -0
  67. package/lib/services/entity.service.d.ts +99 -0
  68. package/lib/services/mask.service.d.ts +36 -0
  69. package/lib/services/permission.service.d.ts +91 -0
  70. package/lib/services/senior-token.service.d.ts +70 -0
  71. package/lib/services/theme.service.d.ts +16 -0
  72. package/lib/services/translation.service.d.ts +54 -0
  73. package/package.json +53 -0
  74. package/public-api.d.ts +17 -0
  75. package/src/lib/styles/entity-list.shared.scss +383 -0
  76. package/src/lib/styles/index.scss +10 -0
@@ -0,0 +1,4006 @@
1
+ import * as i0 from '@angular/core';
2
+ import { NgModule, Injectable, Optional, Inject, EventEmitter, Component, Input, Output, Pipe, Directive, HostListener, ViewEncapsulation, inject } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule, DOCUMENT } from '@angular/common';
5
+ import * as i1 from '@angular/common/http';
6
+ import { HttpParams } from '@angular/common/http';
7
+ import { throwError, BehaviorSubject, of, from, concatMap, catchError as catchError$1 } from 'rxjs';
8
+ import { catchError, map, filter } from 'rxjs/operators';
9
+ import * as i2$1 from '@angular/forms';
10
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
11
+ import * as i3 from 'primeng/dialog';
12
+ import { DialogModule } from 'primeng/dialog';
13
+ import * as i5$1 from 'primeng/button';
14
+ import { ButtonModule } from 'primeng/button';
15
+ import * as i6 from 'primeng/radiobutton';
16
+ import { RadioButtonModule } from 'primeng/radiobutton';
17
+ import * as i7 from 'primeng/checkbox';
18
+ import { CheckboxModule } from 'primeng/checkbox';
19
+ import { DividerModule } from 'primeng/divider';
20
+ import * as XLSX from 'xlsx';
21
+ import { jsPDF } from 'jspdf';
22
+ import autoTable from 'jspdf-autotable';
23
+ import * as i5 from 'primeng/api';
24
+ import * as i4 from 'primeng/progressbar';
25
+ import { ProgressBarModule } from 'primeng/progressbar';
26
+ import * as i5$2 from 'primeng/tag';
27
+ import { TagModule } from 'primeng/tag';
28
+ import * as i1$1 from '@angular/router';
29
+ import { NavigationEnd, RouterModule, Router } from '@angular/router';
30
+ import * as i2$2 from 'primeng/breadcrumb';
31
+ import { BreadcrumbModule } from 'primeng/breadcrumb';
32
+ import * as i4$2 from 'primeng/panel';
33
+ import { PanelModule } from 'primeng/panel';
34
+ import * as i7$1 from 'primeng/drawer';
35
+ import { DrawerModule } from 'primeng/drawer';
36
+ import * as i4$1 from 'primeng/inputtext';
37
+ import { InputTextModule } from 'primeng/inputtext';
38
+ import * as i3$1 from 'primeng/inputnumber';
39
+ import { InputNumberModule } from 'primeng/inputnumber';
40
+ import * as i3$2 from 'primeng/calendar';
41
+ import { CalendarModule } from 'primeng/calendar';
42
+ import * as i3$3 from 'primeng/dropdown';
43
+ import { DropdownModule } from 'primeng/dropdown';
44
+ import * as i8 from 'primeng/table';
45
+ import { TableModule } from 'primeng/table';
46
+ import * as i9 from 'primeng/tooltip';
47
+ import { TooltipModule } from 'primeng/tooltip';
48
+ import { InputTextarea } from 'primeng/inputtextarea';
49
+
50
+ /**
51
+ * Módulo principal da biblioteca @seniorsistemas/components-ia
52
+ *
53
+ * Este módulo será populado conforme os componentes forem migrados.
54
+ * Por enquanto, é um módulo vazio para permitir o build inicial.
55
+ */
56
+ class AngularComponentsModule {
57
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AngularComponentsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
58
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: AngularComponentsModule, imports: [CommonModule] });
59
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AngularComponentsModule, imports: [CommonModule] });
60
+ }
61
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AngularComponentsModule, decorators: [{
62
+ type: NgModule,
63
+ args: [{
64
+ declarations: [],
65
+ imports: [
66
+ CommonModule
67
+ ],
68
+ exports: []
69
+ }]
70
+ }] });
71
+
72
+ class EntityService {
73
+ http;
74
+ translationService;
75
+ queriesUrl = '';
76
+ constructor(http, translationService) {
77
+ this.http = http;
78
+ this.translationService = translationService;
79
+ // Initialize queriesUrl in ngOnInit or a separate method
80
+ // since abstract properties can't be accessed in constructor
81
+ }
82
+ /**
83
+ * Initialize the service - call this after setting actionsUrl
84
+ */
85
+ initializeService() {
86
+ this.queriesUrl = this.actionsUrl.replace('actions', 'queries');
87
+ }
88
+ /**
89
+ * Convert ListParams to HttpParams for GET requests
90
+ */
91
+ getListQueryParams(listParams) {
92
+ const { page = 0, size = 10, sort = [], filterQuery = '', displayFields = [] } = listParams;
93
+ let params = new HttpParams();
94
+ params = params.append('size', String(size));
95
+ params = params.append('offset', String(page));
96
+ if (sort && sort.length) {
97
+ const orderBy = sort
98
+ .map((s) => {
99
+ let order = '';
100
+ if (s.order === 1)
101
+ order = ' asc';
102
+ else if (s.order === -1)
103
+ order = ' desc';
104
+ return `${s.field}${order}`;
105
+ })
106
+ .join(', ');
107
+ params = params.append('orderby', orderBy);
108
+ }
109
+ if (filterQuery) {
110
+ params = params.append('filter', filterQuery);
111
+ }
112
+ if (displayFields && displayFields.length) {
113
+ params = params.append('displayfields', displayFields.join(','));
114
+ }
115
+ return params;
116
+ }
117
+ /**
118
+ * Convert ListParams to body parameters for POST requests
119
+ */
120
+ getBodyParams(listParams) {
121
+ const { page = 0, size = 10, sort = [], filterQuery = '', displayFields = [] } = listParams;
122
+ const bodyParams = {};
123
+ bodyParams.size = size;
124
+ bodyParams.offset = page;
125
+ if (sort && sort.length) {
126
+ bodyParams.orderBy = sort
127
+ .map((s) => {
128
+ let order = '';
129
+ if (s.order === 1)
130
+ order = ' asc';
131
+ else if (s.order === -1)
132
+ order = ' desc';
133
+ return `${s.field}${order}`;
134
+ })
135
+ .join(', ');
136
+ }
137
+ if (filterQuery) {
138
+ bodyParams.filter = filterQuery;
139
+ }
140
+ if (displayFields && displayFields.length) {
141
+ bodyParams.displayfields = displayFields.join(',');
142
+ }
143
+ return bodyParams;
144
+ }
145
+ /**
146
+ * Default error handler
147
+ */
148
+ handleError = (error) => {
149
+ let errorMessage = 'An error occurred';
150
+ if (this.translationService) {
151
+ errorMessage = this.translationService.translate('error.generic');
152
+ }
153
+ if (error.status) {
154
+ switch (error.status) {
155
+ case 401:
156
+ errorMessage = this.translationService
157
+ ? this.translationService.translate('error.unauthorized')
158
+ : 'Unauthorized';
159
+ break;
160
+ case 403:
161
+ errorMessage = this.translationService
162
+ ? this.translationService.translate('error.forbidden')
163
+ : 'Forbidden';
164
+ break;
165
+ case 404:
166
+ errorMessage = this.translationService
167
+ ? this.translationService.translate('error.not_found')
168
+ : 'Not found';
169
+ break;
170
+ case 500:
171
+ errorMessage = this.translationService
172
+ ? this.translationService.translate('error.server_error')
173
+ : 'Server error';
174
+ break;
175
+ default:
176
+ if (error.error && error.error.message) {
177
+ errorMessage = error.error.message;
178
+ }
179
+ else if (error.statusText) {
180
+ errorMessage = error.statusText;
181
+ }
182
+ }
183
+ }
184
+ else if (error.message) {
185
+ errorMessage = error.message;
186
+ }
187
+ console.error('EntityService Error:', error);
188
+ return throwError(() => new Error(errorMessage));
189
+ };
190
+ /**
191
+ * List entities with pagination, sorting, and filtering
192
+ */
193
+ list(listParams = {}) {
194
+ const params = this.getListQueryParams(listParams);
195
+ return this.http.get(this.entityUrl, { params })
196
+ .pipe(catchError(this.handleError));
197
+ }
198
+ /**
199
+ * Get entity by ID
200
+ */
201
+ get(id) {
202
+ return this.http.get(`${this.entityUrl}/${id}`)
203
+ .pipe(catchError(this.handleError));
204
+ }
205
+ /**
206
+ * Create new entity
207
+ */
208
+ insert(entity) {
209
+ return this.http.post(this.entityUrl, entity)
210
+ .pipe(catchError(this.handleError));
211
+ }
212
+ /**
213
+ * Update existing entity
214
+ */
215
+ update(id, entity) {
216
+ return this.http.put(`${this.entityUrl}/${id}`, entity)
217
+ .pipe(catchError(this.handleError));
218
+ }
219
+ /**
220
+ * Delete entity by ID
221
+ */
222
+ delete(id) {
223
+ return this.http.delete(`${this.entityUrl}/${id}`, { observe: 'response' })
224
+ .pipe(map(() => undefined), catchError(this.handleError));
225
+ }
226
+ /**
227
+ * Execute custom filter action
228
+ */
229
+ listCustomFilter(listParams, action) {
230
+ const bodyParams = this.getBodyParams(listParams);
231
+ return this.http.post(`${this.actionsUrl}/${action}`, bodyParams)
232
+ .pipe(catchError(this.handleError));
233
+ }
234
+ /**
235
+ * Execute custom query
236
+ */
237
+ executeQuery(queryName, params = {}) {
238
+ return this.http.post(`${this.queriesUrl}/${queryName}`, params)
239
+ .pipe(catchError(this.handleError));
240
+ }
241
+ /**
242
+ * Execute custom action
243
+ */
244
+ executeAction(actionName, params = {}) {
245
+ return this.http.post(`${this.actionsUrl}/${actionName}`, params)
246
+ .pipe(catchError(this.handleError));
247
+ }
248
+ /**
249
+ * Check if entity exists by ID
250
+ */
251
+ exists(id) {
252
+ return this.http.head(`${this.entityUrl}/${id}`)
253
+ .pipe(map(() => true), catchError((error) => {
254
+ if (error.status === 404) {
255
+ return [false];
256
+ }
257
+ return this.handleError(error);
258
+ }));
259
+ }
260
+ /**
261
+ * Bulk delete entities
262
+ */
263
+ bulkDelete(ids) {
264
+ return this.http.post(`${this.actionsUrl}/bulkDelete`, { ids }, { observe: 'response' })
265
+ .pipe(map(() => undefined), catchError(this.handleError));
266
+ }
267
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EntityService, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
268
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EntityService });
269
+ }
270
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EntityService, decorators: [{
271
+ type: Injectable
272
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined }] });
273
+
274
+ class CookieService {
275
+ /**
276
+ * Obtém o valor de um cookie pelo nome
277
+ */
278
+ getCookie(name) {
279
+ if (typeof document === 'undefined') {
280
+ return null; // SSR protection
281
+ }
282
+ const nameEQ = name + '=';
283
+ const cookies = document.cookie.split(';');
284
+ for (let cookie of cookies) {
285
+ cookie = cookie.trim();
286
+ if (cookie.indexOf(nameEQ) === 0) {
287
+ const value = decodeURIComponent(cookie.substring(nameEQ.length));
288
+ return value;
289
+ }
290
+ }
291
+ return null;
292
+ }
293
+ /**
294
+ * Define um cookie
295
+ */
296
+ setCookie(name, value, days, path = '/') {
297
+ if (typeof document === 'undefined') {
298
+ return; // SSR protection
299
+ }
300
+ let expires = '';
301
+ if (days) {
302
+ const date = new Date();
303
+ date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
304
+ expires = '; expires=' + date.toUTCString();
305
+ }
306
+ document.cookie = `${name}=${encodeURIComponent(value)}${expires}; path=${path}`;
307
+ }
308
+ /**
309
+ * Remove um cookie
310
+ */
311
+ deleteCookie(name, path = '/') {
312
+ this.setCookie(name, '', -1, path);
313
+ }
314
+ /**
315
+ * Verifica se um cookie existe
316
+ */
317
+ hasCookie(name) {
318
+ return this.getCookie(name) !== null;
319
+ }
320
+ /**
321
+ * Obtém todos os cookies como objeto
322
+ */
323
+ getAllCookies() {
324
+ if (typeof document === 'undefined') {
325
+ return {}; // SSR protection
326
+ }
327
+ const cookies = {};
328
+ const cookieArray = document.cookie.split(';');
329
+ for (let cookie of cookieArray) {
330
+ cookie = cookie.trim();
331
+ const [name, value] = cookie.split('=');
332
+ if (name && value) {
333
+ cookies[name] = decodeURIComponent(value);
334
+ }
335
+ }
336
+ return cookies;
337
+ }
338
+ /**
339
+ * Debug: Lista todos os cookies disponíveis
340
+ */
341
+ debugAllCookies() {
342
+ console.log('[CookieService] All available cookies:');
343
+ const allCookies = this.getAllCookies();
344
+ Object.keys(allCookies).forEach(name => {
345
+ const value = allCookies[name];
346
+ console.log(`[CookieService] ${name}: ${value.length > 100 ? value.substring(0, 100) + '...' : value}`);
347
+ });
348
+ if (Object.keys(allCookies).length === 0) {
349
+ console.log('[CookieService] No cookies found');
350
+ }
351
+ }
352
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CookieService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
353
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CookieService, providedIn: 'root' });
354
+ }
355
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CookieService, decorators: [{
356
+ type: Injectable,
357
+ args: [{
358
+ providedIn: 'root'
359
+ }]
360
+ }] });
361
+
362
+ class AuthService {
363
+ cookieService;
364
+ TOKEN_COOKIE_NAME = 'com.senior.token';
365
+ API_URL_COOKIE_NAME = 'com.senior.base.url';
366
+ API_PATH_SUFFIX = '/t/senior.com.br/bridge/1.0/rest';
367
+ currentUserSubject = new BehaviorSubject(null);
368
+ currentUser$ = this.currentUserSubject.asObservable();
369
+ constructor(cookieService) {
370
+ this.cookieService = cookieService;
371
+ this.loadUserFromCookie();
372
+ }
373
+ /**
374
+ * Load user token from cookie
375
+ */
376
+ loadUserFromCookie() {
377
+ const tokenString = this.cookieService.getCookie(this.TOKEN_COOKIE_NAME);
378
+ if (tokenString) {
379
+ try {
380
+ const token = JSON.parse(tokenString);
381
+ if (this.isTokenValid(token)) {
382
+ this.currentUserSubject.next(token);
383
+ }
384
+ else {
385
+ this.currentUserSubject.next(null);
386
+ }
387
+ }
388
+ catch (error) {
389
+ console.error('[AuthService] Error parsing token from cookie:', error);
390
+ this.currentUserSubject.next(null);
391
+ }
392
+ }
393
+ }
394
+ /**
395
+ * Get current user token
396
+ */
397
+ getCurrentUser() {
398
+ return this.currentUserSubject.value;
399
+ }
400
+ /**
401
+ * Get access token
402
+ */
403
+ getAccessToken() {
404
+ const user = this.getCurrentUser();
405
+ return user?.access_token || null;
406
+ }
407
+ /**
408
+ * Get token type (e.g., "Bearer")
409
+ */
410
+ getTokenType() {
411
+ const user = this.getCurrentUser();
412
+ return user?.token_type || 'Bearer';
413
+ }
414
+ /**
415
+ * Get refresh token
416
+ */
417
+ getRefreshToken() {
418
+ const user = this.getCurrentUser();
419
+ return user?.refresh_token || null;
420
+ }
421
+ /**
422
+ * Get API base URL from cookie
423
+ */
424
+ getApiBaseUrl() {
425
+ const baseUrl = this.cookieService.getCookie(this.API_URL_COOKIE_NAME);
426
+ // In development, use proxy
427
+ if (!baseUrl) {
428
+ // Check if we're in development mode
429
+ const isDevelopment = !window.location.hostname.includes('senior.com.br');
430
+ if (isDevelopment) {
431
+ return '/api';
432
+ }
433
+ return null;
434
+ }
435
+ // Remove trailing slash from base URL
436
+ const cleanBaseUrl = baseUrl.replace(/\/$/, '');
437
+ // Combine with API path suffix
438
+ return `${cleanBaseUrl}${this.API_PATH_SUFFIX}`;
439
+ }
440
+ /**
441
+ * Check if user is authenticated
442
+ */
443
+ isAuthenticated() {
444
+ const user = this.getCurrentUser();
445
+ return user !== null && this.isTokenValid(user);
446
+ }
447
+ /**
448
+ * Check if token is valid (not expired)
449
+ */
450
+ isTokenValid(token) {
451
+ if (!token || !token.access_token) {
452
+ return false;
453
+ }
454
+ // Check if token is expired
455
+ const now = Math.floor(Date.now() / 1000);
456
+ const tokenExpiry = token.expires_in;
457
+ // If expires_in is a timestamp, compare directly
458
+ // If it's a duration, we'd need the token creation time
459
+ // For now, assume it's a timestamp
460
+ // If expires_in is very large (like a duration in seconds), skip expiry check
461
+ if (tokenExpiry > 1000000000) {
462
+ // Looks like a timestamp
463
+ return tokenExpiry > now;
464
+ }
465
+ else {
466
+ // Looks like a duration, assume token is valid
467
+ return true;
468
+ }
469
+ }
470
+ /**
471
+ * Check if refresh token is valid
472
+ */
473
+ isRefreshTokenValid() {
474
+ const user = this.getCurrentUser();
475
+ if (!user || !user.refresh_token) {
476
+ return false;
477
+ }
478
+ const now = Math.floor(Date.now() / 1000);
479
+ const refreshExpiry = user.refresh_expires_in;
480
+ return refreshExpiry > now;
481
+ }
482
+ /**
483
+ * Get user locale
484
+ */
485
+ getUserLocale() {
486
+ const user = this.getCurrentUser();
487
+ return user?.locale || 'pt-BR';
488
+ }
489
+ /**
490
+ * Get user full name
491
+ */
492
+ getUserFullName() {
493
+ const user = this.getCurrentUser();
494
+ return user?.fullName || '';
495
+ }
496
+ /**
497
+ * Get user email
498
+ */
499
+ getUserEmail() {
500
+ const user = this.getCurrentUser();
501
+ return user?.email || '';
502
+ }
503
+ /**
504
+ * Get tenant name
505
+ */
506
+ getTenantName() {
507
+ const user = this.getCurrentUser();
508
+ return user?.tenantName || '';
509
+ }
510
+ /**
511
+ * Get username
512
+ */
513
+ getUsername() {
514
+ const user = this.getCurrentUser();
515
+ return user?.username || '';
516
+ }
517
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthService, deps: [{ token: CookieService }], target: i0.ɵɵFactoryTarget.Injectable });
518
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthService, providedIn: 'root' });
519
+ }
520
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthService, decorators: [{
521
+ type: Injectable,
522
+ args: [{
523
+ providedIn: 'root'
524
+ }]
525
+ }], ctorParameters: () => [{ type: CookieService }] });
526
+
527
+ class PermissionService {
528
+ http;
529
+ authService;
530
+ PERMISSION_ENDPOINT = 'platform/authorization/queries/checkAccess';
531
+ permissionCache = new Map();
532
+ cacheExpiry = new Map();
533
+ CACHE_DURATION = 5 * 60 * 1000; // 5 minutes in milliseconds
534
+ constructor(http, authService) {
535
+ this.http = http;
536
+ this.authService = authService;
537
+ }
538
+ /**
539
+ * Check access for a single permission
540
+ */
541
+ checkAccess(resource, action, context) {
542
+ const cacheKey = this.getCacheKey(resource, action, context);
543
+ // Check cache first
544
+ const cachedResult = this.getCachedPermission(cacheKey);
545
+ if (cachedResult !== null) {
546
+ return of(cachedResult.allowed);
547
+ }
548
+ // Make API request
549
+ const request = {
550
+ permissions: [{ resource, action, context }]
551
+ };
552
+ return this.http.post(this.PERMISSION_ENDPOINT, request).pipe(map(response => {
553
+ if (response.results && response.results.length > 0) {
554
+ const result = response.results[0];
555
+ this.cachePermission(cacheKey, result);
556
+ return result.allowed;
557
+ }
558
+ return false;
559
+ }), catchError(error => {
560
+ console.error('Permission check failed:', error);
561
+ // On error, deny access by default
562
+ return of(false);
563
+ }));
564
+ }
565
+ /**
566
+ * Check access for multiple permissions
567
+ */
568
+ checkMultipleAccess(permissions) {
569
+ const uncachedPermissions = [];
570
+ const results = [];
571
+ // Check cache for each permission
572
+ permissions.forEach(permission => {
573
+ const cacheKey = this.getCacheKey(permission.resource, permission.action, permission.context);
574
+ const cachedResult = this.getCachedPermission(cacheKey);
575
+ if (cachedResult !== null) {
576
+ results.push(cachedResult);
577
+ }
578
+ else {
579
+ uncachedPermissions.push(permission);
580
+ }
581
+ });
582
+ // If all permissions are cached, return immediately
583
+ if (uncachedPermissions.length === 0) {
584
+ return of(results);
585
+ }
586
+ // Make API request for uncached permissions
587
+ const request = {
588
+ permissions: uncachedPermissions
589
+ };
590
+ return this.http.post(this.PERMISSION_ENDPOINT, request).pipe(map(response => {
591
+ if (response.results) {
592
+ // Cache the new results
593
+ response.results.forEach(result => {
594
+ const cacheKey = this.getCacheKey(result.resource, result.action);
595
+ this.cachePermission(cacheKey, result);
596
+ });
597
+ // Combine cached and new results
598
+ return [...results, ...response.results];
599
+ }
600
+ return results;
601
+ }), catchError(error => {
602
+ console.error('Multiple permission check failed:', error);
603
+ // On error, return cached results and deny uncached ones
604
+ const deniedResults = uncachedPermissions.map(permission => ({
605
+ allowed: false,
606
+ resource: permission.resource,
607
+ action: permission.action,
608
+ reason: 'Permission check failed'
609
+ }));
610
+ return of([...results, ...deniedResults]);
611
+ }));
612
+ }
613
+ /**
614
+ * Check if user can read a resource
615
+ */
616
+ canRead(resource, context) {
617
+ return this.checkAccess(resource, 'read', context);
618
+ }
619
+ /**
620
+ * Check if user can write to a resource
621
+ */
622
+ canWrite(resource, context) {
623
+ return this.checkAccess(resource, 'write', context);
624
+ }
625
+ /**
626
+ * Check if user can delete a resource
627
+ */
628
+ canDelete(resource, context) {
629
+ return this.checkAccess(resource, 'delete', context);
630
+ }
631
+ /**
632
+ * Check if user can execute an action on a resource
633
+ */
634
+ canExecute(resource, action, context) {
635
+ return this.checkAccess(resource, action, context);
636
+ }
637
+ /**
638
+ * Clear permission cache
639
+ */
640
+ clearCache() {
641
+ this.permissionCache.clear();
642
+ this.cacheExpiry.clear();
643
+ }
644
+ /**
645
+ * Clear expired cache entries
646
+ */
647
+ clearExpiredCache() {
648
+ const now = Date.now();
649
+ const expiredKeys = [];
650
+ this.cacheExpiry.forEach((expiry, key) => {
651
+ if (expiry < now) {
652
+ expiredKeys.push(key);
653
+ }
654
+ });
655
+ expiredKeys.forEach(key => {
656
+ this.permissionCache.delete(key);
657
+ this.cacheExpiry.delete(key);
658
+ });
659
+ }
660
+ /**
661
+ * Generate cache key for permission
662
+ */
663
+ getCacheKey(resource, action, context) {
664
+ const contextStr = context ? JSON.stringify(context) : '';
665
+ return `${resource}:${action}:${contextStr}`;
666
+ }
667
+ /**
668
+ * Get cached permission result
669
+ */
670
+ getCachedPermission(cacheKey) {
671
+ const expiry = this.cacheExpiry.get(cacheKey);
672
+ if (expiry && expiry > Date.now()) {
673
+ return this.permissionCache.get(cacheKey) || null;
674
+ }
675
+ // Remove expired entry
676
+ this.permissionCache.delete(cacheKey);
677
+ this.cacheExpiry.delete(cacheKey);
678
+ return null;
679
+ }
680
+ /**
681
+ * Cache permission result
682
+ */
683
+ cachePermission(cacheKey, result) {
684
+ this.permissionCache.set(cacheKey, result);
685
+ this.cacheExpiry.set(cacheKey, Date.now() + this.CACHE_DURATION);
686
+ }
687
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionService, deps: [{ token: i1.HttpClient }, { token: 'AuthService', optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
688
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionService, providedIn: 'root' });
689
+ }
690
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionService, decorators: [{
691
+ type: Injectable,
692
+ args: [{
693
+ providedIn: 'root'
694
+ }]
695
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
696
+ type: Optional
697
+ }, {
698
+ type: Inject,
699
+ args: ['AuthService']
700
+ }] }] });
701
+
702
+ /**
703
+ * Serviço para gerenciar o token Senior e extrair informações específicas
704
+ * para o sistema de traduções.
705
+ *
706
+ * Nota: Este serviço foca especificamente na extração do locale e informações
707
+ * básicas do usuário. Para funcionalidades completas de autenticação,
708
+ * use o AuthService que tem uma interface mais robusta.
709
+ */
710
+ class SeniorTokenService {
711
+ cookieService;
712
+ SENIOR_TOKEN_COOKIE = 'com.senior.token';
713
+ constructor(cookieService) {
714
+ this.cookieService = cookieService;
715
+ }
716
+ /**
717
+ * Obtém o token Senior completo do cookie
718
+ */
719
+ getSeniorToken() {
720
+ // Tentar o nome padrão primeiro
721
+ let token = this.cookieService.getCookie(this.SENIOR_TOKEN_COOKIE);
722
+ if (!token) {
723
+ // Tentar nomes alternativos comuns
724
+ const alternativeNames = [
725
+ 'com.senior.token',
726
+ 'senior.token',
727
+ 'senior_token',
728
+ 'seniorToken',
729
+ 'token',
730
+ 'authToken',
731
+ 'auth_token'
732
+ ];
733
+ for (const name of alternativeNames) {
734
+ token = this.cookieService.getCookie(name);
735
+ if (token) {
736
+ break;
737
+ }
738
+ }
739
+ }
740
+ return token;
741
+ }
742
+ /**
743
+ * Decodifica e obtém os dados do token Senior
744
+ */
745
+ getSeniorTokenData() {
746
+ const token = this.getSeniorToken();
747
+ if (!token) {
748
+ return null;
749
+ }
750
+ try {
751
+ let tokenData;
752
+ // Primeiro, tentar como JSON direto (mais provável baseado na informação fornecida)
753
+ try {
754
+ tokenData = JSON.parse(token);
755
+ }
756
+ catch (jsonError) {
757
+ // Se falhar, tentar como JWT
758
+ if (token.includes('.')) {
759
+ const parts = token.split('.');
760
+ if (parts.length >= 2) {
761
+ const payload = parts[1];
762
+ // Adicionar padding se necessário
763
+ const paddedPayload = payload + '='.repeat((4 - payload.length % 4) % 4);
764
+ const decodedPayload = atob(paddedPayload);
765
+ tokenData = JSON.parse(decodedPayload);
766
+ }
767
+ else {
768
+ throw new Error('Invalid JWT format');
769
+ }
770
+ }
771
+ else {
772
+ throw jsonError; // Re-throw original JSON error
773
+ }
774
+ }
775
+ return tokenData;
776
+ }
777
+ catch (error) {
778
+ console.error('[SeniorTokenService] Error parsing Senior token:', error);
779
+ return null;
780
+ }
781
+ }
782
+ /**
783
+ * Obtém o locale do usuário do token Senior
784
+ */
785
+ getUserLocale() {
786
+ const tokenData = this.getSeniorTokenData();
787
+ if (!tokenData) {
788
+ return null;
789
+ }
790
+ // O locale deve estar no mesmo nível do access_token
791
+ if (tokenData.locale) {
792
+ return tokenData.locale;
793
+ }
794
+ // Fallback: tentar outros campos comuns
795
+ const possibleLocaleFields = [
796
+ 'language',
797
+ 'lang',
798
+ 'user_locale',
799
+ 'userLocale',
800
+ 'preferred_language',
801
+ 'preferredLanguage'
802
+ ];
803
+ for (const field of possibleLocaleFields) {
804
+ if (tokenData[field]) {
805
+ return tokenData[field];
806
+ }
807
+ }
808
+ return null;
809
+ }
810
+ /**
811
+ * Obtém informações do usuário do token Senior
812
+ */
813
+ getUserInfo() {
814
+ const tokenData = this.getSeniorTokenData();
815
+ if (!tokenData) {
816
+ return null;
817
+ }
818
+ // Baseado na estrutura do AuthService, os campos devem estar no nível raiz
819
+ return {
820
+ userId: tokenData.userId || tokenData['sub'] || tokenData['user_id'] || tokenData.username,
821
+ userName: tokenData.userName || tokenData['name'] || tokenData['user_name'] || tokenData.username || tokenData.fullName,
822
+ locale: tokenData.locale,
823
+ email: tokenData.email,
824
+ fullName: tokenData.fullName
825
+ };
826
+ }
827
+ /**
828
+ * Verifica se o usuário está logado (token existe e é válido)
829
+ */
830
+ isUserLoggedIn() {
831
+ const tokenData = this.getSeniorTokenData();
832
+ if (!tokenData) {
833
+ return false;
834
+ }
835
+ // Verificar expiração se existir
836
+ if (tokenData['exp']) {
837
+ const now = Math.floor(Date.now() / 1000);
838
+ if (tokenData['exp'] < now) {
839
+ console.warn('Senior token has expired');
840
+ return false;
841
+ }
842
+ }
843
+ return true;
844
+ }
845
+ /**
846
+ * Monitora mudanças no cookie do token (para detectar login/logout)
847
+ */
848
+ watchTokenChanges(callback) {
849
+ let lastToken = this.getSeniorToken();
850
+ const checkToken = () => {
851
+ const currentToken = this.getSeniorToken();
852
+ if (currentToken !== lastToken) {
853
+ lastToken = currentToken;
854
+ const tokenData = this.getSeniorTokenData();
855
+ callback(tokenData);
856
+ }
857
+ };
858
+ // Verificar a cada 5 segundos
859
+ setInterval(checkToken, 5000);
860
+ }
861
+ /**
862
+ * Valida se o token tem a estrutura esperada do Senior
863
+ */
864
+ validateTokenStructure() {
865
+ const tokenData = this.getSeniorTokenData();
866
+ const issues = [];
867
+ if (!tokenData) {
868
+ issues.push('Token not found or could not be parsed');
869
+ return { isValid: false, issues };
870
+ }
871
+ // Verificar campos obrigatórios esperados
872
+ const requiredFields = ['access_token'];
873
+ const recommendedFields = ['locale', 'username', 'email'];
874
+ for (const field of requiredFields) {
875
+ if (!tokenData[field]) {
876
+ issues.push(`Missing required field: ${field}`);
877
+ }
878
+ }
879
+ for (const field of recommendedFields) {
880
+ if (!tokenData[field]) {
881
+ issues.push(`Missing recommended field: ${field}`);
882
+ }
883
+ }
884
+ // Verificar se locale está presente e é válido
885
+ if (tokenData.locale) {
886
+ // Importar a função de mapeamento seria circular, então fazer verificação básica
887
+ const supportedLocales = ['pt-BR', 'en-US', 'es-ES', 'pt', 'en', 'es'];
888
+ const locale = tokenData.locale;
889
+ const isSupported = supportedLocales.some(supportedLocale => locale.toLowerCase().includes(supportedLocale.toLowerCase()));
890
+ if (!isSupported) {
891
+ issues.push(`Locale '${locale}' might not be supported`);
892
+ }
893
+ }
894
+ const isValid = issues.filter(issue => issue.includes('required')).length === 0;
895
+ console.log('[SeniorTokenService] Token validation result:', { isValid, issues });
896
+ return { isValid, issues };
897
+ }
898
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SeniorTokenService, deps: [{ token: CookieService }], target: i0.ɵɵFactoryTarget.Injectable });
899
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SeniorTokenService, providedIn: 'root' });
900
+ }
901
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SeniorTokenService, decorators: [{
902
+ type: Injectable,
903
+ args: [{
904
+ providedIn: 'root'
905
+ }]
906
+ }], ctorParameters: () => [{ type: CookieService }] });
907
+
908
+ class ThemeService {
909
+ document;
910
+ isDarkModeSubject = new BehaviorSubject(false);
911
+ isDarkMode$ = this.isDarkModeSubject.asObservable();
912
+ mediaQuery;
913
+ constructor(document) {
914
+ this.document = document;
915
+ // Create media query to watch for system theme changes (kept for compatibility)
916
+ this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
917
+ // Check for saved theme preference, default to light theme
918
+ const savedTheme = localStorage.getItem('theme');
919
+ if (savedTheme === 'dark') {
920
+ // Only use dark theme if explicitly saved as dark
921
+ this.setTheme(true);
922
+ }
923
+ else {
924
+ // Default to light theme, ignoring system preference
925
+ this.setTheme(false);
926
+ // Set default theme preference if not exists
927
+ if (!savedTheme) {
928
+ localStorage.setItem('theme', 'light');
929
+ }
930
+ }
931
+ }
932
+ followSystemTheme() {
933
+ // This method is kept for compatibility but no longer used by default
934
+ // Set theme based on system preference only when explicitly requested
935
+ this.setTheme(this.mediaQuery.matches);
936
+ // Listen for system theme changes only when in auto mode
937
+ this.mediaQuery.addEventListener('change', (e) => {
938
+ const savedTheme = localStorage.getItem('theme');
939
+ if (savedTheme === 'auto') {
940
+ this.setTheme(e.matches);
941
+ }
942
+ });
943
+ }
944
+ toggleTheme() {
945
+ const currentTheme = this.isDarkModeSubject.value;
946
+ this.setTheme(!currentTheme);
947
+ // When manually toggling, save the specific preference
948
+ localStorage.setItem('theme', !currentTheme ? 'dark' : 'light');
949
+ }
950
+ setThemeMode(mode) {
951
+ localStorage.setItem('theme', mode);
952
+ if (mode === 'auto') {
953
+ this.followSystemTheme();
954
+ }
955
+ else {
956
+ this.setTheme(mode === 'dark');
957
+ }
958
+ }
959
+ setTheme(isDark) {
960
+ this.isDarkModeSubject.next(isDark);
961
+ // Apply theme to document
962
+ if (isDark) {
963
+ this.document.documentElement.classList.add('app-dark');
964
+ }
965
+ else {
966
+ this.document.documentElement.classList.remove('app-dark');
967
+ }
968
+ }
969
+ get isDarkMode() {
970
+ return this.isDarkModeSubject.value;
971
+ }
972
+ get currentThemeMode() {
973
+ const savedTheme = localStorage.getItem('theme');
974
+ return savedTheme || 'light'; // Default to light instead of auto
975
+ }
976
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ThemeService, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
977
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ThemeService, providedIn: 'root' });
978
+ }
979
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ThemeService, decorators: [{
980
+ type: Injectable,
981
+ args: [{
982
+ providedIn: 'root'
983
+ }]
984
+ }], ctorParameters: () => [{ type: Document, decorators: [{
985
+ type: Inject,
986
+ args: [DOCUMENT]
987
+ }] }] });
988
+
989
+ const SUPPORTED_LANGUAGES = [
990
+ {
991
+ code: 'pt-BR',
992
+ name: 'Português (Brasil)',
993
+ flag: '🇧🇷',
994
+ aliases: ['pt', 'pt_BR', 'portuguese', 'br']
995
+ },
996
+ {
997
+ code: 'en-US',
998
+ name: 'English (US)',
999
+ flag: '🇺🇸',
1000
+ aliases: ['en', 'en_US', 'english', 'us']
1001
+ },
1002
+ {
1003
+ code: 'es-ES',
1004
+ name: 'Español (España)',
1005
+ flag: '🇪🇸',
1006
+ aliases: ['es', 'es_ES', 'spanish', 'spain']
1007
+ }
1008
+ ];
1009
+ const DEFAULT_LANGUAGE = 'pt-BR';
1010
+ const TRANSLATION_CONFIG = {
1011
+ supportedLanguages: SUPPORTED_LANGUAGES,
1012
+ defaultLanguage: DEFAULT_LANGUAGE,
1013
+ fallbackLanguage: 'pt-BR',
1014
+ storageKey: 'selectedLanguage',
1015
+ assetsPath: './app/locale'
1016
+ };
1017
+ /**
1018
+ * Mapeia um locale do token Senior para um código de idioma suportado
1019
+ */
1020
+ function mapTokenLocaleToLanguage(tokenLocale) {
1021
+ if (!tokenLocale)
1022
+ return null;
1023
+ // Normalizar o locale (remover case sensitivity e caracteres especiais)
1024
+ const normalizedLocale = tokenLocale.toLowerCase().replace(/[_-]/g, '');
1025
+ // Procurar correspondência exata primeiro
1026
+ for (const lang of SUPPORTED_LANGUAGES) {
1027
+ if (lang.code.toLowerCase().replace(/[_-]/g, '') === normalizedLocale) {
1028
+ return lang.code;
1029
+ }
1030
+ }
1031
+ // Procurar por aliases
1032
+ for (const lang of SUPPORTED_LANGUAGES) {
1033
+ if (lang.aliases) {
1034
+ for (const alias of lang.aliases) {
1035
+ if (alias.toLowerCase().replace(/[_-]/g, '') === normalizedLocale) {
1036
+ return lang.code;
1037
+ }
1038
+ }
1039
+ }
1040
+ }
1041
+ // Tentar correspondência parcial (apenas o idioma, ignorando país)
1042
+ const languageOnly = normalizedLocale.substring(0, 2);
1043
+ for (const lang of SUPPORTED_LANGUAGES) {
1044
+ const langCode = lang.code.toLowerCase().substring(0, 2);
1045
+ if (langCode === languageOnly) {
1046
+ return lang.code;
1047
+ }
1048
+ }
1049
+ console.warn(`Unsupported locale from token: ${tokenLocale}`);
1050
+ return null;
1051
+ }
1052
+ /**
1053
+ * Obtém informações de um idioma pelo código
1054
+ */
1055
+ function getLanguageInfo(code) {
1056
+ return SUPPORTED_LANGUAGES.find(lang => lang.code === code) || null;
1057
+ }
1058
+
1059
+ class TranslationService {
1060
+ seniorTokenService;
1061
+ currentLanguage = new BehaviorSubject(TRANSLATION_CONFIG.defaultLanguage);
1062
+ translations = {};
1063
+ loadingLanguages = new Set();
1064
+ translationLoader;
1065
+ constructor(seniorTokenService, loader) {
1066
+ this.seniorTokenService = seniorTokenService;
1067
+ this.translationLoader = loader;
1068
+ this.loadStoredLanguage();
1069
+ // Carregar traduções do idioma inicial imediatamente
1070
+ this.loadTranslations();
1071
+ // Monitorar mudanças no token para atualizar idioma
1072
+ this.watchTokenChanges();
1073
+ }
1074
+ get currentLanguage$() {
1075
+ return this.currentLanguage.asObservable();
1076
+ }
1077
+ getCurrentLanguage() {
1078
+ return this.currentLanguage.value;
1079
+ }
1080
+ async setLanguage(language) {
1081
+ this.currentLanguage.next(language);
1082
+ localStorage.setItem(TRANSLATION_CONFIG.storageKey, language);
1083
+ await this.loadTranslations();
1084
+ }
1085
+ async waitForTranslations() {
1086
+ const language = this.currentLanguage.value;
1087
+ if (!this.isLanguageLoaded(language)) {
1088
+ await this.loadTranslations();
1089
+ }
1090
+ }
1091
+ /**
1092
+ * Verifica se as traduções do idioma atual estão carregadas
1093
+ */
1094
+ isLanguageLoaded(language) {
1095
+ const lang = language || this.currentLanguage.value;
1096
+ return !!this.translations[lang] && Object.keys(this.translations[lang]).length > 0;
1097
+ }
1098
+ translate(key, params) {
1099
+ const currentLang = this.currentLanguage.value;
1100
+ const translations = this.translations[currentLang];
1101
+ if (!translations || Object.keys(translations).length === 0) {
1102
+ // Se as traduções não estão carregadas, tentar carregar de forma assíncrona
1103
+ if (!this.loadingLanguages.has(currentLang)) {
1104
+ this.loadTranslations().catch(error => {
1105
+ console.error('Error loading translations:', error);
1106
+ });
1107
+ }
1108
+ console.warn(`No translations loaded for language: ${currentLang}`);
1109
+ return key;
1110
+ }
1111
+ // Para estrutura plana, buscar diretamente pela chave
1112
+ const translation = translations[key];
1113
+ if (!translation) {
1114
+ console.warn(`Translation key not found: ${key}`);
1115
+ return key;
1116
+ }
1117
+ if (typeof translation !== 'string') {
1118
+ console.warn(`Translation key is not a string: ${key}`);
1119
+ return key;
1120
+ }
1121
+ // Replace parameters if provided
1122
+ if (params) {
1123
+ return this.replaceParams(translation, params);
1124
+ }
1125
+ return translation;
1126
+ }
1127
+ async loadTranslations() {
1128
+ const language = this.currentLanguage.value;
1129
+ // Se já está carregado, não carregar novamente
1130
+ if (this.isLanguageLoaded(language)) {
1131
+ return;
1132
+ }
1133
+ // Evitar carregamentos duplicados
1134
+ if (this.loadingLanguages.has(language)) {
1135
+ // Aguardar um pouco e tentar novamente
1136
+ await new Promise(resolve => setTimeout(resolve, 100));
1137
+ if (this.isLanguageLoaded(language)) {
1138
+ return;
1139
+ }
1140
+ }
1141
+ this.loadingLanguages.add(language);
1142
+ try {
1143
+ await this.doLoadTranslations(language);
1144
+ }
1145
+ catch (error) {
1146
+ console.error('Error in loadTranslations:', error);
1147
+ }
1148
+ finally {
1149
+ this.loadingLanguages.delete(language);
1150
+ }
1151
+ }
1152
+ async doLoadTranslations(language) {
1153
+ try {
1154
+ // Se há um loader customizado, usar ele
1155
+ if (this.translationLoader) {
1156
+ const translations = await this.translationLoader.loadTranslations(language);
1157
+ this.translations[language] = translations;
1158
+ console.log(`Translations loaded for ${language}:`, Object.keys(this.translations[language]).length, 'keys');
1159
+ return;
1160
+ }
1161
+ // Caso contrário, avisar que não há traduções
1162
+ console.warn(`No translation loader provided. Translations for ${language} not loaded.`);
1163
+ this.translations[language] = {};
1164
+ }
1165
+ catch (error) {
1166
+ console.error(`Error loading translations for ${language}:`, error);
1167
+ // Fallback para o idioma padrão se houver erro
1168
+ if (language !== TRANSLATION_CONFIG.fallbackLanguage) {
1169
+ try {
1170
+ if (this.translationLoader) {
1171
+ const fallbackTranslations = await this.translationLoader.loadTranslations(TRANSLATION_CONFIG.fallbackLanguage);
1172
+ this.translations[language] = fallbackTranslations;
1173
+ console.log(`Fallback translations loaded for ${language}`);
1174
+ }
1175
+ }
1176
+ catch (fallbackError) {
1177
+ console.error(`Error loading fallback translations:`, fallbackError);
1178
+ }
1179
+ }
1180
+ }
1181
+ }
1182
+ loadStoredLanguage() {
1183
+ // 1. Primeiro, tentar obter do token Senior
1184
+ const tokenLocale = this.seniorTokenService.getUserLocale();
1185
+ if (tokenLocale) {
1186
+ const mappedLanguage = mapTokenLocaleToLanguage(tokenLocale);
1187
+ if (mappedLanguage) {
1188
+ console.log(`Using language from Senior token: ${tokenLocale} -> ${mappedLanguage}`);
1189
+ this.currentLanguage.next(mappedLanguage);
1190
+ return;
1191
+ }
1192
+ }
1193
+ // 2. Segundo, tentar obter do localStorage
1194
+ const stored = localStorage.getItem(TRANSLATION_CONFIG.storageKey);
1195
+ const supportedCodes = TRANSLATION_CONFIG.supportedLanguages.map(lang => lang.code);
1196
+ if (stored && supportedCodes.includes(stored)) {
1197
+ console.log('Using language from localStorage:', stored);
1198
+ this.currentLanguage.next(stored);
1199
+ return;
1200
+ }
1201
+ // 3. Por último, usar idioma padrão
1202
+ console.log('Using default language:', TRANSLATION_CONFIG.defaultLanguage);
1203
+ this.currentLanguage.next(TRANSLATION_CONFIG.defaultLanguage);
1204
+ }
1205
+ replaceParams(text, params) {
1206
+ return text.replace(/\{\{(\w+)\}\}/g, (match, key) => {
1207
+ return params[key] !== undefined ? params[key] : match;
1208
+ });
1209
+ }
1210
+ getSupportedLanguages() {
1211
+ return TRANSLATION_CONFIG.supportedLanguages;
1212
+ }
1213
+ /**
1214
+ * Força o recarregamento das traduções (útil para desenvolvimento)
1215
+ */
1216
+ async reloadTranslations() {
1217
+ const language = this.currentLanguage.value;
1218
+ delete this.translations[language];
1219
+ await this.loadTranslations();
1220
+ }
1221
+ /**
1222
+ * Obtém todas as chaves de tradução carregadas
1223
+ */
1224
+ getLoadedKeys() {
1225
+ const language = this.currentLanguage.value;
1226
+ const translations = this.translations[language];
1227
+ return translations ? Object.keys(translations) : [];
1228
+ }
1229
+ /**
1230
+ * Verifica se um código de idioma é suportado
1231
+ */
1232
+ isValidLanguage(locale) {
1233
+ const supportedCodes = TRANSLATION_CONFIG.supportedLanguages.map(lang => lang.code);
1234
+ return supportedCodes.includes(locale);
1235
+ }
1236
+ /**
1237
+ * Monitora mudanças no token Senior para atualizar idioma automaticamente
1238
+ */
1239
+ watchTokenChanges() {
1240
+ this.seniorTokenService.watchTokenChanges((tokenData) => {
1241
+ if (tokenData?.locale) {
1242
+ const mappedLanguage = mapTokenLocaleToLanguage(tokenData.locale);
1243
+ if (mappedLanguage) {
1244
+ const newLocale = mappedLanguage;
1245
+ const currentLocale = this.currentLanguage.value;
1246
+ if (newLocale !== currentLocale) {
1247
+ console.log(`Token locale changed: ${tokenData.locale} -> ${newLocale}`);
1248
+ this.setLanguage(newLocale);
1249
+ }
1250
+ }
1251
+ }
1252
+ });
1253
+ }
1254
+ /**
1255
+ * Força atualização do idioma baseado no token atual
1256
+ */
1257
+ async syncWithToken() {
1258
+ const tokenLocale = this.seniorTokenService.getUserLocale();
1259
+ if (tokenLocale) {
1260
+ const mappedLanguage = mapTokenLocaleToLanguage(tokenLocale);
1261
+ if (mappedLanguage) {
1262
+ const newLocale = mappedLanguage;
1263
+ const currentLocale = this.currentLanguage.value;
1264
+ if (newLocale !== currentLocale) {
1265
+ console.log(`Syncing language with token: ${tokenLocale} -> ${newLocale}`);
1266
+ await this.setLanguage(newLocale);
1267
+ }
1268
+ }
1269
+ }
1270
+ }
1271
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TranslationService, deps: [{ token: SeniorTokenService }, { token: 'TranslationLoader', optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
1272
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TranslationService, providedIn: 'root' });
1273
+ }
1274
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TranslationService, decorators: [{
1275
+ type: Injectable,
1276
+ args: [{
1277
+ providedIn: 'root'
1278
+ }]
1279
+ }], ctorParameters: () => [{ type: SeniorTokenService }, { type: undefined, decorators: [{
1280
+ type: Optional
1281
+ }, {
1282
+ type: Inject,
1283
+ args: ['TranslationLoader']
1284
+ }] }] });
1285
+
1286
+ class ExportDialogComponent {
1287
+ translationService;
1288
+ visible = false;
1289
+ allRecords = [];
1290
+ selectedRecords = [];
1291
+ statusLabels;
1292
+ translationPrefix = 'crmx.business';
1293
+ set availableColumns(columns) {
1294
+ if (columns && columns.length > 0) {
1295
+ this.columns = columns;
1296
+ }
1297
+ }
1298
+ visibleChange = new EventEmitter();
1299
+ exportFormat = 'xlsx';
1300
+ exportScope = 'all';
1301
+ formats = [];
1302
+ scopes = [];
1303
+ columns = [];
1304
+ constructor(translationService) {
1305
+ this.translationService = translationService;
1306
+ this.initializeFormats();
1307
+ this.initializeScopes();
1308
+ }
1309
+ ngOnChanges() {
1310
+ this.initializeFormats();
1311
+ this.initializeScopes();
1312
+ }
1313
+ t(key) {
1314
+ if (this.translationService) {
1315
+ return this.translationService.translate(`${this.translationPrefix}.${key}`);
1316
+ }
1317
+ return key;
1318
+ }
1319
+ initializeFormats() {
1320
+ this.formats = [
1321
+ {
1322
+ value: 'xlsx',
1323
+ label: 'Excel',
1324
+ icon: 'pi-file-excel',
1325
+ description: this.t('export_excel_description')
1326
+ },
1327
+ {
1328
+ value: 'pdf',
1329
+ label: 'PDF',
1330
+ icon: 'pi-file-pdf',
1331
+ description: this.t('export_pdf_description')
1332
+ }
1333
+ ];
1334
+ }
1335
+ initializeScopes() {
1336
+ this.scopes = [
1337
+ {
1338
+ value: 'all',
1339
+ label: this.t('export_all_records'),
1340
+ icon: 'pi-list',
1341
+ description: this.t('export_all_records_description')
1342
+ },
1343
+ {
1344
+ value: 'selected',
1345
+ label: this.t('export_selected_records'),
1346
+ icon: 'pi-check-square',
1347
+ description: this.t('export_selected_records_description')
1348
+ }
1349
+ ];
1350
+ }
1351
+ get recordsToExport() {
1352
+ return this.exportScope === 'all' ? this.allRecords : this.selectedRecords;
1353
+ }
1354
+ get selectedColumnsCount() {
1355
+ return this.columns.filter(col => col.selected).length;
1356
+ }
1357
+ get canExport() {
1358
+ return this.selectedColumnsCount > 0 && this.recordsToExport.length > 0;
1359
+ }
1360
+ onHide() {
1361
+ this.visibleChange.emit(false);
1362
+ }
1363
+ selectAllColumns() {
1364
+ this.columns.forEach(col => col.selected = true);
1365
+ }
1366
+ deselectAllColumns() {
1367
+ this.columns.forEach(col => col.selected = false);
1368
+ }
1369
+ export() {
1370
+ if (!this.canExport)
1371
+ return;
1372
+ if (this.exportFormat === 'xlsx') {
1373
+ this.exportToExcel();
1374
+ }
1375
+ else {
1376
+ this.exportToPDF();
1377
+ }
1378
+ this.onHide();
1379
+ }
1380
+ exportToExcel() {
1381
+ const selectedColumns = this.columns.filter(col => col.selected);
1382
+ const data = this.recordsToExport.map(record => {
1383
+ const row = {};
1384
+ selectedColumns.forEach(col => {
1385
+ if (col.field === 'status' && record.status && this.statusLabels) {
1386
+ row[col.header] = this.statusLabels[record.status];
1387
+ }
1388
+ else {
1389
+ row[col.header] = record[col.field];
1390
+ }
1391
+ });
1392
+ return row;
1393
+ });
1394
+ const worksheet = XLSX.utils.json_to_sheet(data);
1395
+ const workbook = XLSX.utils.book_new();
1396
+ XLSX.utils.book_append_sheet(workbook, worksheet, this.t('export_data_sheet_name'));
1397
+ // Ajustar largura das colunas
1398
+ const maxWidth = 30;
1399
+ const colWidths = selectedColumns.map(col => ({
1400
+ wch: Math.min(col.header.length + 5, maxWidth)
1401
+ }));
1402
+ worksheet['!cols'] = colWidths;
1403
+ const fileName = `export-${new Date().getTime()}.xlsx`;
1404
+ XLSX.writeFile(workbook, fileName);
1405
+ }
1406
+ exportToPDF() {
1407
+ const doc = new jsPDF();
1408
+ const selectedColumns = this.columns.filter(col => col.selected);
1409
+ // Título
1410
+ doc.setFontSize(18);
1411
+ doc.setTextColor(18, 168, 133);
1412
+ doc.text(this.t('export_pdf_title'), 14, 20);
1413
+ // Subtítulo
1414
+ doc.setFontSize(10);
1415
+ doc.setTextColor(100);
1416
+ doc.text(`${this.t('export_exported_at')}: ${new Date().toLocaleString('pt-BR')}`, 14, 28);
1417
+ doc.text(`${this.t('export_total_records')}: ${this.recordsToExport.length}`, 14, 34);
1418
+ // Preparar dados
1419
+ const headers = selectedColumns.map(col => col.header);
1420
+ const data = this.recordsToExport.map(record => {
1421
+ return selectedColumns.map(col => {
1422
+ if (col.field === 'status' && record.status && this.statusLabels) {
1423
+ return this.statusLabels[record.status];
1424
+ }
1425
+ return String(record[col.field] || '');
1426
+ });
1427
+ });
1428
+ // Tabela
1429
+ autoTable(doc, {
1430
+ head: [headers],
1431
+ body: data,
1432
+ startY: 40,
1433
+ theme: 'grid',
1434
+ headStyles: {
1435
+ fillColor: [18, 168, 133],
1436
+ textColor: [255, 255, 255],
1437
+ fontStyle: 'bold',
1438
+ halign: 'center'
1439
+ },
1440
+ styles: {
1441
+ fontSize: 9,
1442
+ cellPadding: 3
1443
+ },
1444
+ alternateRowStyles: {
1445
+ fillColor: [248, 255, 254]
1446
+ }
1447
+ });
1448
+ const fileName = `export-${new Date().getTime()}.pdf`;
1449
+ doc.save(fileName);
1450
+ }
1451
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDialogComponent, deps: [{ token: 'TranslationService', optional: true }], target: i0.ɵɵFactoryTarget.Component });
1452
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ExportDialogComponent, isStandalone: true, selector: "sia-export-dialog", inputs: { visible: "visible", allRecords: "allRecords", selectedRecords: "selectedRecords", statusLabels: "statusLabels", translationPrefix: "translationPrefix", availableColumns: "availableColumns" }, outputs: { visibleChange: "visibleChange" }, usesOnChanges: true, ngImport: i0, template: "<p-dialog\n [(visible)]=\"visible\"\n (onHide)=\"onHide()\"\n [modal]=\"true\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n [style]=\"{ width: '700px' }\"\n styleClass=\"export-dialog\"\n>\n <ng-template pTemplate=\"header\">\n <div class=\"custom-header\">\n <i class=\"pi pi-download header-icon\"></i>\n <span>{{ t('export_data_title') }}</span>\n </div>\n </ng-template>\n\n <div class=\"export-content\">\n <!-- Formato de Exporta\u00E7\u00E3o -->\n <div class=\"export-section\">\n <div class=\"section-header\">\n <div class=\"section-icon\">\n <i class=\"pi pi-file\"></i>\n </div>\n <h3>{{ t('export_format_title') }}</h3>\n </div>\n <div class=\"format-options\">\n <div\n *ngFor=\"let format of formats\"\n class=\"format-card\"\n [class.selected]=\"exportFormat === format.value\"\n (click)=\"exportFormat = format.value\"\n >\n <div class=\"card-radio\">\n <p-radioButton\n [value]=\"format.value\"\n [(ngModel)]=\"exportFormat\"\n [inputId]=\"format.value\"\n ></p-radioButton>\n </div>\n <div class=\"card-icon\" [class.excel]=\"format.value === 'xlsx'\" [class.pdf]=\"format.value === 'pdf'\">\n <i class=\"pi\" [ngClass]=\"format.icon\"></i>\n </div>\n <div class=\"card-info\">\n <label [for]=\"format.value\" class=\"card-label\">{{ format.label }}</label>\n <span class=\"card-description\">{{ format.description }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Escopo de Exporta\u00E7\u00E3o -->\n <div class=\"export-section\">\n <div class=\"section-header\">\n <div class=\"section-icon\">\n <i class=\"pi pi-filter\"></i>\n </div>\n <h3>{{ t('export_scope_title') }}</h3>\n </div>\n <div class=\"scope-options\">\n <div\n *ngFor=\"let scope of scopes\"\n class=\"scope-card\"\n [class.selected]=\"exportScope === scope.value\"\n [class.disabled]=\"scope.value === 'selected' && selectedRecords.length === 0\"\n (click)=\"scope.value === 'selected' && selectedRecords.length === 0 ? null : exportScope = scope.value\"\n >\n <div class=\"card-radio\">\n <p-radioButton\n [value]=\"scope.value\"\n [(ngModel)]=\"exportScope\"\n [inputId]=\"scope.value\"\n [disabled]=\"scope.value === 'selected' && selectedRecords.length === 0\"\n ></p-radioButton>\n </div>\n <div class=\"card-icon\">\n <i class=\"pi\" [ngClass]=\"scope.icon\"></i>\n </div>\n <div class=\"card-info\">\n <label [for]=\"scope.value\" class=\"card-label\">{{ scope.label }}</label>\n <span class=\"card-description\">{{ scope.description }}</span>\n <span class=\"card-count\">\n <i class=\"pi pi-circle-fill\"></i>\n {{ scope.value === 'all' ? allRecords.length : selectedRecords.length }} {{ t('export_records_count') }}\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Sele\u00E7\u00E3o de Colunas -->\n <div class=\"export-section\">\n <div class=\"section-header\">\n <div class=\"section-icon\">\n <i class=\"pi pi-table\"></i>\n </div>\n <h3>{{ t('export_columns_title') }}</h3>\n <div class=\"column-actions\">\n <button type=\"button\" class=\"action-link\" (click)=\"selectAllColumns()\">\n {{ t('export_select_all_columns') }}\n </button>\n <span class=\"separator\">|</span>\n <button type=\"button\" class=\"action-link\" (click)=\"deselectAllColumns()\">\n {{ t('export_clear_selection') }}\n </button>\n </div>\n </div>\n <div class=\"columns-grid\">\n <div\n *ngFor=\"let column of columns\"\n class=\"column-item\"\n [class.selected]=\"column.selected\"\n (click)=\"column.selected = !column.selected\"\n >\n <p-checkbox\n [(ngModel)]=\"column.selected\"\n [binary]=\"true\"\n [inputId]=\"column.field\"\n ></p-checkbox>\n <label [for]=\"column.field\" class=\"column-label\">\n {{ column.header }}\n </label>\n </div>\n </div>\n <div class=\"columns-summary\">\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ selectedColumnsCount }} {{ t('export_columns_summary') }} {{ columns.length }} {{ t('export_columns_selected') }}</span>\n </div>\n </div>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <p-button\n [label]=\"t('cancel')\"\n icon=\"pi pi-times\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onHide()\"\n ></p-button>\n <p-button\n [label]=\"t('export_button')\"\n icon=\"pi pi-download\"\n [disabled]=\"!canExport\"\n (onClick)=\"export()\"\n ></p-button>\n </ng-template>\n</p-dialog>\n", styles: ["::ng-deep .export-dialog .p-dialog{border-radius:16px;overflow:hidden;box-shadow:0 20px 60px #0000004d}::ng-deep .export-dialog .p-dialog-content{padding:0;background:#fff;max-height:70vh;overflow-y:auto}.custom-header{display:flex;align-items:center;gap:12px;font-size:18px;font-weight:600}.custom-header .header-icon{font-size:22px;color:var(--p-primary-500)}.export-content{padding:28px 32px}.export-section{margin-bottom:32px}.export-section:last-child{margin-bottom:0}.export-section .section-header{display:flex;align-items:center;gap:12px;margin-bottom:20px}.export-section .section-header .section-icon{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.1),rgba(var(--p-primary-rgb),.15));border-radius:10px}.export-section .section-header .section-icon i{font-size:18px;color:var(--p-primary-500)}.export-section .section-header h3{margin:0;font-size:17px;font-weight:700;color:var(--neutral-color-800);flex:1}.export-section .section-header .column-actions{display:flex;align-items:center;gap:10px;font-size:13px}.export-section .section-header .column-actions .action-link{background:none;border:none;color:var(--p-primary-500);cursor:pointer;font-weight:600;padding:0;transition:all .2s;font-size:13px}.export-section .section-header .column-actions .action-link:hover{color:var(--p-primary-500)}.export-section .section-header .column-actions .separator{color:var(--neutral-color-300);font-weight:400}.format-options{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}.format-card{display:flex;align-items:center;gap:16px;padding:20px;border:2px solid #e5e7eb;border-radius:12px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);background:#fff;position:relative;overflow:hidden}.format-card:before{content:\"\";position:absolute;inset:0;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.03),rgba(var(--p-primary-rgb),.06));opacity:0;transition:opacity .3s}.format-card:hover{border-color:var(--p-primary-500);transform:translateY(-2px);box-shadow:0 8px 24px rgba(var(--p-primary-rgb),.15)}.format-card:hover:before{opacity:1}.format-card.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08));box-shadow:0 0 0 4px rgba(var(--p-primary-rgb),.1)}.format-card.selected .card-icon{background:linear-gradient(135deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);transform:scale(1.05)}.format-card.selected .card-icon i{color:#fff}.format-card .card-radio ::ng-deep .p-radiobutton .p-radiobutton-box{width:20px;height:20px;border-width:2px}.format-card .card-icon{width:52px;height:52px;display:flex;align-items:center;justify-content:center;border-radius:12px;transition:all .3s;position:relative;z-index:1}.format-card .card-icon.excel{background:linear-gradient(135deg,var(--p-primary-50) 0%,var(--p-primary-100) 100%)}.format-card .card-icon.excel i{color:#107c41;font-size:26px}.format-card .card-icon.pdf{background:linear-gradient(135deg,#fee8e8,#fdd4d4)}.format-card .card-icon.pdf i{color:#dc2626;font-size:26px}.format-card .card-info{flex:1;display:flex;flex-direction:column;gap:4px;position:relative;z-index:1}.format-card .card-label{font-size:16px;font-weight:700;color:var(--neutral-color-800);cursor:pointer;margin:0}.format-card .card-description{font-size:13px;color:var(--neutral-color-600);line-height:1.4}.scope-options{display:flex;flex-direction:column;gap:14px}.scope-card{display:flex;align-items:center;gap:16px;padding:20px;border:2px solid #e5e7eb;border-radius:12px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);background:#fff;position:relative;overflow:hidden}.scope-card:before{content:\"\";position:absolute;inset:0;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.03),rgba(var(--p-primary-rgb),.06));opacity:0;transition:opacity .3s}.scope-card:hover:not(.disabled){border-color:var(--p-primary-500);box-shadow:0 4px 16px rgba(var(--p-primary-rgb),.12)}.scope-card:hover:not(.disabled):before{opacity:1}.scope-card.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08));box-shadow:0 0 0 4px rgba(var(--p-primary-rgb),.1)}.scope-card.selected .card-icon{background:linear-gradient(135deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);transform:scale(1.05)}.scope-card.selected .card-icon i{color:#fff}.scope-card.disabled{opacity:.5;cursor:not-allowed;background:#f9fafb}.scope-card.disabled:hover{border-color:#e5e7eb;box-shadow:none}.scope-card .card-radio ::ng-deep .p-radiobutton .p-radiobutton-box{width:20px;height:20px;border-width:2px}.scope-card .card-icon{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--p-primary-50) 0%,var(--p-primary-100) 100%);border-radius:12px;transition:all .3s;position:relative;z-index:1}.scope-card .card-icon i{font-size:22px;color:var(--p-primary-500);transition:all .3s}.scope-card .card-info{flex:1;display:flex;flex-direction:column;gap:4px;position:relative;z-index:1}.scope-card .card-label{font-size:16px;font-weight:700;color:var(--neutral-color-800);cursor:pointer;margin:0}.scope-card .card-description{font-size:13px;color:var(--neutral-color-600);line-height:1.4}.scope-card .card-count{font-size:13px;font-weight:600;color:var(--p-primary-500);margin-top:6px;display:inline-flex;align-items:center;gap:6px}.scope-card .card-count i{font-size:6px}.columns-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:16px}.column-item{display:flex;align-items:center;gap:12px;padding:16px;border:2px solid #e5e7eb;border-radius:10px;transition:all .2s;background:#fff;cursor:pointer}.column-item:hover{border-color:var(--p-primary-500);background:rgba(var(--p-primary-rgb),.03)}.column-item.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08))}.column-item ::ng-deep .p-checkbox .p-checkbox-box{width:20px;height:20px;border-width:2px}.column-item .column-label{font-size:14px;font-weight:600;color:var(--neutral-color-800);cursor:pointer;margin:0;-webkit-user-select:none;user-select:none}.columns-summary{display:flex;align-items:center;gap:10px;padding:14px 18px;background:linear-gradient(135deg,var(--p-primary-50) 0%,var(--p-primary-100) 100%);border-radius:10px;font-size:14px;color:var(--neutral-color-700);font-weight:600}.columns-summary i{color:var(--p-primary-500);font-size:18px}@media (max-width: 768px){::ng-deep .export-dialog .p-dialog{width:95vw!important;margin:0}.dialog-header{padding:28px 24px}.dialog-header .header-icon-wrapper{width:56px;height:56px}.dialog-header .header-icon-wrapper i{font-size:28px}.dialog-header .header-text h2{font-size:24px}.export-content{padding:24px}.format-options,.columns-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i5.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i5$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i6.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i7.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: DividerModule }] });
1453
+ }
1454
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDialogComponent, decorators: [{
1455
+ type: Component,
1456
+ args: [{ selector: 'sia-export-dialog', standalone: true, imports: [
1457
+ CommonModule,
1458
+ FormsModule,
1459
+ DialogModule,
1460
+ ButtonModule,
1461
+ RadioButtonModule,
1462
+ CheckboxModule,
1463
+ DividerModule
1464
+ ], template: "<p-dialog\n [(visible)]=\"visible\"\n (onHide)=\"onHide()\"\n [modal]=\"true\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n [style]=\"{ width: '700px' }\"\n styleClass=\"export-dialog\"\n>\n <ng-template pTemplate=\"header\">\n <div class=\"custom-header\">\n <i class=\"pi pi-download header-icon\"></i>\n <span>{{ t('export_data_title') }}</span>\n </div>\n </ng-template>\n\n <div class=\"export-content\">\n <!-- Formato de Exporta\u00E7\u00E3o -->\n <div class=\"export-section\">\n <div class=\"section-header\">\n <div class=\"section-icon\">\n <i class=\"pi pi-file\"></i>\n </div>\n <h3>{{ t('export_format_title') }}</h3>\n </div>\n <div class=\"format-options\">\n <div\n *ngFor=\"let format of formats\"\n class=\"format-card\"\n [class.selected]=\"exportFormat === format.value\"\n (click)=\"exportFormat = format.value\"\n >\n <div class=\"card-radio\">\n <p-radioButton\n [value]=\"format.value\"\n [(ngModel)]=\"exportFormat\"\n [inputId]=\"format.value\"\n ></p-radioButton>\n </div>\n <div class=\"card-icon\" [class.excel]=\"format.value === 'xlsx'\" [class.pdf]=\"format.value === 'pdf'\">\n <i class=\"pi\" [ngClass]=\"format.icon\"></i>\n </div>\n <div class=\"card-info\">\n <label [for]=\"format.value\" class=\"card-label\">{{ format.label }}</label>\n <span class=\"card-description\">{{ format.description }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Escopo de Exporta\u00E7\u00E3o -->\n <div class=\"export-section\">\n <div class=\"section-header\">\n <div class=\"section-icon\">\n <i class=\"pi pi-filter\"></i>\n </div>\n <h3>{{ t('export_scope_title') }}</h3>\n </div>\n <div class=\"scope-options\">\n <div\n *ngFor=\"let scope of scopes\"\n class=\"scope-card\"\n [class.selected]=\"exportScope === scope.value\"\n [class.disabled]=\"scope.value === 'selected' && selectedRecords.length === 0\"\n (click)=\"scope.value === 'selected' && selectedRecords.length === 0 ? null : exportScope = scope.value\"\n >\n <div class=\"card-radio\">\n <p-radioButton\n [value]=\"scope.value\"\n [(ngModel)]=\"exportScope\"\n [inputId]=\"scope.value\"\n [disabled]=\"scope.value === 'selected' && selectedRecords.length === 0\"\n ></p-radioButton>\n </div>\n <div class=\"card-icon\">\n <i class=\"pi\" [ngClass]=\"scope.icon\"></i>\n </div>\n <div class=\"card-info\">\n <label [for]=\"scope.value\" class=\"card-label\">{{ scope.label }}</label>\n <span class=\"card-description\">{{ scope.description }}</span>\n <span class=\"card-count\">\n <i class=\"pi pi-circle-fill\"></i>\n {{ scope.value === 'all' ? allRecords.length : selectedRecords.length }} {{ t('export_records_count') }}\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Sele\u00E7\u00E3o de Colunas -->\n <div class=\"export-section\">\n <div class=\"section-header\">\n <div class=\"section-icon\">\n <i class=\"pi pi-table\"></i>\n </div>\n <h3>{{ t('export_columns_title') }}</h3>\n <div class=\"column-actions\">\n <button type=\"button\" class=\"action-link\" (click)=\"selectAllColumns()\">\n {{ t('export_select_all_columns') }}\n </button>\n <span class=\"separator\">|</span>\n <button type=\"button\" class=\"action-link\" (click)=\"deselectAllColumns()\">\n {{ t('export_clear_selection') }}\n </button>\n </div>\n </div>\n <div class=\"columns-grid\">\n <div\n *ngFor=\"let column of columns\"\n class=\"column-item\"\n [class.selected]=\"column.selected\"\n (click)=\"column.selected = !column.selected\"\n >\n <p-checkbox\n [(ngModel)]=\"column.selected\"\n [binary]=\"true\"\n [inputId]=\"column.field\"\n ></p-checkbox>\n <label [for]=\"column.field\" class=\"column-label\">\n {{ column.header }}\n </label>\n </div>\n </div>\n <div class=\"columns-summary\">\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ selectedColumnsCount }} {{ t('export_columns_summary') }} {{ columns.length }} {{ t('export_columns_selected') }}</span>\n </div>\n </div>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <p-button\n [label]=\"t('cancel')\"\n icon=\"pi pi-times\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onHide()\"\n ></p-button>\n <p-button\n [label]=\"t('export_button')\"\n icon=\"pi pi-download\"\n [disabled]=\"!canExport\"\n (onClick)=\"export()\"\n ></p-button>\n </ng-template>\n</p-dialog>\n", styles: ["::ng-deep .export-dialog .p-dialog{border-radius:16px;overflow:hidden;box-shadow:0 20px 60px #0000004d}::ng-deep .export-dialog .p-dialog-content{padding:0;background:#fff;max-height:70vh;overflow-y:auto}.custom-header{display:flex;align-items:center;gap:12px;font-size:18px;font-weight:600}.custom-header .header-icon{font-size:22px;color:var(--p-primary-500)}.export-content{padding:28px 32px}.export-section{margin-bottom:32px}.export-section:last-child{margin-bottom:0}.export-section .section-header{display:flex;align-items:center;gap:12px;margin-bottom:20px}.export-section .section-header .section-icon{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.1),rgba(var(--p-primary-rgb),.15));border-radius:10px}.export-section .section-header .section-icon i{font-size:18px;color:var(--p-primary-500)}.export-section .section-header h3{margin:0;font-size:17px;font-weight:700;color:var(--neutral-color-800);flex:1}.export-section .section-header .column-actions{display:flex;align-items:center;gap:10px;font-size:13px}.export-section .section-header .column-actions .action-link{background:none;border:none;color:var(--p-primary-500);cursor:pointer;font-weight:600;padding:0;transition:all .2s;font-size:13px}.export-section .section-header .column-actions .action-link:hover{color:var(--p-primary-500)}.export-section .section-header .column-actions .separator{color:var(--neutral-color-300);font-weight:400}.format-options{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}.format-card{display:flex;align-items:center;gap:16px;padding:20px;border:2px solid #e5e7eb;border-radius:12px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);background:#fff;position:relative;overflow:hidden}.format-card:before{content:\"\";position:absolute;inset:0;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.03),rgba(var(--p-primary-rgb),.06));opacity:0;transition:opacity .3s}.format-card:hover{border-color:var(--p-primary-500);transform:translateY(-2px);box-shadow:0 8px 24px rgba(var(--p-primary-rgb),.15)}.format-card:hover:before{opacity:1}.format-card.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08));box-shadow:0 0 0 4px rgba(var(--p-primary-rgb),.1)}.format-card.selected .card-icon{background:linear-gradient(135deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);transform:scale(1.05)}.format-card.selected .card-icon i{color:#fff}.format-card .card-radio ::ng-deep .p-radiobutton .p-radiobutton-box{width:20px;height:20px;border-width:2px}.format-card .card-icon{width:52px;height:52px;display:flex;align-items:center;justify-content:center;border-radius:12px;transition:all .3s;position:relative;z-index:1}.format-card .card-icon.excel{background:linear-gradient(135deg,var(--p-primary-50) 0%,var(--p-primary-100) 100%)}.format-card .card-icon.excel i{color:#107c41;font-size:26px}.format-card .card-icon.pdf{background:linear-gradient(135deg,#fee8e8,#fdd4d4)}.format-card .card-icon.pdf i{color:#dc2626;font-size:26px}.format-card .card-info{flex:1;display:flex;flex-direction:column;gap:4px;position:relative;z-index:1}.format-card .card-label{font-size:16px;font-weight:700;color:var(--neutral-color-800);cursor:pointer;margin:0}.format-card .card-description{font-size:13px;color:var(--neutral-color-600);line-height:1.4}.scope-options{display:flex;flex-direction:column;gap:14px}.scope-card{display:flex;align-items:center;gap:16px;padding:20px;border:2px solid #e5e7eb;border-radius:12px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);background:#fff;position:relative;overflow:hidden}.scope-card:before{content:\"\";position:absolute;inset:0;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.03),rgba(var(--p-primary-rgb),.06));opacity:0;transition:opacity .3s}.scope-card:hover:not(.disabled){border-color:var(--p-primary-500);box-shadow:0 4px 16px rgba(var(--p-primary-rgb),.12)}.scope-card:hover:not(.disabled):before{opacity:1}.scope-card.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08));box-shadow:0 0 0 4px rgba(var(--p-primary-rgb),.1)}.scope-card.selected .card-icon{background:linear-gradient(135deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);transform:scale(1.05)}.scope-card.selected .card-icon i{color:#fff}.scope-card.disabled{opacity:.5;cursor:not-allowed;background:#f9fafb}.scope-card.disabled:hover{border-color:#e5e7eb;box-shadow:none}.scope-card .card-radio ::ng-deep .p-radiobutton .p-radiobutton-box{width:20px;height:20px;border-width:2px}.scope-card .card-icon{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--p-primary-50) 0%,var(--p-primary-100) 100%);border-radius:12px;transition:all .3s;position:relative;z-index:1}.scope-card .card-icon i{font-size:22px;color:var(--p-primary-500);transition:all .3s}.scope-card .card-info{flex:1;display:flex;flex-direction:column;gap:4px;position:relative;z-index:1}.scope-card .card-label{font-size:16px;font-weight:700;color:var(--neutral-color-800);cursor:pointer;margin:0}.scope-card .card-description{font-size:13px;color:var(--neutral-color-600);line-height:1.4}.scope-card .card-count{font-size:13px;font-weight:600;color:var(--p-primary-500);margin-top:6px;display:inline-flex;align-items:center;gap:6px}.scope-card .card-count i{font-size:6px}.columns-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:16px}.column-item{display:flex;align-items:center;gap:12px;padding:16px;border:2px solid #e5e7eb;border-radius:10px;transition:all .2s;background:#fff;cursor:pointer}.column-item:hover{border-color:var(--p-primary-500);background:rgba(var(--p-primary-rgb),.03)}.column-item.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08))}.column-item ::ng-deep .p-checkbox .p-checkbox-box{width:20px;height:20px;border-width:2px}.column-item .column-label{font-size:14px;font-weight:600;color:var(--neutral-color-800);cursor:pointer;margin:0;-webkit-user-select:none;user-select:none}.columns-summary{display:flex;align-items:center;gap:10px;padding:14px 18px;background:linear-gradient(135deg,var(--p-primary-50) 0%,var(--p-primary-100) 100%);border-radius:10px;font-size:14px;color:var(--neutral-color-700);font-weight:600}.columns-summary i{color:var(--p-primary-500);font-size:18px}@media (max-width: 768px){::ng-deep .export-dialog .p-dialog{width:95vw!important;margin:0}.dialog-header{padding:28px 24px}.dialog-header .header-icon-wrapper{width:56px;height:56px}.dialog-header .header-icon-wrapper i{font-size:28px}.dialog-header .header-text h2{font-size:24px}.export-content{padding:24px}.format-options,.columns-grid{grid-template-columns:1fr}}\n"] }]
1465
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1466
+ type: Optional
1467
+ }, {
1468
+ type: Inject,
1469
+ args: ['TranslationService']
1470
+ }] }], propDecorators: { visible: [{
1471
+ type: Input
1472
+ }], allRecords: [{
1473
+ type: Input
1474
+ }], selectedRecords: [{
1475
+ type: Input
1476
+ }], statusLabels: [{
1477
+ type: Input
1478
+ }], translationPrefix: [{
1479
+ type: Input
1480
+ }], availableColumns: [{
1481
+ type: Input
1482
+ }], visibleChange: [{
1483
+ type: Output
1484
+ }] } });
1485
+
1486
+ class BulkDeleteDialogComponent {
1487
+ translationService;
1488
+ visible = false;
1489
+ stages = []; // Generic entities
1490
+ service; // Generic service
1491
+ entityNameField = 'name'; // Field to display entity name
1492
+ entityCodeField = 'code'; // Field to display entity code (optional)
1493
+ translationPrefix = 'crmx.business';
1494
+ visibleChange = new EventEmitter();
1495
+ onComplete = new EventEmitter();
1496
+ deleteStatuses = [];
1497
+ isProcessing = false;
1498
+ isCompleted = false;
1499
+ currentIndex = 0;
1500
+ successCount = 0;
1501
+ errorCount = 0;
1502
+ constructor(translationService) {
1503
+ this.translationService = translationService;
1504
+ }
1505
+ t(key, params) {
1506
+ if (this.translationService) {
1507
+ const fullKey = `${this.translationPrefix}.${key}`;
1508
+ const translated = this.translationService.translate(fullKey, params);
1509
+ // Se params foi fornecido e a tradução contém placeholders, fazer replace manual
1510
+ if (params && translated.includes('{{')) {
1511
+ return Object.keys(params).reduce((text, key) => {
1512
+ return text.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), params[key]);
1513
+ }, translated);
1514
+ }
1515
+ return translated;
1516
+ }
1517
+ return key;
1518
+ }
1519
+ ngOnChanges() {
1520
+ if (this.visible && this.stages.length > 0) {
1521
+ this.initializeStatuses();
1522
+ }
1523
+ }
1524
+ initializeStatuses() {
1525
+ this.deleteStatuses = this.stages.map(entity => ({
1526
+ entity,
1527
+ status: 'pending'
1528
+ }));
1529
+ this.isProcessing = false;
1530
+ this.isCompleted = false;
1531
+ this.currentIndex = 0;
1532
+ this.successCount = 0;
1533
+ this.errorCount = 0;
1534
+ }
1535
+ get progress() {
1536
+ if (this.deleteStatuses.length === 0)
1537
+ return 0;
1538
+ return Math.round((this.currentIndex / this.deleteStatuses.length) * 100);
1539
+ }
1540
+ get canClose() {
1541
+ return !this.isProcessing || this.isCompleted;
1542
+ }
1543
+ startDeletion() {
1544
+ if (!this.service) {
1545
+ console.error('Service not provided to BulkDeleteDialogComponent');
1546
+ return;
1547
+ }
1548
+ this.isProcessing = true;
1549
+ this.isCompleted = false;
1550
+ // Process deletions sequentially
1551
+ from(this.deleteStatuses)
1552
+ .pipe(concatMap((deleteStatus, index) => {
1553
+ this.currentIndex = index;
1554
+ deleteStatus.status = 'processing';
1555
+ return this.service.delete(deleteStatus.entity.id).pipe(concatMap(() => {
1556
+ deleteStatus.status = 'success';
1557
+ this.successCount++;
1558
+ return of(null);
1559
+ }), catchError$1((error) => {
1560
+ // Check if it's actually a success (204 No Content)
1561
+ if (error?.status === 204) {
1562
+ deleteStatus.status = 'success';
1563
+ this.successCount++;
1564
+ }
1565
+ else {
1566
+ deleteStatus.status = 'error';
1567
+ deleteStatus.errorMessage = error?.message || error?.statusText || this.t('bulk_delete_error_generic');
1568
+ this.errorCount++;
1569
+ }
1570
+ return of(null);
1571
+ }));
1572
+ }))
1573
+ .subscribe({
1574
+ complete: () => {
1575
+ this.currentIndex = this.deleteStatuses.length;
1576
+ this.isCompleted = true;
1577
+ this.isProcessing = false;
1578
+ }
1579
+ });
1580
+ }
1581
+ onHide() {
1582
+ if (!this.canClose)
1583
+ return;
1584
+ this.visible = false;
1585
+ this.visibleChange.emit(false);
1586
+ if (this.isCompleted && this.successCount > 0) {
1587
+ this.onComplete.emit();
1588
+ }
1589
+ }
1590
+ onClose() {
1591
+ this.onHide();
1592
+ }
1593
+ getStatusSeverity(status) {
1594
+ switch (status) {
1595
+ case 'pending': return 'secondary';
1596
+ case 'processing': return 'info';
1597
+ case 'success': return 'success';
1598
+ case 'error': return 'danger';
1599
+ default: return 'secondary';
1600
+ }
1601
+ }
1602
+ getStatusLabel(status) {
1603
+ return this.t(`bulk_delete_status_${status}`);
1604
+ }
1605
+ getStatusIcon(status) {
1606
+ switch (status) {
1607
+ case 'pending': return 'pi pi-clock';
1608
+ case 'processing': return 'pi pi-spin pi-spinner';
1609
+ case 'success': return 'pi pi-check';
1610
+ case 'error': return 'pi pi-times';
1611
+ default: return 'pi pi-question';
1612
+ }
1613
+ }
1614
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BulkDeleteDialogComponent, deps: [{ token: 'TranslationService', optional: true }], target: i0.ɵɵFactoryTarget.Component });
1615
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: BulkDeleteDialogComponent, isStandalone: true, selector: "sia-bulk-delete-dialog", inputs: { visible: "visible", stages: "stages", service: "service", entityNameField: "entityNameField", entityCodeField: "entityCodeField", translationPrefix: "translationPrefix" }, outputs: { visibleChange: "visibleChange", onComplete: "onComplete" }, usesOnChanges: true, ngImport: i0, template: "<p-dialog\n [(visible)]=\"visible\"\n [modal]=\"true\"\n [closable]=\"canClose\"\n [closeOnEscape]=\"canClose\"\n [dismissableMask]=\"false\"\n [style]=\"{ width: '700px', maxWidth: '90vw' }\"\n (onHide)=\"onHide()\"\n>\n <ng-template pTemplate=\"header\">\n <div class=\"dialog-header\">\n <i class=\"pi pi-trash header-icon\"></i>\n <h2>{{ t('bulk_delete_title') }}</h2>\n </div>\n </ng-template>\n\n <div class=\"dialog-content\">\n <!-- Summary -->\n @if (!isProcessing && !isCompleted) {\n <div class=\"summary-section\">\n <p class=\"summary-text\">\n <i class=\"pi pi-exclamation-triangle warning-icon\"></i>\n <span [innerHTML]=\"t('bulk_delete_warning', { count: stages.length })\"></span>\n </p>\n <p class=\"summary-description\">\n {{ t('bulk_delete_description') }}\n </p>\n </div>\n }\n\n <!-- Progress Bar -->\n @if (isProcessing || isCompleted) {\n <div class=\"progress-section\">\n <div class=\"progress-info\">\n <span class=\"progress-label\">{{ t('bulk_delete_progress') }}</span>\n <span class=\"progress-count\">{{ currentIndex }} / {{ deleteStatuses.length }}</span>\n </div>\n <p-progressBar \n [value]=\"progress\" \n [showValue]=\"false\"\n styleClass=\"custom-progress\"\n ></p-progressBar>\n \n <div class=\"progress-stats\">\n <div class=\"stat success\">\n <i class=\"pi pi-check-circle\"></i>\n <span>{{ successCount }} {{ t('bulk_delete_success_count') }}</span>\n </div>\n @if (errorCount > 0) {\n <div class=\"stat error\">\n <i class=\"pi pi-times-circle\"></i>\n <span>{{ errorCount }} {{ t('bulk_delete_error_count') }}</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Status List -->\n @if (isProcessing || isCompleted) {\n <div class=\"status-list\">\n @for (item of deleteStatuses; track item.entity) {\n <div class=\"status-item\">\n <div class=\"status-info\">\n <i [class]=\"getStatusIcon(item.status)\" class=\"status-icon\"></i>\n <div class=\"status-details\">\n <span class=\"stage-name\">{{ item.entity[entityNameField] }}</span>\n @if (item.entity[entityCodeField]) {\n <span class=\"stage-code\">{{ t('bulk_delete_code_label') }}: {{ item.entity[entityCodeField] }}</span>\n }\n </div>\n </div>\n <div class=\"status-badge\">\n <p-tag \n [value]=\"getStatusLabel(item.status)\" \n [severity]=\"getStatusSeverity(item.status)\"\n >\n @if (item.status === 'processing') {\n <i class=\"pi pi-spin pi-spinner\"></i>\n }\n </p-tag>\n @if (item.status === 'error' && item.errorMessage) {\n <small class=\"error-message\">\n {{ item.errorMessage }}\n </small>\n }\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Completion Message -->\n @if (isCompleted) {\n <div class=\"completion-section\">\n <div class=\"completion-message\" [class.has-errors]=\"errorCount > 0\">\n <i [class]=\"errorCount > 0 ? 'pi pi-exclamation-circle' : 'pi pi-check-circle'\" class=\"completion-icon\"></i>\n @if (errorCount === 0) {\n <p>\n {{ t('bulk_delete_success_message') }}\n </p>\n }\n @if (errorCount > 0) {\n <p>\n {{ t('bulk_delete_completed_with_errors', { count: errorCount }) }}\n </p>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n @if (!isProcessing && !isCompleted) {\n <p-button\n [label]=\"t('cancel')\"\n icon=\"pi pi-times\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"onClose()\"\n ></p-button>\n <p-button\n [label]=\"t('bulk_delete_confirm')\"\n icon=\"pi pi-trash\"\n severity=\"danger\"\n (onClick)=\"startDeletion()\"\n ></p-button>\n }\n @if (isCompleted) {\n <p-button\n [label]=\"t('close')\"\n icon=\"pi pi-check\"\n (onClick)=\"onClose()\"\n ></p-button>\n }\n </div>\n </ng-template>\n</p-dialog>\n", styles: [".dialog-header{display:flex;align-items:center;gap:12px}.dialog-header .header-icon{font-size:24px;color:#ef4444}.dialog-header h2{margin:0;font-size:20px;font-weight:700;color:var(--neutral-color-800)}.dialog-content{padding:8px 0}.summary-section{padding:20px;background:linear-gradient(135deg,#fef3c7,#fde68a);border-radius:12px;border:1px solid #fbbf24;margin-bottom:24px}.summary-section .summary-text{display:flex;align-items:center;gap:12px;margin:0 0 12px;font-size:16px;color:var(--neutral-color-800)}.summary-section .summary-text .warning-icon{font-size:24px;color:#f59e0b}.summary-section .summary-text strong{color:#d97706}.summary-section .summary-description{margin:0;font-size:14px;color:var(--neutral-color-600);padding-left:36px}.progress-section{margin-bottom:24px}.progress-section .progress-info{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.progress-section .progress-info .progress-label{font-weight:600;font-size:14px;color:var(--neutral-color-800)}.progress-section .progress-info .progress-count{font-size:14px;color:var(--neutral-color-600);font-weight:500}.progress-section .progress-stats{display:flex;gap:20px;margin-top:12px}.progress-section .progress-stats .stat{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:500}.progress-section .progress-stats .stat.success{color:#10b981}.progress-section .progress-stats .stat.success i{font-size:18px}.progress-section .progress-stats .stat.error{color:#ef4444}.progress-section .progress-stats .stat.error i{font-size:18px}.status-list{max-height:400px;overflow-y:auto;border:1px solid var(--p-surface-200);border-radius:12px;padding:12px;background-color:#fafafa}.status-list .status-item{display:flex;justify-content:space-between;align-items:center;padding:12px;background-color:#fff;border-radius:8px;margin-bottom:8px;transition:all .2s ease}.status-list .status-item:last-child{margin-bottom:0}.status-list .status-item:hover{box-shadow:0 2px 8px #00000014}.status-list .status-item .status-info{display:flex;align-items:center;gap:12px;flex:1}.status-list .status-item .status-info .status-icon{font-size:20px}.status-list .status-item .status-info .status-icon.pi-clock{color:var(--neutral-color-400)}.status-list .status-item .status-info .status-icon.pi-spinner{color:#3b82f6}.status-list .status-item .status-info .status-icon.pi-check{color:#10b981}.status-list .status-item .status-info .status-icon.pi-times{color:#ef4444}.status-list .status-item .status-info .status-details{display:flex;flex-direction:column;gap:4px}.status-list .status-item .status-info .status-details .stage-name{font-weight:600;font-size:14px;color:var(--neutral-color-800)}.status-list .status-item .status-info .status-details .stage-code{font-size:12px;color:var(--neutral-color-500)}.status-list .status-item .status-badge{display:flex;flex-direction:column;align-items:flex-end;gap:4px}.status-list .status-item .status-badge .error-message{font-size:11px;color:#ef4444;max-width:200px;text-align:right}.completion-section{margin-top:24px}.completion-section .completion-message{padding:20px;background:linear-gradient(135deg,#d1fae5,#a7f3d0);border-radius:12px;border:1px solid #10b981;display:flex;align-items:center;gap:16px}.completion-section .completion-message.has-errors{background:linear-gradient(135deg,#fee2e2,#fecaca);border-color:#ef4444}.completion-section .completion-message.has-errors .completion-icon{color:#ef4444}.completion-section .completion-message .completion-icon{font-size:32px;color:#10b981}.completion-section .completion-message p{margin:0;font-size:16px;font-weight:600;color:var(--neutral-color-800)}.dialog-footer{display:flex;justify-content:flex-end;gap:12px}::ng-deep .custom-progress{height:12px;border-radius:6px}::ng-deep .custom-progress .p-progressbar-value{background:linear-gradient(90deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);border-radius:6px}.status-list::-webkit-scrollbar{width:8px}.status-list::-webkit-scrollbar-track{background:var(--p-surface-100);border-radius:4px}.status-list::-webkit-scrollbar-thumb{background:var(--neutral-color-400);border-radius:4px}.status-list::-webkit-scrollbar-thumb:hover{background:var(--neutral-color-500)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i5.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i5$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i4.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i5$2.Tag, selector: "p-tag", inputs: ["style", "styleClass", "severity", "value", "icon", "rounded"] }] });
1616
+ }
1617
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BulkDeleteDialogComponent, decorators: [{
1618
+ type: Component,
1619
+ args: [{ selector: 'sia-bulk-delete-dialog', standalone: true, imports: [
1620
+ CommonModule,
1621
+ DialogModule,
1622
+ ButtonModule,
1623
+ ProgressBarModule,
1624
+ TagModule
1625
+ ], template: "<p-dialog\n [(visible)]=\"visible\"\n [modal]=\"true\"\n [closable]=\"canClose\"\n [closeOnEscape]=\"canClose\"\n [dismissableMask]=\"false\"\n [style]=\"{ width: '700px', maxWidth: '90vw' }\"\n (onHide)=\"onHide()\"\n>\n <ng-template pTemplate=\"header\">\n <div class=\"dialog-header\">\n <i class=\"pi pi-trash header-icon\"></i>\n <h2>{{ t('bulk_delete_title') }}</h2>\n </div>\n </ng-template>\n\n <div class=\"dialog-content\">\n <!-- Summary -->\n @if (!isProcessing && !isCompleted) {\n <div class=\"summary-section\">\n <p class=\"summary-text\">\n <i class=\"pi pi-exclamation-triangle warning-icon\"></i>\n <span [innerHTML]=\"t('bulk_delete_warning', { count: stages.length })\"></span>\n </p>\n <p class=\"summary-description\">\n {{ t('bulk_delete_description') }}\n </p>\n </div>\n }\n\n <!-- Progress Bar -->\n @if (isProcessing || isCompleted) {\n <div class=\"progress-section\">\n <div class=\"progress-info\">\n <span class=\"progress-label\">{{ t('bulk_delete_progress') }}</span>\n <span class=\"progress-count\">{{ currentIndex }} / {{ deleteStatuses.length }}</span>\n </div>\n <p-progressBar \n [value]=\"progress\" \n [showValue]=\"false\"\n styleClass=\"custom-progress\"\n ></p-progressBar>\n \n <div class=\"progress-stats\">\n <div class=\"stat success\">\n <i class=\"pi pi-check-circle\"></i>\n <span>{{ successCount }} {{ t('bulk_delete_success_count') }}</span>\n </div>\n @if (errorCount > 0) {\n <div class=\"stat error\">\n <i class=\"pi pi-times-circle\"></i>\n <span>{{ errorCount }} {{ t('bulk_delete_error_count') }}</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Status List -->\n @if (isProcessing || isCompleted) {\n <div class=\"status-list\">\n @for (item of deleteStatuses; track item.entity) {\n <div class=\"status-item\">\n <div class=\"status-info\">\n <i [class]=\"getStatusIcon(item.status)\" class=\"status-icon\"></i>\n <div class=\"status-details\">\n <span class=\"stage-name\">{{ item.entity[entityNameField] }}</span>\n @if (item.entity[entityCodeField]) {\n <span class=\"stage-code\">{{ t('bulk_delete_code_label') }}: {{ item.entity[entityCodeField] }}</span>\n }\n </div>\n </div>\n <div class=\"status-badge\">\n <p-tag \n [value]=\"getStatusLabel(item.status)\" \n [severity]=\"getStatusSeverity(item.status)\"\n >\n @if (item.status === 'processing') {\n <i class=\"pi pi-spin pi-spinner\"></i>\n }\n </p-tag>\n @if (item.status === 'error' && item.errorMessage) {\n <small class=\"error-message\">\n {{ item.errorMessage }}\n </small>\n }\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Completion Message -->\n @if (isCompleted) {\n <div class=\"completion-section\">\n <div class=\"completion-message\" [class.has-errors]=\"errorCount > 0\">\n <i [class]=\"errorCount > 0 ? 'pi pi-exclamation-circle' : 'pi pi-check-circle'\" class=\"completion-icon\"></i>\n @if (errorCount === 0) {\n <p>\n {{ t('bulk_delete_success_message') }}\n </p>\n }\n @if (errorCount > 0) {\n <p>\n {{ t('bulk_delete_completed_with_errors', { count: errorCount }) }}\n </p>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n @if (!isProcessing && !isCompleted) {\n <p-button\n [label]=\"t('cancel')\"\n icon=\"pi pi-times\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"onClose()\"\n ></p-button>\n <p-button\n [label]=\"t('bulk_delete_confirm')\"\n icon=\"pi pi-trash\"\n severity=\"danger\"\n (onClick)=\"startDeletion()\"\n ></p-button>\n }\n @if (isCompleted) {\n <p-button\n [label]=\"t('close')\"\n icon=\"pi pi-check\"\n (onClick)=\"onClose()\"\n ></p-button>\n }\n </div>\n </ng-template>\n</p-dialog>\n", styles: [".dialog-header{display:flex;align-items:center;gap:12px}.dialog-header .header-icon{font-size:24px;color:#ef4444}.dialog-header h2{margin:0;font-size:20px;font-weight:700;color:var(--neutral-color-800)}.dialog-content{padding:8px 0}.summary-section{padding:20px;background:linear-gradient(135deg,#fef3c7,#fde68a);border-radius:12px;border:1px solid #fbbf24;margin-bottom:24px}.summary-section .summary-text{display:flex;align-items:center;gap:12px;margin:0 0 12px;font-size:16px;color:var(--neutral-color-800)}.summary-section .summary-text .warning-icon{font-size:24px;color:#f59e0b}.summary-section .summary-text strong{color:#d97706}.summary-section .summary-description{margin:0;font-size:14px;color:var(--neutral-color-600);padding-left:36px}.progress-section{margin-bottom:24px}.progress-section .progress-info{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.progress-section .progress-info .progress-label{font-weight:600;font-size:14px;color:var(--neutral-color-800)}.progress-section .progress-info .progress-count{font-size:14px;color:var(--neutral-color-600);font-weight:500}.progress-section .progress-stats{display:flex;gap:20px;margin-top:12px}.progress-section .progress-stats .stat{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:500}.progress-section .progress-stats .stat.success{color:#10b981}.progress-section .progress-stats .stat.success i{font-size:18px}.progress-section .progress-stats .stat.error{color:#ef4444}.progress-section .progress-stats .stat.error i{font-size:18px}.status-list{max-height:400px;overflow-y:auto;border:1px solid var(--p-surface-200);border-radius:12px;padding:12px;background-color:#fafafa}.status-list .status-item{display:flex;justify-content:space-between;align-items:center;padding:12px;background-color:#fff;border-radius:8px;margin-bottom:8px;transition:all .2s ease}.status-list .status-item:last-child{margin-bottom:0}.status-list .status-item:hover{box-shadow:0 2px 8px #00000014}.status-list .status-item .status-info{display:flex;align-items:center;gap:12px;flex:1}.status-list .status-item .status-info .status-icon{font-size:20px}.status-list .status-item .status-info .status-icon.pi-clock{color:var(--neutral-color-400)}.status-list .status-item .status-info .status-icon.pi-spinner{color:#3b82f6}.status-list .status-item .status-info .status-icon.pi-check{color:#10b981}.status-list .status-item .status-info .status-icon.pi-times{color:#ef4444}.status-list .status-item .status-info .status-details{display:flex;flex-direction:column;gap:4px}.status-list .status-item .status-info .status-details .stage-name{font-weight:600;font-size:14px;color:var(--neutral-color-800)}.status-list .status-item .status-info .status-details .stage-code{font-size:12px;color:var(--neutral-color-500)}.status-list .status-item .status-badge{display:flex;flex-direction:column;align-items:flex-end;gap:4px}.status-list .status-item .status-badge .error-message{font-size:11px;color:#ef4444;max-width:200px;text-align:right}.completion-section{margin-top:24px}.completion-section .completion-message{padding:20px;background:linear-gradient(135deg,#d1fae5,#a7f3d0);border-radius:12px;border:1px solid #10b981;display:flex;align-items:center;gap:16px}.completion-section .completion-message.has-errors{background:linear-gradient(135deg,#fee2e2,#fecaca);border-color:#ef4444}.completion-section .completion-message.has-errors .completion-icon{color:#ef4444}.completion-section .completion-message .completion-icon{font-size:32px;color:#10b981}.completion-section .completion-message p{margin:0;font-size:16px;font-weight:600;color:var(--neutral-color-800)}.dialog-footer{display:flex;justify-content:flex-end;gap:12px}::ng-deep .custom-progress{height:12px;border-radius:6px}::ng-deep .custom-progress .p-progressbar-value{background:linear-gradient(90deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);border-radius:6px}.status-list::-webkit-scrollbar{width:8px}.status-list::-webkit-scrollbar-track{background:var(--p-surface-100);border-radius:4px}.status-list::-webkit-scrollbar-thumb{background:var(--neutral-color-400);border-radius:4px}.status-list::-webkit-scrollbar-thumb:hover{background:var(--neutral-color-500)}\n"] }]
1626
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1627
+ type: Optional
1628
+ }, {
1629
+ type: Inject,
1630
+ args: ['TranslationService']
1631
+ }] }], propDecorators: { visible: [{
1632
+ type: Input
1633
+ }], stages: [{
1634
+ type: Input
1635
+ }], service: [{
1636
+ type: Input
1637
+ }], entityNameField: [{
1638
+ type: Input
1639
+ }], entityCodeField: [{
1640
+ type: Input
1641
+ }], translationPrefix: [{
1642
+ type: Input
1643
+ }], visibleChange: [{
1644
+ type: Output
1645
+ }], onComplete: [{
1646
+ type: Output
1647
+ }] } });
1648
+
1649
+ class BreadcrumbComponent {
1650
+ router;
1651
+ activatedRoute;
1652
+ translationService;
1653
+ translationPrefix = 'crmx.business';
1654
+ isProduction = false;
1655
+ breadcrumbItems = [];
1656
+ constructor(router, activatedRoute, translationService) {
1657
+ this.router = router;
1658
+ this.activatedRoute = activatedRoute;
1659
+ this.translationService = translationService;
1660
+ }
1661
+ ngOnInit() {
1662
+ this.buildBreadcrumbs();
1663
+ // Atualiza breadcrumbs quando a navegação muda
1664
+ this.router.events
1665
+ .pipe(filter(event => event instanceof NavigationEnd))
1666
+ .subscribe(() => this.buildBreadcrumbs());
1667
+ }
1668
+ get homeItem() {
1669
+ // Só mostra o link do home em desenvolvimento
1670
+ if (!this.isProduction) {
1671
+ return {
1672
+ icon: 'pi pi-home',
1673
+ routerLink: '/main',
1674
+ title: this.t('breadcrumb_home')
1675
+ };
1676
+ }
1677
+ // Em produção, o home não tem link (apenas ícone)
1678
+ return {
1679
+ icon: 'pi pi-home',
1680
+ title: this.t('breadcrumb_home'),
1681
+ disabled: true
1682
+ };
1683
+ }
1684
+ buildBreadcrumbs() {
1685
+ const breadcrumbs = [];
1686
+ let currentRoute = this.activatedRoute.root;
1687
+ let url = '';
1688
+ let lastBreadcrumbLabel = '';
1689
+ // Percorre toda a árvore de rotas
1690
+ while (currentRoute) {
1691
+ // Verifica se há segmentos de URL
1692
+ if (currentRoute.snapshot.url.length > 0) {
1693
+ const routeURL = currentRoute.snapshot.url.map(segment => segment.path).join('/');
1694
+ url += `/${routeURL}`;
1695
+ }
1696
+ // Verifica se a rota tem dados de breadcrumb
1697
+ const breadcrumbData = currentRoute.snapshot.data['breadcrumb'];
1698
+ // Adiciona o breadcrumb se existir e não for duplicado
1699
+ if (breadcrumbData) {
1700
+ const label = this.t(breadcrumbData.title);
1701
+ // Só adiciona se não for o mesmo label do anterior (evita duplicação)
1702
+ if (label !== lastBreadcrumbLabel) {
1703
+ breadcrumbs.push({
1704
+ label: label,
1705
+ icon: breadcrumbData.icon,
1706
+ routerLink: url || undefined
1707
+ });
1708
+ lastBreadcrumbLabel = label;
1709
+ }
1710
+ }
1711
+ // Move para a próxima rota filha
1712
+ currentRoute = currentRoute.firstChild;
1713
+ }
1714
+ // O último item não deve ter link (está desabilitado)
1715
+ if (breadcrumbs.length > 0) {
1716
+ const lastItem = breadcrumbs[breadcrumbs.length - 1];
1717
+ lastItem.routerLink = undefined;
1718
+ lastItem.disabled = true;
1719
+ }
1720
+ this.breadcrumbItems = breadcrumbs;
1721
+ }
1722
+ t(key) {
1723
+ if (this.translationService) {
1724
+ // Se a chave já tem o prefixo, usa diretamente
1725
+ if (key.includes('.')) {
1726
+ return this.translationService.translate(key);
1727
+ }
1728
+ // Caso contrário, adiciona o prefixo
1729
+ return this.translationService.translate(`${this.translationPrefix}.${key}`);
1730
+ }
1731
+ return key;
1732
+ }
1733
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbComponent, deps: [{ token: i1$1.Router }, { token: i1$1.ActivatedRoute }, { token: 'TranslationService', optional: true }], target: i0.ɵɵFactoryTarget.Component });
1734
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: BreadcrumbComponent, isStandalone: true, selector: "sia-breadcrumb", inputs: { translationPrefix: "translationPrefix", isProduction: "isProduction" }, ngImport: i0, template: `
1735
+ <p-breadcrumb
1736
+ [model]="breadcrumbItems"
1737
+ [home]="homeItem"
1738
+ styleClass="custom-breadcrumb"
1739
+ ></p-breadcrumb>
1740
+ `, isInline: true, styles: ["::ng-deep .p-breadcrumb{background:transparent;border:none;padding:0!important}::ng-deep .custom-breadcrumb{background:transparent;border:none;padding:0!important;.p-breadcrumb-list{background:transparent;padding:0!important;.p-menuitem{.p-menuitem-link{color:var(--text-secondary);text-decoration:none;font-size:14px;font-weight:500;padding:8px 12px;border-radius:6px;transition:all .2s ease;display:flex;align-items:center;&:hover{background:var(--p-primary-50);color:var(--p-primary-600)}.p-menuitem-icon{margin-right:8px;font-size:16px;display:inline-flex;align-items:center}}&:last-child .p-menuitem-link{color:var(--p-primary-600);font-weight:600;background:var(--p-primary-50)}}.p-breadcrumb-separator{color:var(--text-muted);margin:0 8px;font-size:14px}}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: BreadcrumbModule }, { kind: "component", type: i2$2.Breadcrumb, selector: "p-breadcrumb", inputs: ["model", "style", "styleClass", "home", "homeAriaLabel"], outputs: ["onItemClick"] }] });
1741
+ }
1742
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbComponent, decorators: [{
1743
+ type: Component,
1744
+ args: [{ selector: 'sia-breadcrumb', standalone: true, imports: [CommonModule, RouterModule, BreadcrumbModule], template: `
1745
+ <p-breadcrumb
1746
+ [model]="breadcrumbItems"
1747
+ [home]="homeItem"
1748
+ styleClass="custom-breadcrumb"
1749
+ ></p-breadcrumb>
1750
+ `, styles: ["::ng-deep .p-breadcrumb{background:transparent;border:none;padding:0!important}::ng-deep .custom-breadcrumb{background:transparent;border:none;padding:0!important;.p-breadcrumb-list{background:transparent;padding:0!important;.p-menuitem{.p-menuitem-link{color:var(--text-secondary);text-decoration:none;font-size:14px;font-weight:500;padding:8px 12px;border-radius:6px;transition:all .2s ease;display:flex;align-items:center;&:hover{background:var(--p-primary-50);color:var(--p-primary-600)}.p-menuitem-icon{margin-right:8px;font-size:16px;display:inline-flex;align-items:center}}&:last-child .p-menuitem-link{color:var(--p-primary-600);font-weight:600;background:var(--p-primary-50)}}.p-breadcrumb-separator{color:var(--text-muted);margin:0 8px;font-size:14px}}}\n"] }]
1751
+ }], ctorParameters: () => [{ type: i1$1.Router }, { type: i1$1.ActivatedRoute }, { type: undefined, decorators: [{
1752
+ type: Optional
1753
+ }, {
1754
+ type: Inject,
1755
+ args: ['TranslationService']
1756
+ }] }], propDecorators: { translationPrefix: [{
1757
+ type: Input
1758
+ }], isProduction: [{
1759
+ type: Input
1760
+ }] } });
1761
+
1762
+ class TranslatePipe {
1763
+ translationService;
1764
+ cdr;
1765
+ subscription;
1766
+ lastKey;
1767
+ lastParams;
1768
+ lastTranslation;
1769
+ isLoading = false;
1770
+ constructor(translationService, cdr) {
1771
+ this.translationService = translationService;
1772
+ this.cdr = cdr;
1773
+ }
1774
+ transform(key, params) {
1775
+ if (!key)
1776
+ return '';
1777
+ // Check if we need to update the translation
1778
+ if (this.shouldUpdate(key, params)) {
1779
+ this.updateTranslation(key, params);
1780
+ }
1781
+ return this.lastTranslation || key;
1782
+ }
1783
+ ngOnDestroy() {
1784
+ if (this.subscription) {
1785
+ this.subscription.unsubscribe();
1786
+ }
1787
+ }
1788
+ shouldUpdate(key, params) {
1789
+ return this.lastKey !== key ||
1790
+ JSON.stringify(this.lastParams) !== JSON.stringify(params) ||
1791
+ !this.subscription;
1792
+ }
1793
+ updateTranslation(key, params) {
1794
+ this.lastKey = key;
1795
+ this.lastParams = params;
1796
+ if (this.subscription) {
1797
+ this.subscription.unsubscribe();
1798
+ }
1799
+ // Aguardar carregamento das traduções se necessário
1800
+ if (!this.translationService.isLanguageLoaded()) {
1801
+ if (!this.isLoading) {
1802
+ this.isLoading = true;
1803
+ this.translationService.waitForTranslations().then(() => {
1804
+ this.isLoading = false;
1805
+ this.lastTranslation = this.translationService.translate(key, params);
1806
+ this.cdr.markForCheck();
1807
+ }).catch(error => {
1808
+ this.isLoading = false;
1809
+ console.error('Error waiting for translations:', error);
1810
+ this.lastTranslation = key;
1811
+ this.cdr.markForCheck();
1812
+ });
1813
+ }
1814
+ return;
1815
+ }
1816
+ this.subscription = this.translationService.currentLanguage$.subscribe(() => {
1817
+ this.lastTranslation = this.translationService.translate(key, params);
1818
+ this.cdr.markForCheck();
1819
+ });
1820
+ }
1821
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TranslatePipe, deps: [{ token: TranslationService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Pipe });
1822
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: TranslatePipe, isStandalone: true, name: "translate", pure: false });
1823
+ }
1824
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TranslatePipe, decorators: [{
1825
+ type: Pipe,
1826
+ args: [{
1827
+ name: 'translate',
1828
+ pure: false,
1829
+ standalone: true
1830
+ }]
1831
+ }], ctorParameters: () => [{ type: TranslationService }, { type: i0.ChangeDetectorRef }] });
1832
+
1833
+ class CepMaskDirective {
1834
+ el;
1835
+ constructor(el) {
1836
+ this.el = el;
1837
+ }
1838
+ onKeyPress(event) {
1839
+ // Permite apenas números
1840
+ const charCode = event.which ? event.which : event.keyCode;
1841
+ if (charCode > 31 && (charCode < 48 || charCode > 57)) {
1842
+ event.preventDefault();
1843
+ return false;
1844
+ }
1845
+ return true;
1846
+ }
1847
+ onInput(event) {
1848
+ const value = event.target.value;
1849
+ this.applyCepMask(value);
1850
+ }
1851
+ onBlur(event) {
1852
+ const value = event.target.value;
1853
+ this.applyCepMask(value);
1854
+ }
1855
+ onPaste(event) {
1856
+ event.preventDefault();
1857
+ const pastedText = event.clipboardData?.getData('text') || '';
1858
+ const numbers = pastedText.replace(/\D/g, '');
1859
+ this.applyCepMask(numbers);
1860
+ }
1861
+ applyCepMask(value) {
1862
+ if (!value) {
1863
+ this.el.nativeElement.value = '';
1864
+ return;
1865
+ }
1866
+ // Remove todos os caracteres não numéricos
1867
+ const numbers = value.replace(/\D/g, '');
1868
+ // Limita a 8 dígitos
1869
+ const limitedNumbers = numbers.substring(0, 8);
1870
+ // Aplica a máscara 00000-000
1871
+ let maskedValue = '';
1872
+ if (limitedNumbers.length === 0) {
1873
+ maskedValue = '';
1874
+ }
1875
+ else if (limitedNumbers.length <= 5) {
1876
+ maskedValue = limitedNumbers;
1877
+ }
1878
+ else {
1879
+ maskedValue = limitedNumbers.replace(/(\d{5})(\d{0,3})/, '$1-$2');
1880
+ }
1881
+ // Atualizar o valor do input
1882
+ this.el.nativeElement.value = maskedValue;
1883
+ // Disparar evento de input para atualizar o FormControl
1884
+ const inputEvent = new Event('input', { bubbles: true });
1885
+ this.el.nativeElement.dispatchEvent(inputEvent);
1886
+ }
1887
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CepMaskDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
1888
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: CepMaskDirective, isStandalone: true, selector: "[siaCepMask]", host: { listeners: { "keypress": "onKeyPress($event)", "input": "onInput($event)", "blur": "onBlur($event)", "paste": "onPaste($event)" } }, ngImport: i0 });
1889
+ }
1890
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CepMaskDirective, decorators: [{
1891
+ type: Directive,
1892
+ args: [{
1893
+ selector: '[siaCepMask]',
1894
+ standalone: true
1895
+ }]
1896
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { onKeyPress: [{
1897
+ type: HostListener,
1898
+ args: ['keypress', ['$event']]
1899
+ }], onInput: [{
1900
+ type: HostListener,
1901
+ args: ['input', ['$event']]
1902
+ }], onBlur: [{
1903
+ type: HostListener,
1904
+ args: ['blur', ['$event']]
1905
+ }], onPaste: [{
1906
+ type: HostListener,
1907
+ args: ['paste', ['$event']]
1908
+ }] } });
1909
+
1910
+ class PhoneMaskDirective {
1911
+ el;
1912
+ constructor(el) {
1913
+ this.el = el;
1914
+ }
1915
+ onInput(event) {
1916
+ const value = event.target.value;
1917
+ this.applyPhoneMask(value);
1918
+ }
1919
+ onBlur(event) {
1920
+ const value = event.target.value;
1921
+ this.applyPhoneMask(value);
1922
+ }
1923
+ applyPhoneMask(value) {
1924
+ if (!value)
1925
+ return;
1926
+ // Remove todos os caracteres não numéricos
1927
+ const numbers = value.replace(/\D/g, '');
1928
+ let maskedValue = '';
1929
+ if (numbers.length <= 10) {
1930
+ // Telefone fixo: (00) 0000-0000
1931
+ maskedValue = numbers.replace(/(\d{2})(\d{4})(\d{4})/, '($1) $2-$3');
1932
+ }
1933
+ else if (numbers.length === 11) {
1934
+ // Celular: (00) 00000-0000
1935
+ maskedValue = numbers.replace(/(\d{2})(\d{5})(\d{4})/, '($1) $2-$3');
1936
+ }
1937
+ else {
1938
+ // Limitar a 11 dígitos
1939
+ const limitedNumbers = numbers.substring(0, 11);
1940
+ maskedValue = limitedNumbers.replace(/(\d{2})(\d{5})(\d{4})/, '($1) $2-$3');
1941
+ }
1942
+ // Atualizar o valor do input apenas se mudou
1943
+ if (maskedValue && maskedValue !== value) {
1944
+ this.el.nativeElement.value = maskedValue;
1945
+ // Disparar evento de input para atualizar o FormControl
1946
+ const inputEvent = new Event('input', { bubbles: true });
1947
+ this.el.nativeElement.dispatchEvent(inputEvent);
1948
+ }
1949
+ }
1950
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PhoneMaskDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
1951
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: PhoneMaskDirective, isStandalone: true, selector: "[siaPhoneMask]", host: { listeners: { "input": "onInput($event)", "blur": "onBlur($event)" } }, ngImport: i0 });
1952
+ }
1953
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PhoneMaskDirective, decorators: [{
1954
+ type: Directive,
1955
+ args: [{
1956
+ selector: '[siaPhoneMask]',
1957
+ standalone: true
1958
+ }]
1959
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { onInput: [{
1960
+ type: HostListener,
1961
+ args: ['input', ['$event']]
1962
+ }], onBlur: [{
1963
+ type: HostListener,
1964
+ args: ['blur', ['$event']]
1965
+ }] } });
1966
+
1967
+ class MaskService {
1968
+ /**
1969
+ * Aplica máscara de CPF (999.999.999-99) - 100% numérico
1970
+ */
1971
+ applyCpfMask(value) {
1972
+ if (!value)
1973
+ return '';
1974
+ // Remove todos os caracteres não numéricos
1975
+ const numbers = value.replace(/\D/g, '');
1976
+ // Limita a 11 dígitos
1977
+ const limitedNumbers = numbers.substring(0, 11);
1978
+ // Aplica a máscara progressivamente
1979
+ if (limitedNumbers.length <= 3) {
1980
+ return limitedNumbers;
1981
+ }
1982
+ else if (limitedNumbers.length <= 6) {
1983
+ return limitedNumbers.replace(/(\d{3})(\d+)/, '$1.$2');
1984
+ }
1985
+ else if (limitedNumbers.length <= 9) {
1986
+ return limitedNumbers.replace(/(\d{3})(\d{3})(\d+)/, '$1.$2.$3');
1987
+ }
1988
+ else {
1989
+ return limitedNumbers.replace(/(\d{3})(\d{3})(\d{3})(\d+)/, '$1.$2.$3-$4');
1990
+ }
1991
+ }
1992
+ /**
1993
+ * Aplica máscara de CNPJ alfanumérico (XX.XXX.XXX/XXXX-XX)
1994
+ * Aceita letras e números em todos os caracteres
1995
+ */
1996
+ applyCnpjMask(value) {
1997
+ if (!value)
1998
+ return '';
1999
+ // Remove caracteres especiais, mantém letras e números
2000
+ const cleaned = value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
2001
+ // Limita a 14 caracteres
2002
+ const limitedChars = cleaned.substring(0, 14);
2003
+ // Aplica a máscara progressivamente
2004
+ if (limitedChars.length <= 2) {
2005
+ return limitedChars;
2006
+ }
2007
+ else if (limitedChars.length <= 5) {
2008
+ return limitedChars.replace(/(\w{2})(\w+)/, '$1.$2');
2009
+ }
2010
+ else if (limitedChars.length <= 8) {
2011
+ return limitedChars.replace(/(\w{2})(\w{3})(\w+)/, '$1.$2.$3');
2012
+ }
2013
+ else if (limitedChars.length <= 12) {
2014
+ return limitedChars.replace(/(\w{2})(\w{3})(\w{3})(\w+)/, '$1.$2.$3/$4');
2015
+ }
2016
+ else {
2017
+ return limitedChars.replace(/(\w{2})(\w{3})(\w{3})(\w{4})(\w+)/, '$1.$2.$3/$4-$5');
2018
+ }
2019
+ }
2020
+ /**
2021
+ * Aplica a máscara apropriada baseada no tipo de pessoa
2022
+ * CPF: 100% numérico (999.999.999-99)
2023
+ * CNPJ: alfanumérico (XX.XXX.XXX/XXXX-XX)
2024
+ */
2025
+ applyDocumentMask(value, typePerson) {
2026
+ if (!value)
2027
+ return '';
2028
+ if (typePerson === 'NATURAL_PERSON') {
2029
+ return this.applyCpfMask(value);
2030
+ }
2031
+ else {
2032
+ return this.applyCnpjMask(value);
2033
+ }
2034
+ }
2035
+ /**
2036
+ * Remove a máscara de um documento (mantém apenas alfanuméricos)
2037
+ */
2038
+ removeMask(value) {
2039
+ if (!value)
2040
+ return '';
2041
+ return value.replace(/[^a-zA-Z0-9]/g, '');
2042
+ }
2043
+ /**
2044
+ * Determina se um documento é CPF ou CNPJ baseado no comprimento
2045
+ */
2046
+ getDocumentType(value) {
2047
+ if (!value)
2048
+ return 'unknown';
2049
+ const cleaned = this.removeMask(value);
2050
+ if (cleaned.length <= 11) {
2051
+ return 'cpf';
2052
+ }
2053
+ else if (cleaned.length <= 14) {
2054
+ return 'cnpj';
2055
+ }
2056
+ return 'unknown';
2057
+ }
2058
+ /**
2059
+ * Valida se um CPF tem formato válido (11 dígitos numéricos)
2060
+ */
2061
+ isValidCpfFormat(value) {
2062
+ if (!value)
2063
+ return false;
2064
+ const cleaned = value.replace(/\D/g, '');
2065
+ return cleaned.length === 11 && /^\d{11}$/.test(cleaned);
2066
+ }
2067
+ /**
2068
+ * Valida se um CNPJ tem formato válido (14 caracteres alfanuméricos)
2069
+ */
2070
+ isValidCnpjFormat(value) {
2071
+ if (!value)
2072
+ return false;
2073
+ const cleaned = this.removeMask(value);
2074
+ return cleaned.length === 14;
2075
+ }
2076
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaskService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2077
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaskService, providedIn: 'root' });
2078
+ }
2079
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaskService, decorators: [{
2080
+ type: Injectable,
2081
+ args: [{
2082
+ providedIn: 'root'
2083
+ }]
2084
+ }] });
2085
+
2086
+ class DocumentMaskDirective {
2087
+ el;
2088
+ control;
2089
+ maskService;
2090
+ documentType = 'auto';
2091
+ allowAlphanumeric = false;
2092
+ typePerson;
2093
+ constructor(el, control, maskService) {
2094
+ this.el = el;
2095
+ this.control = control;
2096
+ this.maskService = maskService;
2097
+ }
2098
+ ngOnInit() {
2099
+ // Aplicar máscara inicial se já houver valor
2100
+ if (this.control.value) {
2101
+ this.applyMask(this.control.value);
2102
+ }
2103
+ }
2104
+ onInput(event) {
2105
+ const value = event.target.value;
2106
+ this.applyMask(value);
2107
+ }
2108
+ onBlur(event) {
2109
+ const value = event.target.value;
2110
+ this.applyMask(value);
2111
+ }
2112
+ applyMask(value) {
2113
+ if (!value) {
2114
+ this.control.control?.setValue('', { emitEvent: false });
2115
+ return;
2116
+ }
2117
+ const masked = this.maskService.applyDocumentMask(value, this.typePerson || 'JURIDICAL_PERSON');
2118
+ this.control.control?.setValue(masked, { emitEvent: false });
2119
+ this.el.nativeElement.value = masked;
2120
+ }
2121
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DocumentMaskDirective, deps: [{ token: i0.ElementRef }, { token: i2$1.NgControl }, { token: MaskService }], target: i0.ɵɵFactoryTarget.Directive });
2122
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: DocumentMaskDirective, isStandalone: true, selector: "[siaDocumentMask]", inputs: { documentType: "documentType", allowAlphanumeric: "allowAlphanumeric", typePerson: "typePerson" }, host: { listeners: { "input": "onInput($event)", "blur": "onBlur($event)" } }, ngImport: i0 });
2123
+ }
2124
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DocumentMaskDirective, decorators: [{
2125
+ type: Directive,
2126
+ args: [{
2127
+ selector: '[siaDocumentMask]',
2128
+ standalone: true
2129
+ }]
2130
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i2$1.NgControl }, { type: MaskService }], propDecorators: { documentType: [{
2131
+ type: Input
2132
+ }], allowAlphanumeric: [{
2133
+ type: Input
2134
+ }], typePerson: [{
2135
+ type: Input
2136
+ }], onInput: [{
2137
+ type: HostListener,
2138
+ args: ['input', ['$event']]
2139
+ }], onBlur: [{
2140
+ type: HostListener,
2141
+ args: ['blur', ['$event']]
2142
+ }] } });
2143
+
2144
+ /**
2145
+ * Componente wrapper para campos de formulário dinâmico
2146
+ * Centraliza a renderização de label, campo e mensagem de erro
2147
+ */
2148
+ class DynamicFieldWrapperComponent {
2149
+ translationService;
2150
+ field;
2151
+ form;
2152
+ mode = 'form';
2153
+ constructor(translationService) {
2154
+ this.translationService = translationService;
2155
+ }
2156
+ isRequired() {
2157
+ return this.field.required === true && this.mode === 'form';
2158
+ }
2159
+ isFieldInvalid() {
2160
+ const control = this.form.get(this.field.field);
2161
+ return !!(control && control.invalid && (control.dirty || control.touched));
2162
+ }
2163
+ getFieldError() {
2164
+ const control = this.form.get(this.field.field);
2165
+ if (control?.hasError('required')) {
2166
+ return this.translationService.translate('crmx.business.required_field');
2167
+ }
2168
+ if (control?.hasError('minlength')) {
2169
+ const minLength = this.field.minLength || control.errors?.['minlength'].requiredLength;
2170
+ return this.translationService.translate('crmx.business.min_length_error', { length: minLength });
2171
+ }
2172
+ if (control?.hasError('maxlength')) {
2173
+ const maxLength = this.field.maxLength || control.errors?.['maxlength'].requiredLength;
2174
+ return this.translationService.translate('crmx.business.max_length_error', { length: maxLength });
2175
+ }
2176
+ if (control?.hasError('min')) {
2177
+ const min = this.field.min || control.errors?.['min'].min;
2178
+ return this.translationService.translate('crmx.business.min_value_error', { value: min });
2179
+ }
2180
+ if (control?.hasError('max')) {
2181
+ const max = this.field.max || control.errors?.['max'].max;
2182
+ return this.translationService.translate('crmx.business.max_value_error', { value: max });
2183
+ }
2184
+ return '';
2185
+ }
2186
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldWrapperComponent, deps: [{ token: TranslationService }], target: i0.ɵɵFactoryTarget.Component });
2187
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DynamicFieldWrapperComponent, isStandalone: true, selector: "sia-dynamic-field-wrapper", inputs: { field: "field", form: "form", mode: "mode" }, ngImport: i0, template: `
2188
+ <div class="field">
2189
+ <label [for]="field.field">
2190
+ {{ field.label | translate }}
2191
+ <span *ngIf="isRequired()" class="required-asterisk">*</span>
2192
+ </label>
2193
+
2194
+ <!-- Conteúdo do campo (projetado via ng-content) -->
2195
+ <ng-content></ng-content>
2196
+
2197
+ <small *ngIf="isFieldInvalid()" class="p-error">
2198
+ {{ getFieldError() }}
2199
+ </small>
2200
+ </div>
2201
+ `, isInline: true, styles: [".field{display:flex;flex-direction:column;gap:.5rem;label{font-weight:500;color:var(--text-color);.required-asterisk{color:var(--p-red-500);margin-left:.25rem}}small.p-error{margin-top:.25rem;color:var(--p-red-500)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2202
+ }
2203
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldWrapperComponent, decorators: [{
2204
+ type: Component,
2205
+ args: [{ selector: 'sia-dynamic-field-wrapper', standalone: true, imports: [
2206
+ CommonModule,
2207
+ TranslatePipe
2208
+ ], template: `
2209
+ <div class="field">
2210
+ <label [for]="field.field">
2211
+ {{ field.label | translate }}
2212
+ <span *ngIf="isRequired()" class="required-asterisk">*</span>
2213
+ </label>
2214
+
2215
+ <!-- Conteúdo do campo (projetado via ng-content) -->
2216
+ <ng-content></ng-content>
2217
+
2218
+ <small *ngIf="isFieldInvalid()" class="p-error">
2219
+ {{ getFieldError() }}
2220
+ </small>
2221
+ </div>
2222
+ `, styles: [".field{display:flex;flex-direction:column;gap:.5rem;label{font-weight:500;color:var(--text-color);.required-asterisk{color:var(--p-red-500);margin-left:.25rem}}small.p-error{margin-top:.25rem;color:var(--p-red-500)}}\n"] }]
2223
+ }], ctorParameters: () => [{ type: TranslationService }], propDecorators: { field: [{
2224
+ type: Input
2225
+ }], form: [{
2226
+ type: Input
2227
+ }], mode: [{
2228
+ type: Input
2229
+ }] } });
2230
+
2231
+ class DynamicFieldTextComponent {
2232
+ translationService;
2233
+ field;
2234
+ form;
2235
+ mode = 'form';
2236
+ constructor(translationService) {
2237
+ this.translationService = translationService;
2238
+ }
2239
+ isFieldInvalid() {
2240
+ const control = this.form.get(this.field.field);
2241
+ return !!(control && control.invalid && (control.dirty || control.touched));
2242
+ }
2243
+ isFieldDirty() {
2244
+ const control = this.form.get(this.field.field);
2245
+ return !!(control && control.dirty);
2246
+ }
2247
+ getPlaceholder() {
2248
+ if (this.mode === 'filter') {
2249
+ return this.translationService.translate('crmx.business.filter_search_placeholder', {
2250
+ label: this.field.label.toLowerCase()
2251
+ });
2252
+ }
2253
+ return this.field.placeholder || '';
2254
+ }
2255
+ getDocumentType() {
2256
+ const typePersonValue = this.form?.get('typePerson')?.value;
2257
+ return typePersonValue || 'JURIDICAL_PERSON';
2258
+ }
2259
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldTextComponent, deps: [{ token: TranslationService }], target: i0.ɵɵFactoryTarget.Component });
2260
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DynamicFieldTextComponent, isStandalone: true, selector: "sia-dynamic-field-text", inputs: { field: "field", form: "form", mode: "mode" }, ngImport: i0, template: `
2261
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2262
+ <div [formGroup]="form">
2263
+ <input
2264
+ *ngIf="!field.mask"
2265
+ pInputText
2266
+ [id]="field.field"
2267
+ [formControlName]="field.field"
2268
+ [placeholder]="getPlaceholder()"
2269
+ [maxlength]="field.maxLength || 524288"
2270
+ [class.ng-invalid]="isFieldInvalid()"
2271
+ [class.ng-dirty]="isFieldDirty()" />
2272
+
2273
+ <!-- Input com máscara CEP -->
2274
+ <input
2275
+ *ngIf="field.mask === 'cep'"
2276
+ pInputText
2277
+ appCepMask
2278
+ [id]="field.field"
2279
+ [formControlName]="field.field"
2280
+ [placeholder]="getPlaceholder()"
2281
+ [maxlength]="field.maxLength || 524288"
2282
+ [class.ng-invalid]="isFieldInvalid()"
2283
+ [class.ng-dirty]="isFieldDirty()" />
2284
+
2285
+ <!-- Input com máscara Telefone -->
2286
+ <input
2287
+ *ngIf="field.mask === 'phone'"
2288
+ pInputText
2289
+ appPhoneMask
2290
+ [id]="field.field"
2291
+ [formControlName]="field.field"
2292
+ [placeholder]="getPlaceholder()"
2293
+ [maxlength]="field.maxLength || 524288"
2294
+ [class.ng-invalid]="isFieldInvalid()"
2295
+ [class.ng-dirty]="isFieldDirty()" />
2296
+
2297
+ <!-- Input com máscara Documento -->
2298
+ <input
2299
+ *ngIf="field.mask === 'document'"
2300
+ pInputText
2301
+ appDocumentMask
2302
+ [id]="field.field"
2303
+ [formControlName]="field.field"
2304
+ [placeholder]="getPlaceholder()"
2305
+ [maxlength]="field.maxLength || 524288"
2306
+ [class.ng-invalid]="isFieldInvalid()"
2307
+ [class.ng-dirty]="isFieldDirty()" />
2308
+ </div>
2309
+ </sia-dynamic-field-wrapper>
2310
+ `, isInline: true, styles: ["input{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.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: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
2311
+ }
2312
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldTextComponent, decorators: [{
2313
+ type: Component,
2314
+ args: [{ selector: 'sia-dynamic-field-text', standalone: true, imports: [
2315
+ CommonModule,
2316
+ ReactiveFormsModule,
2317
+ InputTextModule,
2318
+ CepMaskDirective,
2319
+ PhoneMaskDirective,
2320
+ DocumentMaskDirective,
2321
+ DynamicFieldWrapperComponent
2322
+ ], template: `
2323
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2324
+ <div [formGroup]="form">
2325
+ <input
2326
+ *ngIf="!field.mask"
2327
+ pInputText
2328
+ [id]="field.field"
2329
+ [formControlName]="field.field"
2330
+ [placeholder]="getPlaceholder()"
2331
+ [maxlength]="field.maxLength || 524288"
2332
+ [class.ng-invalid]="isFieldInvalid()"
2333
+ [class.ng-dirty]="isFieldDirty()" />
2334
+
2335
+ <!-- Input com máscara CEP -->
2336
+ <input
2337
+ *ngIf="field.mask === 'cep'"
2338
+ pInputText
2339
+ appCepMask
2340
+ [id]="field.field"
2341
+ [formControlName]="field.field"
2342
+ [placeholder]="getPlaceholder()"
2343
+ [maxlength]="field.maxLength || 524288"
2344
+ [class.ng-invalid]="isFieldInvalid()"
2345
+ [class.ng-dirty]="isFieldDirty()" />
2346
+
2347
+ <!-- Input com máscara Telefone -->
2348
+ <input
2349
+ *ngIf="field.mask === 'phone'"
2350
+ pInputText
2351
+ appPhoneMask
2352
+ [id]="field.field"
2353
+ [formControlName]="field.field"
2354
+ [placeholder]="getPlaceholder()"
2355
+ [maxlength]="field.maxLength || 524288"
2356
+ [class.ng-invalid]="isFieldInvalid()"
2357
+ [class.ng-dirty]="isFieldDirty()" />
2358
+
2359
+ <!-- Input com máscara Documento -->
2360
+ <input
2361
+ *ngIf="field.mask === 'document'"
2362
+ pInputText
2363
+ appDocumentMask
2364
+ [id]="field.field"
2365
+ [formControlName]="field.field"
2366
+ [placeholder]="getPlaceholder()"
2367
+ [maxlength]="field.maxLength || 524288"
2368
+ [class.ng-invalid]="isFieldInvalid()"
2369
+ [class.ng-dirty]="isFieldDirty()" />
2370
+ </div>
2371
+ </sia-dynamic-field-wrapper>
2372
+ `, styles: ["input{width:100%}\n"] }]
2373
+ }], ctorParameters: () => [{ type: TranslationService }], propDecorators: { field: [{
2374
+ type: Input
2375
+ }], form: [{
2376
+ type: Input
2377
+ }], mode: [{
2378
+ type: Input
2379
+ }] } });
2380
+
2381
+ class DynamicFieldNumberComponent {
2382
+ translationService;
2383
+ field;
2384
+ form;
2385
+ mode = 'form';
2386
+ constructor(translationService) {
2387
+ this.translationService = translationService;
2388
+ }
2389
+ isFieldInvalid() {
2390
+ const control = this.form.get(this.field.field);
2391
+ return !!(control && control.invalid && (control.dirty || control.touched));
2392
+ }
2393
+ isFieldDirty() {
2394
+ const control = this.form.get(this.field.field);
2395
+ return !!(control && control.dirty);
2396
+ }
2397
+ getPlaceholder() {
2398
+ if (this.mode === 'filter') {
2399
+ return this.translationService.translate('crmx.business.filter_search_placeholder', {
2400
+ label: this.field.label.toLowerCase()
2401
+ });
2402
+ }
2403
+ return this.field.placeholder || '';
2404
+ }
2405
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldNumberComponent, deps: [{ token: TranslationService }], target: i0.ɵɵFactoryTarget.Component });
2406
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DynamicFieldNumberComponent, isStandalone: true, selector: "sia-dynamic-field-number", inputs: { field: "field", form: "form", mode: "mode" }, ngImport: i0, template: `
2407
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2408
+ <div [formGroup]="form">
2409
+ <p-inputNumber
2410
+ [inputId]="field.field"
2411
+ [formControlName]="field.field"
2412
+ [placeholder]="getPlaceholder()"
2413
+ [min]="field.min"
2414
+ [max]="field.max"
2415
+ [minFractionDigits]="field.minFractionDigits || 0"
2416
+ [maxFractionDigits]="field.maxFractionDigits || 2"
2417
+ [mode]="field.numberMode || 'decimal'"
2418
+ [currency]="field.currency || 'BRL'"
2419
+ [locale]="field.locale || 'pt-BR'"
2420
+ [class.ng-invalid]="isFieldInvalid()"
2421
+ [class.ng-dirty]="isFieldDirty()"
2422
+ styleClass="w-full">
2423
+ </p-inputNumber>
2424
+ </div>
2425
+ </sia-dynamic-field-wrapper>
2426
+ `, isInline: true, styles: ["::ng-deep p-inputNumber{width:100%;.p-inputnumber,input{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i3$1.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled", "fluid"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
2427
+ }
2428
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldNumberComponent, decorators: [{
2429
+ type: Component,
2430
+ args: [{ selector: 'sia-dynamic-field-number', standalone: true, imports: [
2431
+ CommonModule,
2432
+ ReactiveFormsModule,
2433
+ InputNumberModule,
2434
+ DynamicFieldWrapperComponent
2435
+ ], template: `
2436
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2437
+ <div [formGroup]="form">
2438
+ <p-inputNumber
2439
+ [inputId]="field.field"
2440
+ [formControlName]="field.field"
2441
+ [placeholder]="getPlaceholder()"
2442
+ [min]="field.min"
2443
+ [max]="field.max"
2444
+ [minFractionDigits]="field.minFractionDigits || 0"
2445
+ [maxFractionDigits]="field.maxFractionDigits || 2"
2446
+ [mode]="field.numberMode || 'decimal'"
2447
+ [currency]="field.currency || 'BRL'"
2448
+ [locale]="field.locale || 'pt-BR'"
2449
+ [class.ng-invalid]="isFieldInvalid()"
2450
+ [class.ng-dirty]="isFieldDirty()"
2451
+ styleClass="w-full">
2452
+ </p-inputNumber>
2453
+ </div>
2454
+ </sia-dynamic-field-wrapper>
2455
+ `, styles: ["::ng-deep p-inputNumber{width:100%;.p-inputnumber,input{width:100%}}\n"] }]
2456
+ }], ctorParameters: () => [{ type: TranslationService }], propDecorators: { field: [{
2457
+ type: Input
2458
+ }], form: [{
2459
+ type: Input
2460
+ }], mode: [{
2461
+ type: Input
2462
+ }] } });
2463
+
2464
+ class DynamicFieldDateComponent {
2465
+ translationService;
2466
+ field;
2467
+ form;
2468
+ mode = 'form';
2469
+ constructor(translationService) {
2470
+ this.translationService = translationService;
2471
+ }
2472
+ isFieldInvalid() {
2473
+ const control = this.form.get(this.field.field);
2474
+ return !!(control && control.invalid && (control.dirty || control.touched));
2475
+ }
2476
+ isFieldDirty() {
2477
+ const control = this.form.get(this.field.field);
2478
+ return !!(control && control.dirty);
2479
+ }
2480
+ getPlaceholder() {
2481
+ if (this.mode === 'filter') {
2482
+ return this.translationService.translate('crmx.business.filter_select_placeholder', {
2483
+ label: this.field.label.toLowerCase()
2484
+ });
2485
+ }
2486
+ return this.field.placeholder || '';
2487
+ }
2488
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldDateComponent, deps: [{ token: TranslationService }], target: i0.ɵɵFactoryTarget.Component });
2489
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DynamicFieldDateComponent, isStandalone: true, selector: "sia-dynamic-field-date", inputs: { field: "field", form: "form", mode: "mode" }, ngImport: i0, template: `
2490
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2491
+ <div [formGroup]="form">
2492
+ <p-calendar
2493
+ [inputId]="field.field"
2494
+ [formControlName]="field.field"
2495
+ [placeholder]="getPlaceholder()"
2496
+ [dateFormat]="field.dateFormat || 'dd/mm/yy'"
2497
+ [showIcon]="true"
2498
+ [showButtonBar]="true"
2499
+ [class.ng-invalid]="isFieldInvalid()"
2500
+ [class.ng-dirty]="isFieldDirty()"
2501
+ [appendTo]="'body'"
2502
+ styleClass="w-full">
2503
+ </p-calendar>
2504
+ </div>
2505
+ </sia-dynamic-field-wrapper>
2506
+ `, isInline: true, styles: ["::ng-deep p-calendar{width:100%;.p-calendar,input{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CalendarModule }, { kind: "component", type: i3$2.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
2507
+ }
2508
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldDateComponent, decorators: [{
2509
+ type: Component,
2510
+ args: [{ selector: 'sia-dynamic-field-date', standalone: true, imports: [
2511
+ CommonModule,
2512
+ ReactiveFormsModule,
2513
+ CalendarModule,
2514
+ DynamicFieldWrapperComponent
2515
+ ], template: `
2516
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2517
+ <div [formGroup]="form">
2518
+ <p-calendar
2519
+ [inputId]="field.field"
2520
+ [formControlName]="field.field"
2521
+ [placeholder]="getPlaceholder()"
2522
+ [dateFormat]="field.dateFormat || 'dd/mm/yy'"
2523
+ [showIcon]="true"
2524
+ [showButtonBar]="true"
2525
+ [class.ng-invalid]="isFieldInvalid()"
2526
+ [class.ng-dirty]="isFieldDirty()"
2527
+ [appendTo]="'body'"
2528
+ styleClass="w-full">
2529
+ </p-calendar>
2530
+ </div>
2531
+ </sia-dynamic-field-wrapper>
2532
+ `, styles: ["::ng-deep p-calendar{width:100%;.p-calendar,input{width:100%}}\n"] }]
2533
+ }], ctorParameters: () => [{ type: TranslationService }], propDecorators: { field: [{
2534
+ type: Input
2535
+ }], form: [{
2536
+ type: Input
2537
+ }], mode: [{
2538
+ type: Input
2539
+ }] } });
2540
+
2541
+ class DynamicFieldDropdownComponent {
2542
+ translationService;
2543
+ field;
2544
+ form;
2545
+ mode = 'form';
2546
+ constructor(translationService) {
2547
+ this.translationService = translationService;
2548
+ }
2549
+ isFieldInvalid() {
2550
+ const control = this.form.get(this.field.field);
2551
+ return !!(control && control.invalid && (control.dirty || control.touched));
2552
+ }
2553
+ isFieldDirty() {
2554
+ const control = this.form.get(this.field.field);
2555
+ return !!(control && control.dirty);
2556
+ }
2557
+ getPlaceholder() {
2558
+ if (this.mode === 'filter') {
2559
+ return this.translationService.translate('crmx.business.filter_select_placeholder', {
2560
+ label: this.field.label.toLowerCase()
2561
+ });
2562
+ }
2563
+ return this.field.placeholder || this.translationService.translate('crmx.business.select_option');
2564
+ }
2565
+ getOptions() {
2566
+ if (this.field.type === 'enum' && this.field.enumValues) {
2567
+ return this.field.enumValues.map(value => ({
2568
+ label: this.field.enumLabels?.[value] || value,
2569
+ value: value
2570
+ }));
2571
+ }
2572
+ return this.field.options || [];
2573
+ }
2574
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldDropdownComponent, deps: [{ token: TranslationService }], target: i0.ɵɵFactoryTarget.Component });
2575
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DynamicFieldDropdownComponent, isStandalone: true, selector: "sia-dynamic-field-dropdown", inputs: { field: "field", form: "form", mode: "mode" }, ngImport: i0, template: `
2576
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2577
+ <div [formGroup]="form">
2578
+ <p-dropdown
2579
+ [inputId]="field.field"
2580
+ [formControlName]="field.field"
2581
+ [options]="getOptions()"
2582
+ [optionLabel]="field.optionLabel || 'label'"
2583
+ [optionValue]="field.optionValue || 'value'"
2584
+ [placeholder]="getPlaceholder()"
2585
+ [showClear]="mode === 'filter' || field.showClear"
2586
+ [filter]="field.filter || false"
2587
+ [filterBy]="field.filterBy"
2588
+ [class.ng-invalid]="isFieldInvalid()"
2589
+ [class.ng-dirty]="isFieldDirty()"
2590
+ [appendTo]="'body'"
2591
+ panelStyleClass="dynamic-form-dropdown-panel"
2592
+ styleClass="w-full">
2593
+ </p-dropdown>
2594
+ </div>
2595
+ </sia-dynamic-field-wrapper>
2596
+ `, isInline: true, styles: ["::ng-deep p-dropdown{width:100%;.p-dropdown{width:100%}}.dynamic-form-dropdown-panel{min-width:100%!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i3$3.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }], encapsulation: i0.ViewEncapsulation.None });
2597
+ }
2598
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldDropdownComponent, decorators: [{
2599
+ type: Component,
2600
+ args: [{ selector: 'sia-dynamic-field-dropdown', standalone: true, imports: [
2601
+ CommonModule,
2602
+ ReactiveFormsModule,
2603
+ DropdownModule,
2604
+ DynamicFieldWrapperComponent
2605
+ ], template: `
2606
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2607
+ <div [formGroup]="form">
2608
+ <p-dropdown
2609
+ [inputId]="field.field"
2610
+ [formControlName]="field.field"
2611
+ [options]="getOptions()"
2612
+ [optionLabel]="field.optionLabel || 'label'"
2613
+ [optionValue]="field.optionValue || 'value'"
2614
+ [placeholder]="getPlaceholder()"
2615
+ [showClear]="mode === 'filter' || field.showClear"
2616
+ [filter]="field.filter || false"
2617
+ [filterBy]="field.filterBy"
2618
+ [class.ng-invalid]="isFieldInvalid()"
2619
+ [class.ng-dirty]="isFieldDirty()"
2620
+ [appendTo]="'body'"
2621
+ panelStyleClass="dynamic-form-dropdown-panel"
2622
+ styleClass="w-full">
2623
+ </p-dropdown>
2624
+ </div>
2625
+ </sia-dynamic-field-wrapper>
2626
+ `, encapsulation: ViewEncapsulation.None, styles: ["::ng-deep p-dropdown{width:100%;.p-dropdown{width:100%}}.dynamic-form-dropdown-panel{min-width:100%!important}\n"] }]
2627
+ }], ctorParameters: () => [{ type: TranslationService }], propDecorators: { field: [{
2628
+ type: Input
2629
+ }], form: [{
2630
+ type: Input
2631
+ }], mode: [{
2632
+ type: Input
2633
+ }] } });
2634
+
2635
+ class DynamicFieldLookupComponent {
2636
+ translationService;
2637
+ field;
2638
+ form;
2639
+ formGroup;
2640
+ mode = 'form';
2641
+ displayValue = '';
2642
+ showLookupDialog = false;
2643
+ loading = false;
2644
+ searchTerm = '';
2645
+ data = [];
2646
+ filteredData = [];
2647
+ totalRecords = 0;
2648
+ currentPage = 0;
2649
+ pageSize = 5;
2650
+ selectedItem = null;
2651
+ columns = [];
2652
+ displayField = 'name';
2653
+ valueField = 'id';
2654
+ searchFields = ['name'];
2655
+ constructor(translationService) {
2656
+ this.translationService = translationService;
2657
+ }
2658
+ ngOnInit() {
2659
+ this.setupLookupConfig();
2660
+ this.watchValueChanges();
2661
+ }
2662
+ watchValueChanges() {
2663
+ const control = this.form.get(this.field.field);
2664
+ if (control) {
2665
+ // Carregar valor inicial
2666
+ if (control.value) {
2667
+ this.handleValueDisplay(control.value);
2668
+ }
2669
+ // Observar mudanças
2670
+ control.valueChanges.subscribe(value => {
2671
+ if (value !== null && value !== undefined && value !== '') {
2672
+ this.handleValueDisplay(value);
2673
+ }
2674
+ else {
2675
+ this.displayValue = '';
2676
+ }
2677
+ });
2678
+ }
2679
+ }
2680
+ get value() {
2681
+ return this.form.get(this.field.field)?.value;
2682
+ }
2683
+ get currentValue() {
2684
+ return this.value;
2685
+ }
2686
+ setValue(value) {
2687
+ this.form.get(this.field.field)?.setValue(value);
2688
+ }
2689
+ setupLookupConfig() {
2690
+ if (this.field) {
2691
+ this.displayField = this.field.lookupDisplayField || 'name';
2692
+ this.valueField = this.field.lookupValueField || 'id';
2693
+ this.searchFields = this.field.lookupSearchFields || ['name'];
2694
+ this.columns = this.field.lookupColumns || [
2695
+ { field: this.displayField, header: 'Nome', width: '100%' }
2696
+ ];
2697
+ }
2698
+ }
2699
+ handleValueDisplay(value) {
2700
+ if (value === null || value === undefined || value === '') {
2701
+ this.displayValue = '';
2702
+ return;
2703
+ }
2704
+ if (typeof value === 'object' && value !== null) {
2705
+ this.displayValue = this.formatDisplayValue(value);
2706
+ // Em modo filter, manter o objeto completo
2707
+ // Em modo form, extrair apenas o ID EXCETO se já temos o objeto completo
2708
+ // (evita requisições desnecessárias quando o objeto já está carregado)
2709
+ if (this.mode === 'form') {
2710
+ // Verificar se o objeto tem o campo de display preenchido
2711
+ // Se tiver, significa que já temos os dados completos e não precisamos fazer nada
2712
+ const hasDisplayData = this.field?.lookupDisplayFields
2713
+ ? this.field.lookupDisplayFields.some(f => this.getFieldValue(value, f))
2714
+ : value[this.displayField];
2715
+ if (!hasDisplayData) {
2716
+ // Só extrair o ID se não temos os dados de display
2717
+ const newValue = value[this.valueField] || value.id || value;
2718
+ if (this.value !== newValue) {
2719
+ this.setValue(newValue);
2720
+ }
2721
+ }
2722
+ }
2723
+ return;
2724
+ }
2725
+ // Se é um ID (string ou number), não fazer requisição
2726
+ // Deixar vazio e só carregar quando abrir o lookup
2727
+ if (typeof value === 'string' || typeof value === 'number') {
2728
+ this.displayValue = '';
2729
+ return;
2730
+ }
2731
+ }
2732
+ formatDisplayValue(item) {
2733
+ if (!item)
2734
+ return '';
2735
+ if (this.field?.lookupDisplayFields && this.field.lookupDisplayFields.length > 0) {
2736
+ const values = this.field.lookupDisplayFields
2737
+ .map(fieldName => this.getFieldValue(item, fieldName))
2738
+ .filter(value => value !== null && value !== undefined && value !== '');
2739
+ return values.join(' - ');
2740
+ }
2741
+ return this.getFieldValue(item, this.displayField) || '';
2742
+ }
2743
+ isFieldInvalid() {
2744
+ const control = this.form.get(this.field.field);
2745
+ return !!(control && control.invalid && (control.dirty || control.touched));
2746
+ }
2747
+ isFieldDisabled() {
2748
+ const control = this.form.get(this.field.field);
2749
+ return control?.disabled || false;
2750
+ }
2751
+ getPlaceholder() {
2752
+ if (this.mode === 'filter') {
2753
+ return this.translationService.translate('crmx.business.filter_search_placeholder', {
2754
+ label: this.field.label.toLowerCase()
2755
+ });
2756
+ }
2757
+ return this.field.placeholder || 'Clique para selecionar';
2758
+ }
2759
+ openLookup() {
2760
+ if (this.isFieldDisabled()) {
2761
+ return;
2762
+ }
2763
+ this.updateSelectedItem();
2764
+ this.showLookupDialog = true;
2765
+ this.currentPage = 0;
2766
+ this.searchTerm = '';
2767
+ this.loadData();
2768
+ }
2769
+ onDialogHide() {
2770
+ this.searchTerm = '';
2771
+ this.currentPage = 0;
2772
+ }
2773
+ onPageChange(event) {
2774
+ if (!this.showLookupDialog) {
2775
+ return;
2776
+ }
2777
+ this.currentPage = event.first / event.rows;
2778
+ this.loadData();
2779
+ }
2780
+ loadData() {
2781
+ if (!this.field?.lookupService) {
2782
+ return;
2783
+ }
2784
+ this.loading = true;
2785
+ const listParams = {
2786
+ page: this.currentPage,
2787
+ size: this.pageSize
2788
+ };
2789
+ if (this.searchTerm.trim()) {
2790
+ const term = this.searchTerm.trim();
2791
+ const filterConditions = [];
2792
+ const isNumericTerm = !isNaN(Number(term)) && term !== '';
2793
+ this.searchFields.forEach(field => {
2794
+ const column = this.columns.find(col => col.field === field);
2795
+ const fieldType = column?.type || 'text';
2796
+ if (fieldType === 'number') {
2797
+ if (isNumericTerm) {
2798
+ filterConditions.push(`${field} eq ${term}`);
2799
+ }
2800
+ }
2801
+ else {
2802
+ const upperTerm = term.toUpperCase();
2803
+ filterConditions.push(`UPPER(${field}) like '%${upperTerm}%'`);
2804
+ }
2805
+ });
2806
+ if (filterConditions.length > 0) {
2807
+ listParams.filterQuery = filterConditions.join(' or ');
2808
+ }
2809
+ }
2810
+ if (this.field.lookupFilters) {
2811
+ Object.keys(this.field.lookupFilters).forEach(key => {
2812
+ listParams[key] = this.field.lookupFilters[key];
2813
+ });
2814
+ }
2815
+ if (this.field.lookupDynamicFilters && this.formGroup) {
2816
+ const dynamicFilters = this.field.lookupDynamicFilters(this.formGroup.value);
2817
+ const dynamicFilterConditions = [];
2818
+ Object.keys(dynamicFilters).forEach(key => {
2819
+ const value = dynamicFilters[key];
2820
+ if (value !== null && value !== undefined) {
2821
+ dynamicFilterConditions.push(`${key} eq '${value}'`);
2822
+ }
2823
+ });
2824
+ if (dynamicFilterConditions.length > 0) {
2825
+ const dynamicFilterQuery = dynamicFilterConditions.join(' and ');
2826
+ if (listParams.filterQuery) {
2827
+ listParams.filterQuery = `(${listParams.filterQuery}) and (${dynamicFilterQuery})`;
2828
+ }
2829
+ else {
2830
+ listParams.filterQuery = dynamicFilterQuery;
2831
+ }
2832
+ }
2833
+ }
2834
+ this.field.lookupService.list(listParams).subscribe({
2835
+ next: (response) => {
2836
+ this.data = response.contents || [];
2837
+ this.filteredData = [...this.data];
2838
+ this.totalRecords = response.totalElements || 0;
2839
+ this.updateSelectedItem();
2840
+ // Após carregar os dados da grid, se temos um valor selecionado mas sem displayValue,
2841
+ // tentar encontrar o item nos dados carregados
2842
+ if (this.value && !this.displayValue) {
2843
+ const item = this.filteredData.find(i => i[this.valueField] === this.value);
2844
+ if (item) {
2845
+ this.displayValue = this.formatDisplayValue(item);
2846
+ }
2847
+ }
2848
+ this.loading = false;
2849
+ },
2850
+ error: (_error) => {
2851
+ this.data = [];
2852
+ this.filteredData = [];
2853
+ this.totalRecords = 0;
2854
+ this.loading = false;
2855
+ }
2856
+ });
2857
+ }
2858
+ updateSelectedItem() {
2859
+ if (!this.value) {
2860
+ this.selectedItem = null;
2861
+ return;
2862
+ }
2863
+ const item = this.filteredData.find(i => i[this.valueField] === this.value);
2864
+ if (item) {
2865
+ this.selectedItem = item;
2866
+ }
2867
+ }
2868
+ onSearch() {
2869
+ if (!this.showLookupDialog) {
2870
+ return;
2871
+ }
2872
+ this.currentPage = 0;
2873
+ this.loadData();
2874
+ }
2875
+ clearSearch() {
2876
+ this.searchTerm = '';
2877
+ this.currentPage = 0;
2878
+ this.loadData();
2879
+ }
2880
+ selectItem(item) {
2881
+ if (this.mode === 'filter') {
2882
+ this.setValue(item);
2883
+ this.displayValue = this.formatDisplayValue(item);
2884
+ }
2885
+ else {
2886
+ const newValue = item[this.valueField];
2887
+ this.setValue(newValue);
2888
+ this.displayValue = this.formatDisplayValue(item);
2889
+ }
2890
+ this.showLookupDialog = false;
2891
+ }
2892
+ onRowSelect(event) {
2893
+ this.selectItem(event.data);
2894
+ }
2895
+ clearValue() {
2896
+ this.setValue(null);
2897
+ this.displayValue = '';
2898
+ this.selectedItem = null;
2899
+ }
2900
+ getFieldValue(item, field) {
2901
+ return field.split('.').reduce((obj, key) => obj?.[key], item) || '';
2902
+ }
2903
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldLookupComponent, deps: [{ token: TranslationService }], target: i0.ɵɵFactoryTarget.Component });
2904
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DynamicFieldLookupComponent, isStandalone: true, selector: "sia-dynamic-field-lookup", inputs: { field: "field", form: "form", formGroup: "formGroup", mode: "mode" }, ngImport: i0, template: `
2905
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
2906
+ <div [formGroup]="form" class="lookup-field" [ngClass]="{'disabled': isFieldDisabled()}">
2907
+ <!-- Hidden input para o formControl -->
2908
+ <input type="hidden" [formControlName]="field.field" />
2909
+
2910
+ <div class="lookup-input-group">
2911
+ <input
2912
+ pInputText
2913
+ [value]="displayValue"
2914
+ [placeholder]="getPlaceholder()"
2915
+ [class.p-invalid]="isFieldInvalid()"
2916
+ [disabled]="isFieldDisabled()"
2917
+ readonly
2918
+ class="lookup-display"
2919
+ (click)="openLookup()"
2920
+ />
2921
+ <div class="lookup-buttons">
2922
+ <p-button
2923
+ *ngIf="currentValue"
2924
+ icon="pi pi-trash"
2925
+ severity="danger"
2926
+ [text]="true"
2927
+ [rounded]="true"
2928
+ (onClick)="clearValue()"
2929
+ [disabled]="isFieldDisabled()"
2930
+ pTooltip="Limpar"
2931
+ tooltipPosition="top"
2932
+ size="small"
2933
+ ></p-button>
2934
+ <p-button
2935
+ icon="pi pi-search"
2936
+ severity="secondary"
2937
+ [text]="true"
2938
+ [rounded]="true"
2939
+ (onClick)="openLookup()"
2940
+ [disabled]="isFieldDisabled()"
2941
+ pTooltip="Buscar"
2942
+ tooltipPosition="top"
2943
+ size="small"
2944
+ ></p-button>
2945
+ </div>
2946
+ </div>
2947
+ </div>
2948
+
2949
+ <!-- Lookup Dialog -->
2950
+ <p-dialog
2951
+ [(visible)]="showLookupDialog"
2952
+ [modal]="true"
2953
+ [style]="{ width: '800px', maxHeight: '80vh' }"
2954
+ [contentStyle]="{ maxHeight: 'calc(80vh - 120px)', overflow: 'auto' }"
2955
+ [draggable]="false"
2956
+ [resizable]="true"
2957
+ [header]="'Selecionar ' + (field.label | translate)"
2958
+ [appendTo]="'body'"
2959
+ (onHide)="onDialogHide()"
2960
+ >
2961
+ <div class="lookup-dialog-content">
2962
+ <!-- Search Input -->
2963
+ <div class="search-wrapper">
2964
+ <input
2965
+ pInputText
2966
+ [(ngModel)]="searchTerm"
2967
+ [placeholder]="'Buscar ' + (field.label | translate).toLowerCase() + '...'"
2968
+ (keydown.enter)="onSearch()"
2969
+ class="search-input-full"
2970
+ />
2971
+ <div class="search-buttons-inside">
2972
+ <p-button
2973
+ *ngIf="searchTerm"
2974
+ icon="pi pi-trash"
2975
+ severity="danger"
2976
+ [text]="true"
2977
+ [rounded]="true"
2978
+ (onClick)="clearSearch()"
2979
+ class="search-button-clear"
2980
+ pTooltip="Limpar busca"
2981
+ tooltipPosition="top"
2982
+ ></p-button>
2983
+ <p-button
2984
+ icon="pi pi-search"
2985
+ severity="secondary"
2986
+ [text]="true"
2987
+ [rounded]="true"
2988
+ (onClick)="onSearch()"
2989
+ class="search-button-search"
2990
+ pTooltip="Buscar"
2991
+ tooltipPosition="top"
2992
+ ></p-button>
2993
+ </div>
2994
+ </div>
2995
+
2996
+ <!-- Data Table -->
2997
+ <p-table
2998
+ [value]="filteredData"
2999
+ [loading]="loading"
3000
+ [paginator]="true"
3001
+ [rows]="pageSize"
3002
+ [totalRecords]="totalRecords"
3003
+ [lazy]="true"
3004
+ (onLazyLoad)="onPageChange($event)"
3005
+ [showCurrentPageReport]="true"
3006
+ currentPageReportTemplate="Mostrando {first} a {last} de {totalRecords} registros"
3007
+ dataKey="id"
3008
+ [rowHover]="true"
3009
+ responsiveLayout="scroll"
3010
+ styleClass="p-datatable-striped lookup-table"
3011
+ [(selection)]="selectedItem"
3012
+ (onRowSelect)="onRowSelect($event)"
3013
+ selectionMode="single"
3014
+ >
3015
+ <ng-template pTemplate="header">
3016
+ <tr>
3017
+ <th *ngFor="let col of columns" [style.width]="col.width">
3018
+ {{ col.header }}
3019
+ </th>
3020
+ </tr>
3021
+ </ng-template>
3022
+
3023
+ <ng-template pTemplate="body" let-item>
3024
+ <tr [pSelectableRow]="item">
3025
+ <td *ngFor="let col of columns">
3026
+ {{ getFieldValue(item, col.field) }}
3027
+ </td>
3028
+ </tr>
3029
+ </ng-template>
3030
+
3031
+ <ng-template pTemplate="emptymessage">
3032
+ <tr>
3033
+ <td [attr.colspan]="columns.length" class="empty-message">
3034
+ <div class="empty-state">
3035
+ <i class="pi pi-search"></i>
3036
+ <p>Nenhum registro encontrado</p>
3037
+ <small>Tente ajustar os termos de busca</small>
3038
+ </div>
3039
+ </td>
3040
+ </tr>
3041
+ </ng-template>
3042
+ </p-table>
3043
+ </div>
3044
+ </p-dialog>
3045
+ </sia-dynamic-field-wrapper>
3046
+ `, isInline: true, styles: [".lookup-field{width:100%;position:relative}.lookup-input-group{position:relative;width:100%;.lookup-display{width:100%;cursor:pointer;height:2.857rem;padding:.75rem 4rem .75rem .75rem;font-size:1rem;line-height:1.5;box-sizing:border-box;display:flex;align-items:center;&:focus{outline:none}&:hover{background:transparent}}.lookup-buttons{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;z-index:10}::ng-deep .p-button{width:2rem!important;height:2rem!important;min-width:2rem!important;min-height:2rem!important;padding:0!important;margin:0!important;display:flex!important;align-items:center!important;justify-content:center!important;box-sizing:border-box!important;.p-button-icon{margin:0!important;font-size:.875rem!important}.p-button-label{display:none!important}}}.lookup-dialog-content{height:100%;display:flex;flex-direction:column;padding:0;.search-wrapper{position:relative;margin-bottom:16px;width:100%;.search-input-full{width:100%;padding-right:5.5rem}.search-buttons-inside{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;::ng-deep .p-button{width:2.5rem!important;height:2.5rem!important}}}.lookup-table{flex:1;.empty-state{text-align:center;padding:32px;color:var(--p-text-color-secondary);i{font-size:48px;margin-bottom:16px;display:block}p{margin:8px 0;font-size:16px}small{font-size:14px}}}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.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: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i5$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i5.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i8.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i8.SelectableRow, selector: "[pSelectableRow]", inputs: ["pSelectableRow", "pSelectableRowIndex", "pSelectableRowDisabled"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i9.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
3047
+ }
3048
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldLookupComponent, decorators: [{
3049
+ type: Component,
3050
+ args: [{ selector: 'sia-dynamic-field-lookup', standalone: true, imports: [
3051
+ CommonModule,
3052
+ ReactiveFormsModule,
3053
+ FormsModule,
3054
+ ButtonModule,
3055
+ InputTextModule,
3056
+ DialogModule,
3057
+ TableModule,
3058
+ TooltipModule,
3059
+ TranslatePipe,
3060
+ DynamicFieldWrapperComponent
3061
+ ], template: `
3062
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
3063
+ <div [formGroup]="form" class="lookup-field" [ngClass]="{'disabled': isFieldDisabled()}">
3064
+ <!-- Hidden input para o formControl -->
3065
+ <input type="hidden" [formControlName]="field.field" />
3066
+
3067
+ <div class="lookup-input-group">
3068
+ <input
3069
+ pInputText
3070
+ [value]="displayValue"
3071
+ [placeholder]="getPlaceholder()"
3072
+ [class.p-invalid]="isFieldInvalid()"
3073
+ [disabled]="isFieldDisabled()"
3074
+ readonly
3075
+ class="lookup-display"
3076
+ (click)="openLookup()"
3077
+ />
3078
+ <div class="lookup-buttons">
3079
+ <p-button
3080
+ *ngIf="currentValue"
3081
+ icon="pi pi-trash"
3082
+ severity="danger"
3083
+ [text]="true"
3084
+ [rounded]="true"
3085
+ (onClick)="clearValue()"
3086
+ [disabled]="isFieldDisabled()"
3087
+ pTooltip="Limpar"
3088
+ tooltipPosition="top"
3089
+ size="small"
3090
+ ></p-button>
3091
+ <p-button
3092
+ icon="pi pi-search"
3093
+ severity="secondary"
3094
+ [text]="true"
3095
+ [rounded]="true"
3096
+ (onClick)="openLookup()"
3097
+ [disabled]="isFieldDisabled()"
3098
+ pTooltip="Buscar"
3099
+ tooltipPosition="top"
3100
+ size="small"
3101
+ ></p-button>
3102
+ </div>
3103
+ </div>
3104
+ </div>
3105
+
3106
+ <!-- Lookup Dialog -->
3107
+ <p-dialog
3108
+ [(visible)]="showLookupDialog"
3109
+ [modal]="true"
3110
+ [style]="{ width: '800px', maxHeight: '80vh' }"
3111
+ [contentStyle]="{ maxHeight: 'calc(80vh - 120px)', overflow: 'auto' }"
3112
+ [draggable]="false"
3113
+ [resizable]="true"
3114
+ [header]="'Selecionar ' + (field.label | translate)"
3115
+ [appendTo]="'body'"
3116
+ (onHide)="onDialogHide()"
3117
+ >
3118
+ <div class="lookup-dialog-content">
3119
+ <!-- Search Input -->
3120
+ <div class="search-wrapper">
3121
+ <input
3122
+ pInputText
3123
+ [(ngModel)]="searchTerm"
3124
+ [placeholder]="'Buscar ' + (field.label | translate).toLowerCase() + '...'"
3125
+ (keydown.enter)="onSearch()"
3126
+ class="search-input-full"
3127
+ />
3128
+ <div class="search-buttons-inside">
3129
+ <p-button
3130
+ *ngIf="searchTerm"
3131
+ icon="pi pi-trash"
3132
+ severity="danger"
3133
+ [text]="true"
3134
+ [rounded]="true"
3135
+ (onClick)="clearSearch()"
3136
+ class="search-button-clear"
3137
+ pTooltip="Limpar busca"
3138
+ tooltipPosition="top"
3139
+ ></p-button>
3140
+ <p-button
3141
+ icon="pi pi-search"
3142
+ severity="secondary"
3143
+ [text]="true"
3144
+ [rounded]="true"
3145
+ (onClick)="onSearch()"
3146
+ class="search-button-search"
3147
+ pTooltip="Buscar"
3148
+ tooltipPosition="top"
3149
+ ></p-button>
3150
+ </div>
3151
+ </div>
3152
+
3153
+ <!-- Data Table -->
3154
+ <p-table
3155
+ [value]="filteredData"
3156
+ [loading]="loading"
3157
+ [paginator]="true"
3158
+ [rows]="pageSize"
3159
+ [totalRecords]="totalRecords"
3160
+ [lazy]="true"
3161
+ (onLazyLoad)="onPageChange($event)"
3162
+ [showCurrentPageReport]="true"
3163
+ currentPageReportTemplate="Mostrando {first} a {last} de {totalRecords} registros"
3164
+ dataKey="id"
3165
+ [rowHover]="true"
3166
+ responsiveLayout="scroll"
3167
+ styleClass="p-datatable-striped lookup-table"
3168
+ [(selection)]="selectedItem"
3169
+ (onRowSelect)="onRowSelect($event)"
3170
+ selectionMode="single"
3171
+ >
3172
+ <ng-template pTemplate="header">
3173
+ <tr>
3174
+ <th *ngFor="let col of columns" [style.width]="col.width">
3175
+ {{ col.header }}
3176
+ </th>
3177
+ </tr>
3178
+ </ng-template>
3179
+
3180
+ <ng-template pTemplate="body" let-item>
3181
+ <tr [pSelectableRow]="item">
3182
+ <td *ngFor="let col of columns">
3183
+ {{ getFieldValue(item, col.field) }}
3184
+ </td>
3185
+ </tr>
3186
+ </ng-template>
3187
+
3188
+ <ng-template pTemplate="emptymessage">
3189
+ <tr>
3190
+ <td [attr.colspan]="columns.length" class="empty-message">
3191
+ <div class="empty-state">
3192
+ <i class="pi pi-search"></i>
3193
+ <p>Nenhum registro encontrado</p>
3194
+ <small>Tente ajustar os termos de busca</small>
3195
+ </div>
3196
+ </td>
3197
+ </tr>
3198
+ </ng-template>
3199
+ </p-table>
3200
+ </div>
3201
+ </p-dialog>
3202
+ </sia-dynamic-field-wrapper>
3203
+ `, styles: [".lookup-field{width:100%;position:relative}.lookup-input-group{position:relative;width:100%;.lookup-display{width:100%;cursor:pointer;height:2.857rem;padding:.75rem 4rem .75rem .75rem;font-size:1rem;line-height:1.5;box-sizing:border-box;display:flex;align-items:center;&:focus{outline:none}&:hover{background:transparent}}.lookup-buttons{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;z-index:10}::ng-deep .p-button{width:2rem!important;height:2rem!important;min-width:2rem!important;min-height:2rem!important;padding:0!important;margin:0!important;display:flex!important;align-items:center!important;justify-content:center!important;box-sizing:border-box!important;.p-button-icon{margin:0!important;font-size:.875rem!important}.p-button-label{display:none!important}}}.lookup-dialog-content{height:100%;display:flex;flex-direction:column;padding:0;.search-wrapper{position:relative;margin-bottom:16px;width:100%;.search-input-full{width:100%;padding-right:5.5rem}.search-buttons-inside{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;::ng-deep .p-button{width:2.5rem!important;height:2.5rem!important}}}.lookup-table{flex:1;.empty-state{text-align:center;padding:32px;color:var(--p-text-color-secondary);i{font-size:48px;margin-bottom:16px;display:block}p{margin:8px 0;font-size:16px}small{font-size:14px}}}}\n"] }]
3204
+ }], ctorParameters: () => [{ type: TranslationService }], propDecorators: { field: [{
3205
+ type: Input
3206
+ }], form: [{
3207
+ type: Input
3208
+ }], formGroup: [{
3209
+ type: Input
3210
+ }], mode: [{
3211
+ type: Input
3212
+ }] } });
3213
+
3214
+ class DynamicFieldTextareaComponent {
3215
+ translationService;
3216
+ field;
3217
+ form;
3218
+ mode = 'form';
3219
+ constructor(translationService) {
3220
+ this.translationService = translationService;
3221
+ }
3222
+ isFieldInvalid() {
3223
+ const control = this.form.get(this.field.field);
3224
+ return !!(control && control.invalid && (control.dirty || control.touched));
3225
+ }
3226
+ isFieldDirty() {
3227
+ const control = this.form.get(this.field.field);
3228
+ return !!(control && control.dirty);
3229
+ }
3230
+ getPlaceholder() {
3231
+ if (this.mode === 'filter') {
3232
+ return this.translationService.translate('crmx.business.filter_search_placeholder', {
3233
+ label: this.field.label.toLowerCase()
3234
+ });
3235
+ }
3236
+ return this.field.placeholder || '';
3237
+ }
3238
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldTextareaComponent, deps: [{ token: TranslationService }], target: i0.ɵɵFactoryTarget.Component });
3239
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DynamicFieldTextareaComponent, isStandalone: true, selector: "sia-dynamic-field-textarea", inputs: { field: "field", form: "form", mode: "mode" }, ngImport: i0, template: `
3240
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
3241
+ <div [formGroup]="form">
3242
+ <textarea
3243
+ pInputTextarea
3244
+ [id]="field.field"
3245
+ [formControlName]="field.field"
3246
+ [placeholder]="getPlaceholder()"
3247
+ [rows]="field.rows || 3"
3248
+ [maxlength]="field.maxLength || 524288"
3249
+ [class.ng-invalid]="isFieldInvalid()"
3250
+ [class.ng-dirty]="isFieldDirty()">
3251
+ </textarea>
3252
+ </div>
3253
+ </sia-dynamic-field-wrapper>
3254
+ `, isInline: true, styles: ["textarea{width:100%;resize:vertical}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.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: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: InputTextarea, selector: "[pInputTextarea]", inputs: ["autoResize", "variant", "fluid"], outputs: ["onResize"] }, { kind: "component", type: DynamicFieldWrapperComponent, selector: "sia-dynamic-field-wrapper", inputs: ["field", "form", "mode"] }] });
3255
+ }
3256
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFieldTextareaComponent, decorators: [{
3257
+ type: Component,
3258
+ args: [{ selector: 'sia-dynamic-field-textarea', standalone: true, imports: [
3259
+ CommonModule,
3260
+ ReactiveFormsModule,
3261
+ InputTextarea,
3262
+ DynamicFieldWrapperComponent
3263
+ ], template: `
3264
+ <sia-dynamic-field-wrapper [field]="field" [form]="form" [mode]="mode">
3265
+ <div [formGroup]="form">
3266
+ <textarea
3267
+ pInputTextarea
3268
+ [id]="field.field"
3269
+ [formControlName]="field.field"
3270
+ [placeholder]="getPlaceholder()"
3271
+ [rows]="field.rows || 3"
3272
+ [maxlength]="field.maxLength || 524288"
3273
+ [class.ng-invalid]="isFieldInvalid()"
3274
+ [class.ng-dirty]="isFieldDirty()">
3275
+ </textarea>
3276
+ </div>
3277
+ </sia-dynamic-field-wrapper>
3278
+ `, styles: ["textarea{width:100%;resize:vertical}\n"] }]
3279
+ }], ctorParameters: () => [{ type: TranslationService }], propDecorators: { field: [{
3280
+ type: Input
3281
+ }], form: [{
3282
+ type: Input
3283
+ }], mode: [{
3284
+ type: Input
3285
+ }] } });
3286
+
3287
+ class DynamicFormComponent {
3288
+ fb;
3289
+ translationService;
3290
+ formFields = [];
3291
+ formSections = [];
3292
+ fixedFilters = []; // Filtros fixos (sempre visíveis no topo)
3293
+ filterSections = []; // Seções de filtros (colapsáveis)
3294
+ entityData = null;
3295
+ mode = 'form';
3296
+ // Display Mode Configuration
3297
+ displayMode = 'inline';
3298
+ visible = false;
3299
+ dialogConfig = {};
3300
+ drawerConfig = {};
3301
+ // Button Configuration
3302
+ showSubmitButton = false;
3303
+ showCancelButton = false;
3304
+ submitButtonLabel;
3305
+ cancelButtonLabel;
3306
+ submitButtonIcon = 'pi pi-check';
3307
+ cancelButtonIcon = 'pi pi-times';
3308
+ formReady = new EventEmitter();
3309
+ formSubmit = new EventEmitter();
3310
+ visibleChange = new EventEmitter();
3311
+ onCancel = new EventEmitter();
3312
+ fieldSave = new EventEmitter();
3313
+ form;
3314
+ loading = false;
3315
+ // Inline edit mode state
3316
+ editingField = null;
3317
+ savingField = false;
3318
+ get hasFields() {
3319
+ return this.formFields.length > 0;
3320
+ }
3321
+ get hasSections() {
3322
+ return this.formSections.length > 0;
3323
+ }
3324
+ get hasFixedFilters() {
3325
+ return this.fixedFilters.length > 0;
3326
+ }
3327
+ get hasFilterSections() {
3328
+ return this.filterSections.length > 0;
3329
+ }
3330
+ get allFields() {
3331
+ // Retorna todos os campos (diretos + das seções + filtros fixos + seções de filtros)
3332
+ const sectionFields = this.formSections.flatMap(section => section.fields);
3333
+ const filterSectionFields = this.filterSections.flatMap(section => section.fields);
3334
+ return [...this.formFields, ...sectionFields, ...this.fixedFilters, ...filterSectionFields];
3335
+ }
3336
+ get isInlineMode() {
3337
+ return this.displayMode === 'inline';
3338
+ }
3339
+ get isInlineEditMode() {
3340
+ return this.mode === 'inline-edit';
3341
+ }
3342
+ get fieldMode() {
3343
+ return this.isInlineEditMode ? 'form' : this.mode;
3344
+ }
3345
+ get isDialogMode() {
3346
+ return this.displayMode === 'dialog';
3347
+ }
3348
+ get isDrawerMode() {
3349
+ return this.displayMode === 'drawer';
3350
+ }
3351
+ // Dialog configuration with defaults
3352
+ get dialogHeader() {
3353
+ return this.dialogConfig.header || this.translationService.translate('crmx.business.form');
3354
+ }
3355
+ get dialogWidth() {
3356
+ return this.dialogConfig.width || '600px';
3357
+ }
3358
+ get dialogDraggable() {
3359
+ return this.dialogConfig.draggable !== undefined ? this.dialogConfig.draggable : false;
3360
+ }
3361
+ get dialogResizable() {
3362
+ return this.dialogConfig.resizable !== undefined ? this.dialogConfig.resizable : false;
3363
+ }
3364
+ get dialogMaximizable() {
3365
+ return this.dialogConfig.maximizable !== undefined ? this.dialogConfig.maximizable : false;
3366
+ }
3367
+ get dialogCloseOnEscape() {
3368
+ return this.dialogConfig.closeOnEscape !== undefined ? this.dialogConfig.closeOnEscape : true;
3369
+ }
3370
+ get dialogDismissableMask() {
3371
+ return this.dialogConfig.dismissableMask !== undefined ? this.dialogConfig.dismissableMask : false;
3372
+ }
3373
+ // Drawer configuration with defaults
3374
+ get drawerHeader() {
3375
+ return this.drawerConfig.header || this.translationService.translate('crmx.business.form');
3376
+ }
3377
+ get drawerPosition() {
3378
+ return this.drawerConfig.position || 'right';
3379
+ }
3380
+ get drawerShowCloseIcon() {
3381
+ return this.drawerConfig.showCloseIcon !== undefined ? this.drawerConfig.showCloseIcon : true;
3382
+ }
3383
+ get drawerCloseOnEscape() {
3384
+ return this.drawerConfig.closeOnEscape !== undefined ? this.drawerConfig.closeOnEscape : true;
3385
+ }
3386
+ get drawerDismissable() {
3387
+ return this.drawerConfig.dismissable !== undefined ? this.drawerConfig.dismissable : true;
3388
+ }
3389
+ get drawerStyle() {
3390
+ const position = this.drawerPosition;
3391
+ const isHorizontal = position === 'left' || position === 'right';
3392
+ // Se tem customWidth, usa ela
3393
+ if (this.drawerConfig.customWidth) {
3394
+ return isHorizontal
3395
+ ? { width: this.drawerConfig.customWidth }
3396
+ : { height: this.drawerConfig.customWidth };
3397
+ }
3398
+ // Usa o tamanho padronizado
3399
+ const size = this.drawerConfig.size || 'medium';
3400
+ const sizeMap = {
3401
+ 'small': isHorizontal ? '25%' : '25vh',
3402
+ 'medium': isHorizontal ? '40%' : '40vh',
3403
+ 'large': isHorizontal ? '60%' : '60vh',
3404
+ 'xlarge': isHorizontal ? '80%' : '80vh',
3405
+ 'full': isHorizontal ? '100%' : '100vh'
3406
+ };
3407
+ const sizeValue = sizeMap[size];
3408
+ return isHorizontal
3409
+ ? { width: sizeValue }
3410
+ : { height: sizeValue };
3411
+ }
3412
+ get submitLabel() {
3413
+ return this.submitButtonLabel || this.translationService.translate('crmx.business.save');
3414
+ }
3415
+ get cancelLabel() {
3416
+ return this.cancelButtonLabel || this.translationService.translate('crmx.business.cancel');
3417
+ }
3418
+ constructor(fb, translationService) {
3419
+ this.fb = fb;
3420
+ this.translationService = translationService;
3421
+ }
3422
+ ngOnInit() {
3423
+ this.initForm();
3424
+ this.setupConditionalFieldsWatcher();
3425
+ this.formReady.emit(this.form);
3426
+ }
3427
+ ngOnChanges(changes) {
3428
+ if (changes['entityData'] && this.form) {
3429
+ if (this.entityData) {
3430
+ this.loadEntityData();
3431
+ }
3432
+ else {
3433
+ this.resetForm();
3434
+ }
3435
+ }
3436
+ // Reset loading and form when dialog/drawer is opened
3437
+ if (changes['visible'] && changes['visible'].currentValue === true) {
3438
+ this.loading = false;
3439
+ // If opening without entityData (new record), reset form
3440
+ if (!this.entityData && this.form) {
3441
+ this.resetForm();
3442
+ }
3443
+ }
3444
+ }
3445
+ initForm() {
3446
+ const formControls = {};
3447
+ // Processa todos os campos (diretos + das seções)
3448
+ this.allFields.forEach(field => {
3449
+ const defaultValue = this.getDefaultValue(field);
3450
+ const validators = this.mode === 'filter' ? [] : (field.validators || []);
3451
+ const control = this.fb.control({ value: defaultValue, disabled: field.disabled || false }, validators);
3452
+ formControls[field.field] = control;
3453
+ });
3454
+ this.form = this.fb.group(formControls);
3455
+ this.applyInitialDisabledState();
3456
+ }
3457
+ getDefaultValue(field) {
3458
+ if (field.defaultValue !== undefined) {
3459
+ return field.defaultValue;
3460
+ }
3461
+ switch (field.type) {
3462
+ case 'number':
3463
+ case 'date':
3464
+ return null;
3465
+ case 'dropdown':
3466
+ case 'enum':
3467
+ return this.mode === 'filter' ? null : '';
3468
+ default:
3469
+ return '';
3470
+ }
3471
+ }
3472
+ applyInitialDisabledState() {
3473
+ this.allFields.forEach(field => {
3474
+ if (field.disabledWhen) {
3475
+ const control = this.form.get(field.field);
3476
+ if (control && this.isFieldDisabled(field)) {
3477
+ control.disable();
3478
+ }
3479
+ }
3480
+ });
3481
+ }
3482
+ loadEntityData() {
3483
+ if (this.entityData && this.form) {
3484
+ const processedData = this.processEntityData(this.entityData);
3485
+ this.form.patchValue(processedData);
3486
+ }
3487
+ }
3488
+ processEntityData(entity) {
3489
+ const processedData = { ...entity };
3490
+ this.allFields.forEach(field => {
3491
+ if (field.type === 'lookup' && processedData[field.field]) {
3492
+ const lookupValue = processedData[field.field];
3493
+ // Keep the full object if it has display data, otherwise extract ID
3494
+ if (typeof lookupValue === 'object' && lookupValue.id) {
3495
+ const hasDisplayData = field.lookupDisplayFields
3496
+ ? field.lookupDisplayFields.some(f => this.getNestedValue(lookupValue, f))
3497
+ : lookupValue[field.lookupDisplayField || 'name'];
3498
+ if (hasDisplayData) {
3499
+ // Keep the full object to avoid unnecessary API calls
3500
+ processedData[field.field] = lookupValue;
3501
+ }
3502
+ else {
3503
+ // Only has ID, extract it
3504
+ processedData[field.field] = lookupValue.id;
3505
+ }
3506
+ }
3507
+ }
3508
+ });
3509
+ return processedData;
3510
+ }
3511
+ getNestedValue(obj, path) {
3512
+ return path.split('.').reduce((current, key) => current?.[key], obj);
3513
+ }
3514
+ resetForm() {
3515
+ if (this.form) {
3516
+ const resetValues = {};
3517
+ this.allFields.forEach(field => {
3518
+ resetValues[field.field] = this.getDefaultValue(field);
3519
+ });
3520
+ this.form.reset(resetValues);
3521
+ }
3522
+ }
3523
+ processFilterValue(formValue) {
3524
+ const processedValue = { ...formValue };
3525
+ this.allFields.forEach(field => {
3526
+ const fieldValue = processedValue[field.field];
3527
+ if (field.type === 'lookup') {
3528
+ if (fieldValue && typeof fieldValue === 'object' && fieldValue.id) {
3529
+ processedValue[field.field] = fieldValue;
3530
+ }
3531
+ else if (fieldValue && typeof fieldValue === 'string') {
3532
+ processedValue[field.field] = { id: fieldValue };
3533
+ }
3534
+ else {
3535
+ processedValue[field.field] = null;
3536
+ }
3537
+ }
3538
+ });
3539
+ return processedValue;
3540
+ }
3541
+ processFormValue(formValue) {
3542
+ const processedValue = { ...formValue };
3543
+ if (this.entityData?.id) {
3544
+ processedValue.id = this.entityData.id;
3545
+ }
3546
+ this.allFields.forEach(field => {
3547
+ const fieldValue = processedValue[field.field];
3548
+ if (field.type === 'lookup') {
3549
+ // If it's an object, extract the ID
3550
+ if (fieldValue && typeof fieldValue === 'object') {
3551
+ processedValue[field.field] = fieldValue.id || fieldValue[field.lookupValueField || 'id'] || null;
3552
+ }
3553
+ else if (fieldValue && fieldValue !== '') {
3554
+ // If it's already an ID, keep it
3555
+ processedValue[field.field] = { id: fieldValue };
3556
+ }
3557
+ else {
3558
+ processedValue[field.field] = null;
3559
+ }
3560
+ }
3561
+ else if (field.type === 'date') {
3562
+ if (fieldValue instanceof Date) {
3563
+ processedValue[field.field] = fieldValue.toISOString().split('T')[0];
3564
+ }
3565
+ else if (!fieldValue) {
3566
+ processedValue[field.field] = null;
3567
+ }
3568
+ }
3569
+ else if (['text', 'textarea', 'number', 'enum', 'dropdown'].includes(field.type)) {
3570
+ if (fieldValue === '' || fieldValue === undefined) {
3571
+ processedValue[field.field] = null;
3572
+ }
3573
+ }
3574
+ });
3575
+ return processedValue;
3576
+ }
3577
+ getFieldGridClass(field) {
3578
+ const classes = [];
3579
+ // Colspan base (padrão: 6)
3580
+ let baseColspan = field.colspan || 6;
3581
+ // Verifica condições de colspan dinâmico
3582
+ if (field.colspanWhen && field.colspanWhen.length > 0) {
3583
+ for (const condition of field.colspanWhen) {
3584
+ const dependentFieldValue = this.form.get(condition.field)?.value;
3585
+ if (dependentFieldValue === condition.value) {
3586
+ baseColspan = condition.colspan;
3587
+ break;
3588
+ }
3589
+ }
3590
+ }
3591
+ // Adiciona classe base
3592
+ classes.push(`col-${baseColspan}`);
3593
+ // Suporta tanto o objeto size quanto as propriedades antigas (para compatibilidade)
3594
+ const sizeXs = field.size?.xs ?? field.colspanXs;
3595
+ const sizeSm = field.size?.sm ?? field.colspanSm;
3596
+ const sizeMd = field.size?.md ?? field.colspanMd;
3597
+ const sizeLg = field.size?.lg ?? field.colspanLg;
3598
+ const sizeXl = field.size?.xl ?? field.colspanXl;
3599
+ // Adiciona classes responsivas se definidas
3600
+ if (sizeXs !== undefined) {
3601
+ classes.push(`col-xs-${sizeXs}`);
3602
+ }
3603
+ if (sizeSm !== undefined) {
3604
+ classes.push(`col-sm-${sizeSm}`);
3605
+ }
3606
+ if (sizeMd !== undefined) {
3607
+ classes.push(`col-md-${sizeMd}`);
3608
+ }
3609
+ if (sizeLg !== undefined) {
3610
+ classes.push(`col-lg-${sizeLg}`);
3611
+ }
3612
+ if (sizeXl !== undefined) {
3613
+ classes.push(`col-xl-${sizeXl}`);
3614
+ }
3615
+ return classes.join(' ');
3616
+ }
3617
+ isFieldVisible(field) {
3618
+ if (this.mode === 'filter') {
3619
+ return true;
3620
+ }
3621
+ if (!field.visibleWhen) {
3622
+ return true;
3623
+ }
3624
+ const dependentFieldValue = this.form.get(field.visibleWhen.field)?.value;
3625
+ return dependentFieldValue === field.visibleWhen.value;
3626
+ }
3627
+ isFieldDisabled(field) {
3628
+ if (field.disabled) {
3629
+ return true;
3630
+ }
3631
+ if (!('disabledWhen' in field) || !field.disabledWhen) {
3632
+ return false;
3633
+ }
3634
+ const dependentFieldValue = this.form.get(field.disabledWhen.field)?.value;
3635
+ if (field.disabledWhen.value === null) {
3636
+ return !dependentFieldValue;
3637
+ }
3638
+ return dependentFieldValue === field.disabledWhen.value;
3639
+ }
3640
+ setupConditionalFieldsWatcher() {
3641
+ const visibleDependentFields = this.allFields.filter(field => field.visibleWhen);
3642
+ const visibleWatchedFields = [...new Set(visibleDependentFields.map(field => field.visibleWhen.field))];
3643
+ const disabledDependentFields = this.allFields.filter(field => field.disabledWhen);
3644
+ const disabledWatchedFields = [...new Set(disabledDependentFields.map(field => field.disabledWhen.field))];
3645
+ const allWatchedFields = [...new Set([...visibleWatchedFields, ...disabledWatchedFields])];
3646
+ allWatchedFields.forEach(fieldName => {
3647
+ const control = this.form.get(fieldName);
3648
+ if (control) {
3649
+ control.valueChanges.subscribe(() => {
3650
+ this.clearHiddenFields();
3651
+ this.updateDisabledFields();
3652
+ });
3653
+ }
3654
+ });
3655
+ }
3656
+ updateDisabledFields() {
3657
+ this.allFields.forEach(field => {
3658
+ if (field.disabledWhen) {
3659
+ const control = this.form.get(field.field);
3660
+ if (control) {
3661
+ const shouldBeDisabled = this.isFieldDisabled(field);
3662
+ if (shouldBeDisabled && control.enabled) {
3663
+ control.setValue(null);
3664
+ control.disable();
3665
+ }
3666
+ else if (!shouldBeDisabled && control.disabled && !field.disabled) {
3667
+ control.enable();
3668
+ }
3669
+ }
3670
+ }
3671
+ });
3672
+ }
3673
+ clearHiddenFields() {
3674
+ this.allFields.forEach(field => {
3675
+ if (field.visibleWhen && !this.isFieldVisible(field)) {
3676
+ const control = this.form.get(field.field);
3677
+ if (control) {
3678
+ const resetValue = this.getDefaultValue(field);
3679
+ control.setValue(resetValue);
3680
+ control.markAsUntouched();
3681
+ control.markAsPristine();
3682
+ }
3683
+ }
3684
+ });
3685
+ }
3686
+ getForm() {
3687
+ return this.form;
3688
+ }
3689
+ clearFilters() {
3690
+ if (this.mode === 'filter') {
3691
+ this.resetForm();
3692
+ }
3693
+ }
3694
+ getTypePersonValue() {
3695
+ const typePersonValue = this.form?.get('typePerson')?.value;
3696
+ return typePersonValue || 'JURIDICAL_PERSON';
3697
+ }
3698
+ // Display Mode Control Methods
3699
+ open(entity) {
3700
+ if (entity) {
3701
+ this.entityData = entity;
3702
+ }
3703
+ else {
3704
+ this.entityData = null;
3705
+ this.resetForm(); // Reset form when opening for new entity
3706
+ }
3707
+ this.loading = false; // Reset loading state
3708
+ this.visible = true;
3709
+ this.visibleChange.emit(true);
3710
+ }
3711
+ close() {
3712
+ this.loading = false; // Reset loading state
3713
+ this.visible = false;
3714
+ this.visibleChange.emit(false);
3715
+ this.resetForm(); // Reset form when closing
3716
+ this.onCancel.emit();
3717
+ }
3718
+ handleCancel() {
3719
+ this.close();
3720
+ }
3721
+ handleSubmit() {
3722
+ if (this.mode === 'form' && this.form.invalid) {
3723
+ this.form.markAllAsTouched();
3724
+ return;
3725
+ }
3726
+ this.loading = true;
3727
+ const formValue = this.mode === 'filter'
3728
+ ? this.processFilterValue(this.form.value)
3729
+ : this.processFormValue(this.form.value);
3730
+ this.formSubmit.emit(formValue);
3731
+ }
3732
+ // Inline Edit Mode Methods
3733
+ startEditField(fieldName) {
3734
+ if (this.isInlineEditMode) {
3735
+ this.editingField = fieldName;
3736
+ // Ensure the field has the current value from entityData
3737
+ if (this.entityData) {
3738
+ const control = this.form.get(fieldName);
3739
+ if (control) {
3740
+ const field = this.allFields.find(f => f.field === fieldName);
3741
+ const value = this.entityData[fieldName];
3742
+ // For lookup fields in inline-edit mode, keep the full object
3743
+ // This prevents unnecessary API calls since we already have the data
3744
+ if (field?.type === 'lookup' && value && typeof value === 'object') {
3745
+ // Keep the full object - the lookup component will use it
3746
+ control.setValue(value);
3747
+ }
3748
+ else {
3749
+ control.setValue(value);
3750
+ }
3751
+ }
3752
+ }
3753
+ }
3754
+ }
3755
+ isFieldEditing(fieldName) {
3756
+ return this.editingField === fieldName;
3757
+ }
3758
+ saveInlineField(fieldName) {
3759
+ if (!this.isInlineEditMode)
3760
+ return;
3761
+ const control = this.form.get(fieldName);
3762
+ if (!control || control.invalid) {
3763
+ control?.markAsTouched();
3764
+ return;
3765
+ }
3766
+ this.savingField = true;
3767
+ let fieldValue = control.value;
3768
+ // For lookup fields, extract the ID if it's an object
3769
+ const field = this.allFields.find(f => f.field === fieldName);
3770
+ if (field?.type === 'lookup' && fieldValue && typeof fieldValue === 'object') {
3771
+ fieldValue = fieldValue.id || fieldValue[field.lookupValueField || 'id'];
3772
+ }
3773
+ this.fieldSave.emit({ field: fieldName, value: fieldValue });
3774
+ }
3775
+ cancelInlineEdit() {
3776
+ if (this.isInlineEditMode && this.editingField) {
3777
+ const control = this.form.get(this.editingField);
3778
+ if (control && this.entityData) {
3779
+ // Restore original value
3780
+ const originalValue = this.entityData[this.editingField];
3781
+ control.setValue(originalValue);
3782
+ control.markAsPristine();
3783
+ control.markAsUntouched();
3784
+ }
3785
+ this.editingField = null;
3786
+ this.savingField = false;
3787
+ }
3788
+ }
3789
+ // Method to be called by parent after save completes
3790
+ completeInlineEdit() {
3791
+ this.editingField = null;
3792
+ this.savingField = false;
3793
+ }
3794
+ getFieldDisplayValue(fieldName) {
3795
+ if (!this.entityData)
3796
+ return '';
3797
+ const field = this.allFields.find(f => f.field === fieldName);
3798
+ const value = this.entityData[fieldName];
3799
+ if (!value)
3800
+ return '';
3801
+ // For lookup fields, return the display name
3802
+ if (field?.type === 'lookup' && typeof value === 'object' && value.name) {
3803
+ return value.name;
3804
+ }
3805
+ // For number fields, format appropriately
3806
+ if (field?.type === 'number' && typeof value === 'number') {
3807
+ if (field.numberMode === 'currency') {
3808
+ return new Intl.NumberFormat(field.locale || 'pt-BR', {
3809
+ style: 'currency',
3810
+ currency: field.currency || 'BRL'
3811
+ }).format(value);
3812
+ }
3813
+ return new Intl.NumberFormat(field.locale || 'pt-BR', {
3814
+ minimumFractionDigits: field.minFractionDigits || 0,
3815
+ maximumFractionDigits: field.maxFractionDigits || 2
3816
+ }).format(value);
3817
+ }
3818
+ // For date fields, format as date
3819
+ if (field?.type === 'date' && value) {
3820
+ const date = typeof value === 'string' ? new Date(value) : value;
3821
+ return date.toLocaleDateString('pt-BR');
3822
+ }
3823
+ return String(value);
3824
+ }
3825
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFormComponent, deps: [{ token: i2$1.FormBuilder }, { token: TranslationService }], target: i0.ɵɵFactoryTarget.Component });
3826
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DynamicFormComponent, isStandalone: true, selector: "sia-dynamic-form", inputs: { formFields: "formFields", formSections: "formSections", fixedFilters: "fixedFilters", filterSections: "filterSections", entityData: "entityData", mode: "mode", displayMode: "displayMode", visible: "visible", dialogConfig: "dialogConfig", drawerConfig: "drawerConfig", showSubmitButton: "showSubmitButton", showCancelButton: "showCancelButton", submitButtonLabel: "submitButtonLabel", cancelButtonLabel: "cancelButtonLabel", submitButtonIcon: "submitButtonIcon", cancelButtonIcon: "cancelButtonIcon" }, outputs: { formReady: "formReady", formSubmit: "formSubmit", visibleChange: "visibleChange", onCancel: "onCancel", fieldSave: "fieldSave" }, usesOnChanges: true, ngImport: i0, template: "<!-- Modo Inline (padr\u00E3o) -->\n<ng-container *ngIf=\"isInlineMode\">\n <ng-container *ngTemplateOutlet=\"formContent\"></ng-container>\n</ng-container>\n\n<!-- Modo Dialog -->\n<p-dialog\n *ngIf=\"isDialogMode\"\n [(visible)]=\"visible\"\n [header]=\"dialogHeader | translate\"\n [modal]=\"true\"\n [style]=\"{ width: dialogWidth }\"\n [draggable]=\"dialogDraggable\"\n [resizable]=\"dialogResizable\"\n [maximizable]=\"dialogMaximizable\"\n [closeOnEscape]=\"dialogCloseOnEscape\"\n [dismissableMask]=\"dialogDismissableMask\"\n (onHide)=\"close()\">\n \n <ng-container *ngTemplateOutlet=\"formContent\"></ng-container>\n \n <ng-template pTemplate=\"footer\" *ngIf=\"showSubmitButton || showCancelButton\">\n <div class=\"dialog-footer\">\n <p-button\n *ngIf=\"showCancelButton\"\n [label]=\"cancelLabel | translate\"\n [icon]=\"cancelButtonIcon\"\n severity=\"secondary\"\n (onClick)=\"handleCancel()\"\n [disabled]=\"loading\">\n </p-button>\n <p-button\n *ngIf=\"showSubmitButton\"\n [label]=\"submitLabel | translate\"\n [icon]=\"submitButtonIcon\"\n (onClick)=\"handleSubmit()\"\n [loading]=\"loading\"\n [disabled]=\"form.invalid\">\n </p-button>\n </div>\n </ng-template>\n</p-dialog>\n\n<!-- Modo Drawer -->\n<p-drawer\n *ngIf=\"isDrawerMode\"\n [(visible)]=\"visible\"\n [header]=\"drawerHeader | translate\"\n [position]=\"drawerPosition\"\n [style]=\"drawerStyle\"\n [showCloseIcon]=\"drawerShowCloseIcon\"\n [closeOnEscape]=\"drawerCloseOnEscape\"\n [modal]=\"drawerDismissable\"\n (onHide)=\"close()\">\n \n <ng-container *ngTemplateOutlet=\"formContent\"></ng-container>\n \n <ng-template pTemplate=\"footer\" *ngIf=\"showSubmitButton || showCancelButton\">\n <div class=\"drawer-footer\">\n <p-button\n *ngIf=\"showCancelButton\"\n [label]=\"cancelLabel | translate\"\n [icon]=\"cancelButtonIcon\"\n severity=\"secondary\"\n (onClick)=\"handleCancel()\"\n [disabled]=\"loading\">\n </p-button>\n <p-button\n *ngIf=\"showSubmitButton\"\n [label]=\"submitLabel | translate\"\n [icon]=\"submitButtonIcon\"\n (onClick)=\"handleSubmit()\"\n [loading]=\"loading\"\n [disabled]=\"form.invalid\">\n </p-button>\n </div>\n </ng-template>\n</p-drawer>\n\n<!-- Template do Formul\u00E1rio (reutiliz\u00E1vel) -->\n<ng-template #formContent>\n <form [formGroup]=\"form\" class=\"dynamic-form\">\n \n <!-- Filtros Fixos (apenas no modo filter) -->\n <div *ngIf=\"mode === 'filter' && hasFixedFilters\" class=\"fixed-filters-section\">\n <div class=\"grid\">\n <ng-container *ngFor=\"let field of fixedFilters\">\n <div *ngIf=\"isFieldVisible(field)\" [ngClass]=\"getFieldGridClass(field)\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Campos Diretos (sem se\u00E7\u00E3o) -->\n <div *ngIf=\"hasFields\">\n <!-- Modo Inline Edit -->\n <div *ngIf=\"isInlineEditMode\" class=\"inline-edit-fields\">\n <ng-container *ngFor=\"let field of formFields\">\n <div *ngIf=\"isFieldVisible(field)\" class=\"inline-edit-field\">\n <ng-container *ngTemplateOutlet=\"inlineEditFieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n\n <!-- Modo Form Normal -->\n <div *ngIf=\"!isInlineEditMode\" class=\"grid\">\n <ng-container *ngFor=\"let field of formFields\">\n <div *ngIf=\"isFieldVisible(field)\" [ngClass]=\"getFieldGridClass(field)\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Se\u00E7\u00F5es de Campos (modo form) -->\n <div *ngIf=\"hasSections\" class=\"sections-container\">\n <p-panel \n *ngFor=\"let section of formSections\"\n [toggleable]=\"section.toggleable !== false\"\n [collapsed]=\"section.collapsed || false\"\n [styleClass]=\"'section-panel'\">\n \n <ng-template pTemplate=\"header\">\n <div class=\"section-header\">\n <i *ngIf=\"section.icon\" [class]=\"section.icon + ' section-icon'\"></i>\n <span class=\"section-title\">{{ section.title }}</span>\n </div>\n </ng-template>\n\n <!-- Modo Inline Edit -->\n <div *ngIf=\"isInlineEditMode\" class=\"inline-edit-fields\">\n <ng-container *ngFor=\"let field of section.fields\">\n <div *ngIf=\"isFieldVisible(field)\" class=\"inline-edit-field\">\n <ng-container *ngTemplateOutlet=\"inlineEditFieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n\n <!-- Modo Form Normal -->\n <div *ngIf=\"!isInlineEditMode\" class=\"grid\">\n <ng-container *ngFor=\"let field of section.fields\">\n <div *ngIf=\"isFieldVisible(field)\" [ngClass]=\"getFieldGridClass(field)\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n </p-panel>\n </div>\n\n <!-- Se\u00E7\u00F5es de Filtros (apenas no modo filter) -->\n <div *ngIf=\"mode === 'filter' && hasFilterSections\" class=\"filter-sections-container\">\n <p-panel \n *ngFor=\"let section of filterSections\"\n [header]=\"section.title\"\n [toggleable]=\"true\"\n [collapsed]=\"true\">\n\n <div class=\"grid\">\n <ng-container *ngFor=\"let field of section.fields\">\n <div *ngIf=\"isFieldVisible(field)\" [ngClass]=\"getFieldGridClass(field)\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n </p-panel>\n </div>\n\n <!-- Bot\u00F5es inline (apenas para modo inline) -->\n <div *ngIf=\"isInlineMode && (showSubmitButton || showCancelButton)\" class=\"form-actions\">\n <p-button\n *ngIf=\"showCancelButton\"\n [label]=\"cancelLabel | translate\"\n [icon]=\"cancelButtonIcon\"\n severity=\"secondary\"\n (onClick)=\"handleCancel()\"\n [disabled]=\"loading\">\n </p-button>\n <p-button\n *ngIf=\"showSubmitButton\"\n [label]=\"submitLabel | translate\"\n [icon]=\"submitButtonIcon\"\n (onClick)=\"handleSubmit()\"\n [loading]=\"loading\"\n [disabled]=\"form.invalid\">\n </p-button>\n </div>\n\n </form>\n</ng-template>\n\n<!-- Template de Campo (reutiliz\u00E1vel) -->\n<ng-template #fieldTemplate let-field=\"field\">\n <!-- Text Field -->\n <sia-dynamic-field-text\n *ngIf=\"field.type === 'text'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-text>\n\n <!-- Textarea Field -->\n <sia-dynamic-field-textarea\n *ngIf=\"field.type === 'textarea'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-textarea>\n\n <!-- Number Field -->\n <sia-dynamic-field-number\n *ngIf=\"field.type === 'number'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-number>\n\n <!-- Date Field -->\n <sia-dynamic-field-date\n *ngIf=\"field.type === 'date'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-date>\n\n <!-- Dropdown/Enum Field -->\n <sia-dynamic-field-dropdown\n *ngIf=\"field.type === 'dropdown' || field.type === 'enum'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-dropdown>\n\n <!-- Lookup Field -->\n <sia-dynamic-field-lookup\n *ngIf=\"field.type === 'lookup'\"\n [field]=\"field\"\n [form]=\"form\"\n [formGroup]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-lookup>\n</ng-template>\n\n\n<!-- Template de Campo Inline Edit -->\n<ng-template #inlineEditFieldTemplate let-field=\"field\">\n <div class=\"edit-inline-field\">\n <label class=\"field-label\">{{ field.label | translate }}</label>\n \n <!-- Display Mode -->\n <div class=\"field-display\" \n *ngIf=\"!isFieldEditing(field.field)\" \n (click)=\"startEditField(field.field)\"\n [class.disabled]=\"field.disabled\">\n <span class=\"display-value\" \n [attr.data-placeholder]=\"field.placeholder || ('crmx.business.not_informed' | translate)\">\n {{ getFieldDisplayValue(field.field) }}\n </span>\n <i class=\"pi pi-pencil edit-icon\" *ngIf=\"!field.disabled\"></i>\n </div>\n \n <!-- Edit Mode -->\n <div class=\"field-edit\" *ngIf=\"isFieldEditing(field.field)\">\n <div class=\"input-container\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"edit-actions\">\n <p-button \n type=\"button\" \n icon=\"pi pi-check\" \n [rounded]=\"true\"\n severity=\"primary\"\n (onClick)=\"saveInlineField(field.field)\" \n [loading]=\"savingField\"\n [disabled]=\"form.get(field.field)?.invalid\">\n </p-button>\n <p-button \n type=\"button\" \n icon=\"pi pi-times\" \n [rounded]=\"true\"\n severity=\"secondary\"\n (onClick)=\"cancelInlineEdit()\"\n [disabled]=\"savingField\">\n </p-button>\n </div>\n \n <!-- Error Message -->\n <div class=\"field-error\" *ngIf=\"form.get(field.field)?.invalid && form.get(field.field)?.touched\">\n <small class=\"p-error\">\n {{ form.get(field.field)?.errors?.['required'] ? ('crmx.business.required_field' | translate) : '' }}\n </small>\n </div>\n </div>\n </div>\n</ng-template>\n", styles: [".dynamic-form{width:100%}.dynamic-form .grid{display:grid;grid-template-columns:repeat(12,1fr);gap:1rem}.dynamic-form .grid>div.col-1{grid-column:span 1}.dynamic-form .grid>div.col-2{grid-column:span 2}.dynamic-form .grid>div.col-3{grid-column:span 3}.dynamic-form .grid>div.col-4{grid-column:span 4}.dynamic-form .grid>div.col-5{grid-column:span 5}.dynamic-form .grid>div.col-6{grid-column:span 6}.dynamic-form .grid>div.col-7{grid-column:span 7}.dynamic-form .grid>div.col-8{grid-column:span 8}.dynamic-form .grid>div.col-9{grid-column:span 9}.dynamic-form .grid>div.col-10{grid-column:span 10}.dynamic-form .grid>div.col-11{grid-column:span 11}.dynamic-form .grid>div.col-12{grid-column:span 12}@media (max-width: 575.98px){.dynamic-form .grid>div.col-xs-1{grid-column:span 1}.dynamic-form .grid>div.col-xs-2{grid-column:span 2}.dynamic-form .grid>div.col-xs-3{grid-column:span 3}.dynamic-form .grid>div.col-xs-4{grid-column:span 4}.dynamic-form .grid>div.col-xs-5{grid-column:span 5}.dynamic-form .grid>div.col-xs-6{grid-column:span 6}.dynamic-form .grid>div.col-xs-7{grid-column:span 7}.dynamic-form .grid>div.col-xs-8{grid-column:span 8}.dynamic-form .grid>div.col-xs-9{grid-column:span 9}.dynamic-form .grid>div.col-xs-10{grid-column:span 10}.dynamic-form .grid>div.col-xs-11{grid-column:span 11}.dynamic-form .grid>div.col-xs-12{grid-column:span 12}}@media (min-width: 576px){.dynamic-form .grid>div.col-sm-1{grid-column:span 1}.dynamic-form .grid>div.col-sm-2{grid-column:span 2}.dynamic-form .grid>div.col-sm-3{grid-column:span 3}.dynamic-form .grid>div.col-sm-4{grid-column:span 4}.dynamic-form .grid>div.col-sm-5{grid-column:span 5}.dynamic-form .grid>div.col-sm-6{grid-column:span 6}.dynamic-form .grid>div.col-sm-7{grid-column:span 7}.dynamic-form .grid>div.col-sm-8{grid-column:span 8}.dynamic-form .grid>div.col-sm-9{grid-column:span 9}.dynamic-form .grid>div.col-sm-10{grid-column:span 10}.dynamic-form .grid>div.col-sm-11{grid-column:span 11}.dynamic-form .grid>div.col-sm-12{grid-column:span 12}}@media (min-width: 768px){.dynamic-form .grid>div.col-md-1{grid-column:span 1}.dynamic-form .grid>div.col-md-2{grid-column:span 2}.dynamic-form .grid>div.col-md-3{grid-column:span 3}.dynamic-form .grid>div.col-md-4{grid-column:span 4}.dynamic-form .grid>div.col-md-5{grid-column:span 5}.dynamic-form .grid>div.col-md-6{grid-column:span 6}.dynamic-form .grid>div.col-md-7{grid-column:span 7}.dynamic-form .grid>div.col-md-8{grid-column:span 8}.dynamic-form .grid>div.col-md-9{grid-column:span 9}.dynamic-form .grid>div.col-md-10{grid-column:span 10}.dynamic-form .grid>div.col-md-11{grid-column:span 11}.dynamic-form .grid>div.col-md-12{grid-column:span 12}}@media (min-width: 992px){.dynamic-form .grid>div.col-lg-1{grid-column:span 1}.dynamic-form .grid>div.col-lg-2{grid-column:span 2}.dynamic-form .grid>div.col-lg-3{grid-column:span 3}.dynamic-form .grid>div.col-lg-4{grid-column:span 4}.dynamic-form .grid>div.col-lg-5{grid-column:span 5}.dynamic-form .grid>div.col-lg-6{grid-column:span 6}.dynamic-form .grid>div.col-lg-7{grid-column:span 7}.dynamic-form .grid>div.col-lg-8{grid-column:span 8}.dynamic-form .grid>div.col-lg-9{grid-column:span 9}.dynamic-form .grid>div.col-lg-10{grid-column:span 10}.dynamic-form .grid>div.col-lg-11{grid-column:span 11}.dynamic-form .grid>div.col-lg-12{grid-column:span 12}}@media (min-width: 1200px){.dynamic-form .grid>div.col-xl-1{grid-column:span 1}.dynamic-form .grid>div.col-xl-2{grid-column:span 2}.dynamic-form .grid>div.col-xl-3{grid-column:span 3}.dynamic-form .grid>div.col-xl-4{grid-column:span 4}.dynamic-form .grid>div.col-xl-5{grid-column:span 5}.dynamic-form .grid>div.col-xl-6{grid-column:span 6}.dynamic-form .grid>div.col-xl-7{grid-column:span 7}.dynamic-form .grid>div.col-xl-8{grid-column:span 8}.dynamic-form .grid>div.col-xl-9{grid-column:span 9}.dynamic-form .grid>div.col-xl-10{grid-column:span 10}.dynamic-form .grid>div.col-xl-11{grid-column:span 11}.dynamic-form .grid>div.col-xl-12{grid-column:span 12}}.dynamic-form .sections-container{display:flex;flex-direction:column;gap:1rem;margin-top:1rem}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-header{background-color:var(--surface-50);border-color:var(--surface-200)}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-header .section-header{display:flex;align-items:center;gap:.5rem;width:100%}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-header .section-header .section-icon{font-size:1.1rem;color:var(--primary-color)}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-header .section-header .section-title{font-weight:600;color:var(--text-color)}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-content{padding:1.5rem}.dynamic-form .fixed-filters-section{margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid var(--surface-200)}.dynamic-form .filter-sections-container{display:flex;flex-direction:column;gap:1rem;margin-top:1rem}.dynamic-form .form-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--surface-200)}.dialog-footer{display:flex;justify-content:flex-end;gap:.5rem}.drawer-footer{display:flex;justify-content:flex-end;gap:.5rem;padding:1rem;border-top:1px solid var(--surface-200)}.inline-edit-fields{display:flex;flex-direction:column;gap:1rem}.inline-edit-fields .inline-edit-field{display:flex;flex-direction:column;gap:.25rem}.inline-edit-fields .inline-edit-field .field-label{font-weight:500;font-size:.875rem;color:var(--text-color-secondary);margin-bottom:.25rem}.inline-edit-fields .inline-edit-field .field-display{display:flex;align-items:center;justify-content:space-between;padding:.75rem;background-color:var(--surface-50);border:1px solid var(--surface-200);border-radius:var(--border-radius);cursor:pointer;transition:all .2s ease;min-height:42px}.inline-edit-fields .inline-edit-field .field-display:hover:not(.disabled){background-color:var(--surface-100);border-color:var(--primary-color)}.inline-edit-fields .inline-edit-field .field-display:hover:not(.disabled) .edit-icon{opacity:1}.inline-edit-fields .inline-edit-field .field-display.disabled{cursor:not-allowed;opacity:.6}.inline-edit-fields .inline-edit-field .field-display .display-value{flex:1;color:var(--text-color)}.inline-edit-fields .inline-edit-field .field-display .display-value:empty:before{content:attr(data-placeholder);color:var(--text-color-secondary);font-style:italic}.inline-edit-fields .inline-edit-field .field-display .edit-icon{color:var(--primary-color);font-size:.875rem;opacity:.5;transition:opacity .2s ease}.inline-edit-fields .inline-edit-field .field-edit{display:flex;flex-direction:column;gap:.5rem}.inline-edit-fields .inline-edit-field .field-edit .input-container{flex:1}.inline-edit-fields .inline-edit-field .field-edit .input-container ::ng-deep .field{gap:0}.inline-edit-fields .inline-edit-field .field-edit .input-container ::ng-deep .field label{display:none}.inline-edit-fields .inline-edit-field .field-edit .input-container ::ng-deep .field small.p-error{display:none}.inline-edit-fields .inline-edit-field .field-edit .edit-actions{display:flex;gap:.5rem;justify-content:flex-end}.inline-edit-fields .inline-edit-field .field-edit .field-error small.p-error{color:var(--p-red-500);font-size:.875rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PanelModule }, { kind: "component", type: i4$2.Panel, selector: "p-panel", inputs: ["toggleable", "header", "collapsed", "style", "styleClass", "iconPos", "expandIcon", "collapseIcon", "showHeader", "toggler", "transitionOptions", "toggleButtonProps"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "directive", type: i5.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i7$1.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i5$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: DynamicFieldTextComponent, selector: "sia-dynamic-field-text", inputs: ["field", "form", "mode"] }, { kind: "component", type: DynamicFieldNumberComponent, selector: "sia-dynamic-field-number", inputs: ["field", "form", "mode"] }, { kind: "component", type: DynamicFieldDateComponent, selector: "sia-dynamic-field-date", inputs: ["field", "form", "mode"] }, { kind: "component", type: DynamicFieldDropdownComponent, selector: "sia-dynamic-field-dropdown", inputs: ["field", "form", "mode"] }, { kind: "component", type: DynamicFieldLookupComponent, selector: "sia-dynamic-field-lookup", inputs: ["field", "form", "formGroup", "mode"] }, { kind: "component", type: DynamicFieldTextareaComponent, selector: "sia-dynamic-field-textarea", inputs: ["field", "form", "mode"] }] });
3827
+ }
3828
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DynamicFormComponent, decorators: [{
3829
+ type: Component,
3830
+ args: [{ selector: 'sia-dynamic-form', standalone: true, imports: [
3831
+ CommonModule,
3832
+ ReactiveFormsModule,
3833
+ PanelModule,
3834
+ DialogModule,
3835
+ DrawerModule,
3836
+ ButtonModule,
3837
+ TranslatePipe,
3838
+ DynamicFieldTextComponent,
3839
+ DynamicFieldNumberComponent,
3840
+ DynamicFieldDateComponent,
3841
+ DynamicFieldDropdownComponent,
3842
+ DynamicFieldLookupComponent,
3843
+ DynamicFieldTextareaComponent
3844
+ ], template: "<!-- Modo Inline (padr\u00E3o) -->\n<ng-container *ngIf=\"isInlineMode\">\n <ng-container *ngTemplateOutlet=\"formContent\"></ng-container>\n</ng-container>\n\n<!-- Modo Dialog -->\n<p-dialog\n *ngIf=\"isDialogMode\"\n [(visible)]=\"visible\"\n [header]=\"dialogHeader | translate\"\n [modal]=\"true\"\n [style]=\"{ width: dialogWidth }\"\n [draggable]=\"dialogDraggable\"\n [resizable]=\"dialogResizable\"\n [maximizable]=\"dialogMaximizable\"\n [closeOnEscape]=\"dialogCloseOnEscape\"\n [dismissableMask]=\"dialogDismissableMask\"\n (onHide)=\"close()\">\n \n <ng-container *ngTemplateOutlet=\"formContent\"></ng-container>\n \n <ng-template pTemplate=\"footer\" *ngIf=\"showSubmitButton || showCancelButton\">\n <div class=\"dialog-footer\">\n <p-button\n *ngIf=\"showCancelButton\"\n [label]=\"cancelLabel | translate\"\n [icon]=\"cancelButtonIcon\"\n severity=\"secondary\"\n (onClick)=\"handleCancel()\"\n [disabled]=\"loading\">\n </p-button>\n <p-button\n *ngIf=\"showSubmitButton\"\n [label]=\"submitLabel | translate\"\n [icon]=\"submitButtonIcon\"\n (onClick)=\"handleSubmit()\"\n [loading]=\"loading\"\n [disabled]=\"form.invalid\">\n </p-button>\n </div>\n </ng-template>\n</p-dialog>\n\n<!-- Modo Drawer -->\n<p-drawer\n *ngIf=\"isDrawerMode\"\n [(visible)]=\"visible\"\n [header]=\"drawerHeader | translate\"\n [position]=\"drawerPosition\"\n [style]=\"drawerStyle\"\n [showCloseIcon]=\"drawerShowCloseIcon\"\n [closeOnEscape]=\"drawerCloseOnEscape\"\n [modal]=\"drawerDismissable\"\n (onHide)=\"close()\">\n \n <ng-container *ngTemplateOutlet=\"formContent\"></ng-container>\n \n <ng-template pTemplate=\"footer\" *ngIf=\"showSubmitButton || showCancelButton\">\n <div class=\"drawer-footer\">\n <p-button\n *ngIf=\"showCancelButton\"\n [label]=\"cancelLabel | translate\"\n [icon]=\"cancelButtonIcon\"\n severity=\"secondary\"\n (onClick)=\"handleCancel()\"\n [disabled]=\"loading\">\n </p-button>\n <p-button\n *ngIf=\"showSubmitButton\"\n [label]=\"submitLabel | translate\"\n [icon]=\"submitButtonIcon\"\n (onClick)=\"handleSubmit()\"\n [loading]=\"loading\"\n [disabled]=\"form.invalid\">\n </p-button>\n </div>\n </ng-template>\n</p-drawer>\n\n<!-- Template do Formul\u00E1rio (reutiliz\u00E1vel) -->\n<ng-template #formContent>\n <form [formGroup]=\"form\" class=\"dynamic-form\">\n \n <!-- Filtros Fixos (apenas no modo filter) -->\n <div *ngIf=\"mode === 'filter' && hasFixedFilters\" class=\"fixed-filters-section\">\n <div class=\"grid\">\n <ng-container *ngFor=\"let field of fixedFilters\">\n <div *ngIf=\"isFieldVisible(field)\" [ngClass]=\"getFieldGridClass(field)\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Campos Diretos (sem se\u00E7\u00E3o) -->\n <div *ngIf=\"hasFields\">\n <!-- Modo Inline Edit -->\n <div *ngIf=\"isInlineEditMode\" class=\"inline-edit-fields\">\n <ng-container *ngFor=\"let field of formFields\">\n <div *ngIf=\"isFieldVisible(field)\" class=\"inline-edit-field\">\n <ng-container *ngTemplateOutlet=\"inlineEditFieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n\n <!-- Modo Form Normal -->\n <div *ngIf=\"!isInlineEditMode\" class=\"grid\">\n <ng-container *ngFor=\"let field of formFields\">\n <div *ngIf=\"isFieldVisible(field)\" [ngClass]=\"getFieldGridClass(field)\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Se\u00E7\u00F5es de Campos (modo form) -->\n <div *ngIf=\"hasSections\" class=\"sections-container\">\n <p-panel \n *ngFor=\"let section of formSections\"\n [toggleable]=\"section.toggleable !== false\"\n [collapsed]=\"section.collapsed || false\"\n [styleClass]=\"'section-panel'\">\n \n <ng-template pTemplate=\"header\">\n <div class=\"section-header\">\n <i *ngIf=\"section.icon\" [class]=\"section.icon + ' section-icon'\"></i>\n <span class=\"section-title\">{{ section.title }}</span>\n </div>\n </ng-template>\n\n <!-- Modo Inline Edit -->\n <div *ngIf=\"isInlineEditMode\" class=\"inline-edit-fields\">\n <ng-container *ngFor=\"let field of section.fields\">\n <div *ngIf=\"isFieldVisible(field)\" class=\"inline-edit-field\">\n <ng-container *ngTemplateOutlet=\"inlineEditFieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n\n <!-- Modo Form Normal -->\n <div *ngIf=\"!isInlineEditMode\" class=\"grid\">\n <ng-container *ngFor=\"let field of section.fields\">\n <div *ngIf=\"isFieldVisible(field)\" [ngClass]=\"getFieldGridClass(field)\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n </p-panel>\n </div>\n\n <!-- Se\u00E7\u00F5es de Filtros (apenas no modo filter) -->\n <div *ngIf=\"mode === 'filter' && hasFilterSections\" class=\"filter-sections-container\">\n <p-panel \n *ngFor=\"let section of filterSections\"\n [header]=\"section.title\"\n [toggleable]=\"true\"\n [collapsed]=\"true\">\n\n <div class=\"grid\">\n <ng-container *ngFor=\"let field of section.fields\">\n <div *ngIf=\"isFieldVisible(field)\" [ngClass]=\"getFieldGridClass(field)\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n </ng-container>\n </div>\n </p-panel>\n </div>\n\n <!-- Bot\u00F5es inline (apenas para modo inline) -->\n <div *ngIf=\"isInlineMode && (showSubmitButton || showCancelButton)\" class=\"form-actions\">\n <p-button\n *ngIf=\"showCancelButton\"\n [label]=\"cancelLabel | translate\"\n [icon]=\"cancelButtonIcon\"\n severity=\"secondary\"\n (onClick)=\"handleCancel()\"\n [disabled]=\"loading\">\n </p-button>\n <p-button\n *ngIf=\"showSubmitButton\"\n [label]=\"submitLabel | translate\"\n [icon]=\"submitButtonIcon\"\n (onClick)=\"handleSubmit()\"\n [loading]=\"loading\"\n [disabled]=\"form.invalid\">\n </p-button>\n </div>\n\n </form>\n</ng-template>\n\n<!-- Template de Campo (reutiliz\u00E1vel) -->\n<ng-template #fieldTemplate let-field=\"field\">\n <!-- Text Field -->\n <sia-dynamic-field-text\n *ngIf=\"field.type === 'text'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-text>\n\n <!-- Textarea Field -->\n <sia-dynamic-field-textarea\n *ngIf=\"field.type === 'textarea'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-textarea>\n\n <!-- Number Field -->\n <sia-dynamic-field-number\n *ngIf=\"field.type === 'number'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-number>\n\n <!-- Date Field -->\n <sia-dynamic-field-date\n *ngIf=\"field.type === 'date'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-date>\n\n <!-- Dropdown/Enum Field -->\n <sia-dynamic-field-dropdown\n *ngIf=\"field.type === 'dropdown' || field.type === 'enum'\"\n [field]=\"field\"\n [form]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-dropdown>\n\n <!-- Lookup Field -->\n <sia-dynamic-field-lookup\n *ngIf=\"field.type === 'lookup'\"\n [field]=\"field\"\n [form]=\"form\"\n [formGroup]=\"form\"\n [mode]=\"fieldMode\">\n </sia-dynamic-field-lookup>\n</ng-template>\n\n\n<!-- Template de Campo Inline Edit -->\n<ng-template #inlineEditFieldTemplate let-field=\"field\">\n <div class=\"edit-inline-field\">\n <label class=\"field-label\">{{ field.label | translate }}</label>\n \n <!-- Display Mode -->\n <div class=\"field-display\" \n *ngIf=\"!isFieldEditing(field.field)\" \n (click)=\"startEditField(field.field)\"\n [class.disabled]=\"field.disabled\">\n <span class=\"display-value\" \n [attr.data-placeholder]=\"field.placeholder || ('crmx.business.not_informed' | translate)\">\n {{ getFieldDisplayValue(field.field) }}\n </span>\n <i class=\"pi pi-pencil edit-icon\" *ngIf=\"!field.disabled\"></i>\n </div>\n \n <!-- Edit Mode -->\n <div class=\"field-edit\" *ngIf=\"isFieldEditing(field.field)\">\n <div class=\"input-container\">\n <ng-container *ngTemplateOutlet=\"fieldTemplate; context: { field: field }\"></ng-container>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"edit-actions\">\n <p-button \n type=\"button\" \n icon=\"pi pi-check\" \n [rounded]=\"true\"\n severity=\"primary\"\n (onClick)=\"saveInlineField(field.field)\" \n [loading]=\"savingField\"\n [disabled]=\"form.get(field.field)?.invalid\">\n </p-button>\n <p-button \n type=\"button\" \n icon=\"pi pi-times\" \n [rounded]=\"true\"\n severity=\"secondary\"\n (onClick)=\"cancelInlineEdit()\"\n [disabled]=\"savingField\">\n </p-button>\n </div>\n \n <!-- Error Message -->\n <div class=\"field-error\" *ngIf=\"form.get(field.field)?.invalid && form.get(field.field)?.touched\">\n <small class=\"p-error\">\n {{ form.get(field.field)?.errors?.['required'] ? ('crmx.business.required_field' | translate) : '' }}\n </small>\n </div>\n </div>\n </div>\n</ng-template>\n", styles: [".dynamic-form{width:100%}.dynamic-form .grid{display:grid;grid-template-columns:repeat(12,1fr);gap:1rem}.dynamic-form .grid>div.col-1{grid-column:span 1}.dynamic-form .grid>div.col-2{grid-column:span 2}.dynamic-form .grid>div.col-3{grid-column:span 3}.dynamic-form .grid>div.col-4{grid-column:span 4}.dynamic-form .grid>div.col-5{grid-column:span 5}.dynamic-form .grid>div.col-6{grid-column:span 6}.dynamic-form .grid>div.col-7{grid-column:span 7}.dynamic-form .grid>div.col-8{grid-column:span 8}.dynamic-form .grid>div.col-9{grid-column:span 9}.dynamic-form .grid>div.col-10{grid-column:span 10}.dynamic-form .grid>div.col-11{grid-column:span 11}.dynamic-form .grid>div.col-12{grid-column:span 12}@media (max-width: 575.98px){.dynamic-form .grid>div.col-xs-1{grid-column:span 1}.dynamic-form .grid>div.col-xs-2{grid-column:span 2}.dynamic-form .grid>div.col-xs-3{grid-column:span 3}.dynamic-form .grid>div.col-xs-4{grid-column:span 4}.dynamic-form .grid>div.col-xs-5{grid-column:span 5}.dynamic-form .grid>div.col-xs-6{grid-column:span 6}.dynamic-form .grid>div.col-xs-7{grid-column:span 7}.dynamic-form .grid>div.col-xs-8{grid-column:span 8}.dynamic-form .grid>div.col-xs-9{grid-column:span 9}.dynamic-form .grid>div.col-xs-10{grid-column:span 10}.dynamic-form .grid>div.col-xs-11{grid-column:span 11}.dynamic-form .grid>div.col-xs-12{grid-column:span 12}}@media (min-width: 576px){.dynamic-form .grid>div.col-sm-1{grid-column:span 1}.dynamic-form .grid>div.col-sm-2{grid-column:span 2}.dynamic-form .grid>div.col-sm-3{grid-column:span 3}.dynamic-form .grid>div.col-sm-4{grid-column:span 4}.dynamic-form .grid>div.col-sm-5{grid-column:span 5}.dynamic-form .grid>div.col-sm-6{grid-column:span 6}.dynamic-form .grid>div.col-sm-7{grid-column:span 7}.dynamic-form .grid>div.col-sm-8{grid-column:span 8}.dynamic-form .grid>div.col-sm-9{grid-column:span 9}.dynamic-form .grid>div.col-sm-10{grid-column:span 10}.dynamic-form .grid>div.col-sm-11{grid-column:span 11}.dynamic-form .grid>div.col-sm-12{grid-column:span 12}}@media (min-width: 768px){.dynamic-form .grid>div.col-md-1{grid-column:span 1}.dynamic-form .grid>div.col-md-2{grid-column:span 2}.dynamic-form .grid>div.col-md-3{grid-column:span 3}.dynamic-form .grid>div.col-md-4{grid-column:span 4}.dynamic-form .grid>div.col-md-5{grid-column:span 5}.dynamic-form .grid>div.col-md-6{grid-column:span 6}.dynamic-form .grid>div.col-md-7{grid-column:span 7}.dynamic-form .grid>div.col-md-8{grid-column:span 8}.dynamic-form .grid>div.col-md-9{grid-column:span 9}.dynamic-form .grid>div.col-md-10{grid-column:span 10}.dynamic-form .grid>div.col-md-11{grid-column:span 11}.dynamic-form .grid>div.col-md-12{grid-column:span 12}}@media (min-width: 992px){.dynamic-form .grid>div.col-lg-1{grid-column:span 1}.dynamic-form .grid>div.col-lg-2{grid-column:span 2}.dynamic-form .grid>div.col-lg-3{grid-column:span 3}.dynamic-form .grid>div.col-lg-4{grid-column:span 4}.dynamic-form .grid>div.col-lg-5{grid-column:span 5}.dynamic-form .grid>div.col-lg-6{grid-column:span 6}.dynamic-form .grid>div.col-lg-7{grid-column:span 7}.dynamic-form .grid>div.col-lg-8{grid-column:span 8}.dynamic-form .grid>div.col-lg-9{grid-column:span 9}.dynamic-form .grid>div.col-lg-10{grid-column:span 10}.dynamic-form .grid>div.col-lg-11{grid-column:span 11}.dynamic-form .grid>div.col-lg-12{grid-column:span 12}}@media (min-width: 1200px){.dynamic-form .grid>div.col-xl-1{grid-column:span 1}.dynamic-form .grid>div.col-xl-2{grid-column:span 2}.dynamic-form .grid>div.col-xl-3{grid-column:span 3}.dynamic-form .grid>div.col-xl-4{grid-column:span 4}.dynamic-form .grid>div.col-xl-5{grid-column:span 5}.dynamic-form .grid>div.col-xl-6{grid-column:span 6}.dynamic-form .grid>div.col-xl-7{grid-column:span 7}.dynamic-form .grid>div.col-xl-8{grid-column:span 8}.dynamic-form .grid>div.col-xl-9{grid-column:span 9}.dynamic-form .grid>div.col-xl-10{grid-column:span 10}.dynamic-form .grid>div.col-xl-11{grid-column:span 11}.dynamic-form .grid>div.col-xl-12{grid-column:span 12}}.dynamic-form .sections-container{display:flex;flex-direction:column;gap:1rem;margin-top:1rem}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-header{background-color:var(--surface-50);border-color:var(--surface-200)}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-header .section-header{display:flex;align-items:center;gap:.5rem;width:100%}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-header .section-header .section-icon{font-size:1.1rem;color:var(--primary-color)}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-header .section-header .section-title{font-weight:600;color:var(--text-color)}.dynamic-form .sections-container ::ng-deep .section-panel .p-panel-content{padding:1.5rem}.dynamic-form .fixed-filters-section{margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid var(--surface-200)}.dynamic-form .filter-sections-container{display:flex;flex-direction:column;gap:1rem;margin-top:1rem}.dynamic-form .form-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--surface-200)}.dialog-footer{display:flex;justify-content:flex-end;gap:.5rem}.drawer-footer{display:flex;justify-content:flex-end;gap:.5rem;padding:1rem;border-top:1px solid var(--surface-200)}.inline-edit-fields{display:flex;flex-direction:column;gap:1rem}.inline-edit-fields .inline-edit-field{display:flex;flex-direction:column;gap:.25rem}.inline-edit-fields .inline-edit-field .field-label{font-weight:500;font-size:.875rem;color:var(--text-color-secondary);margin-bottom:.25rem}.inline-edit-fields .inline-edit-field .field-display{display:flex;align-items:center;justify-content:space-between;padding:.75rem;background-color:var(--surface-50);border:1px solid var(--surface-200);border-radius:var(--border-radius);cursor:pointer;transition:all .2s ease;min-height:42px}.inline-edit-fields .inline-edit-field .field-display:hover:not(.disabled){background-color:var(--surface-100);border-color:var(--primary-color)}.inline-edit-fields .inline-edit-field .field-display:hover:not(.disabled) .edit-icon{opacity:1}.inline-edit-fields .inline-edit-field .field-display.disabled{cursor:not-allowed;opacity:.6}.inline-edit-fields .inline-edit-field .field-display .display-value{flex:1;color:var(--text-color)}.inline-edit-fields .inline-edit-field .field-display .display-value:empty:before{content:attr(data-placeholder);color:var(--text-color-secondary);font-style:italic}.inline-edit-fields .inline-edit-field .field-display .edit-icon{color:var(--primary-color);font-size:.875rem;opacity:.5;transition:opacity .2s ease}.inline-edit-fields .inline-edit-field .field-edit{display:flex;flex-direction:column;gap:.5rem}.inline-edit-fields .inline-edit-field .field-edit .input-container{flex:1}.inline-edit-fields .inline-edit-field .field-edit .input-container ::ng-deep .field{gap:0}.inline-edit-fields .inline-edit-field .field-edit .input-container ::ng-deep .field label{display:none}.inline-edit-fields .inline-edit-field .field-edit .input-container ::ng-deep .field small.p-error{display:none}.inline-edit-fields .inline-edit-field .field-edit .edit-actions{display:flex;gap:.5rem;justify-content:flex-end}.inline-edit-fields .inline-edit-field .field-edit .field-error small.p-error{color:var(--p-red-500);font-size:.875rem}\n"] }]
3845
+ }], ctorParameters: () => [{ type: i2$1.FormBuilder }, { type: TranslationService }], propDecorators: { formFields: [{
3846
+ type: Input
3847
+ }], formSections: [{
3848
+ type: Input
3849
+ }], fixedFilters: [{
3850
+ type: Input
3851
+ }], filterSections: [{
3852
+ type: Input
3853
+ }], entityData: [{
3854
+ type: Input
3855
+ }], mode: [{
3856
+ type: Input
3857
+ }], displayMode: [{
3858
+ type: Input
3859
+ }], visible: [{
3860
+ type: Input
3861
+ }], dialogConfig: [{
3862
+ type: Input
3863
+ }], drawerConfig: [{
3864
+ type: Input
3865
+ }], showSubmitButton: [{
3866
+ type: Input
3867
+ }], showCancelButton: [{
3868
+ type: Input
3869
+ }], submitButtonLabel: [{
3870
+ type: Input
3871
+ }], cancelButtonLabel: [{
3872
+ type: Input
3873
+ }], submitButtonIcon: [{
3874
+ type: Input
3875
+ }], cancelButtonIcon: [{
3876
+ type: Input
3877
+ }], formReady: [{
3878
+ type: Output
3879
+ }], formSubmit: [{
3880
+ type: Output
3881
+ }], visibleChange: [{
3882
+ type: Output
3883
+ }], onCancel: [{
3884
+ type: Output
3885
+ }], fieldSave: [{
3886
+ type: Output
3887
+ }] } });
3888
+
3889
+ const apiInterceptor = (req, next) => {
3890
+ const authService = inject(AuthService);
3891
+ const router = inject(Router);
3892
+ // Get the access token, token type, and base URL
3893
+ const accessToken = authService.getAccessToken();
3894
+ const tokenType = authService.getTokenType();
3895
+ const baseUrl = authService.getApiBaseUrl();
3896
+ let modifiedReq = req;
3897
+ // Add Authorization header if token exists
3898
+ if (accessToken && tokenType) {
3899
+ const authHeader = `${tokenType} ${accessToken}`;
3900
+ modifiedReq = modifiedReq.clone({
3901
+ setHeaders: {
3902
+ Authorization: authHeader
3903
+ }
3904
+ });
3905
+ }
3906
+ // Prepend base URL if it exists and the request URL is relative
3907
+ if (baseUrl && !isAbsoluteUrl(modifiedReq.url)) {
3908
+ const fullUrl = combineUrls(baseUrl, modifiedReq.url);
3909
+ modifiedReq = modifiedReq.clone({
3910
+ url: fullUrl
3911
+ });
3912
+ }
3913
+ return next(modifiedReq).pipe(catchError$1((error) => {
3914
+ // Handle 401 Unauthorized errors
3915
+ if (error.status === 401) {
3916
+ // Redirect to forbidden route
3917
+ router.navigate(['/forbidden']);
3918
+ }
3919
+ // Re-throw the error so other error handlers can process it
3920
+ return throwError(() => error);
3921
+ }));
3922
+ };
3923
+ /**
3924
+ * Check if URL is absolute
3925
+ */
3926
+ function isAbsoluteUrl(url) {
3927
+ return /^https?:\/\//.test(url);
3928
+ }
3929
+ /**
3930
+ * Combine base URL with relative URL
3931
+ */
3932
+ function combineUrls(baseUrl, relativeUrl) {
3933
+ // Remove trailing slash from base URL
3934
+ const cleanBaseUrl = baseUrl.replace(/\/$/, '');
3935
+ // Remove leading slash from relative URL
3936
+ const cleanRelativeUrl = relativeUrl.replace(/^\//, '');
3937
+ return `${cleanBaseUrl}/${cleanRelativeUrl}`;
3938
+ }
3939
+
3940
+ /*
3941
+ * Public API Surface of @seniorsistemas/components-ia
3942
+ *
3943
+ * Este arquivo será populado conforme os componentes forem migrados.
3944
+ * Por enquanto, exporta apenas o módulo principal.
3945
+ */
3946
+ // ============================================================================
3947
+ // MODULE
3948
+ // ============================================================================
3949
+ // ============================================================================
3950
+ // NOTA: Os exports abaixo serão descomentados conforme os arquivos forem migrados
3951
+ // ============================================================================
3952
+ // COMPONENTS - BASE
3953
+ // export * from './lib/components/base/entity-list-base/entity-list-base.component';
3954
+ // export * from './lib/components/base/entity-form-base/entity-form-base.component';
3955
+ // COMPONENTS - FORMS
3956
+ // export * from './lib/components/forms/entity-form-fields/entity-form-fields.component';
3957
+ // export * from './lib/components/forms/entity-form-dialog/entity-form-dialog.component';
3958
+ // export * from './lib/components/forms/entity-form-drawer/entity-form-drawer.component';
3959
+ // export * from './lib/components/forms/sub-resource-form-dialog/sub-resource-form-dialog.component';
3960
+ // COMPONENTS - LISTS
3961
+ // export * from './lib/components/lists/form-filter/form-filter.component';
3962
+ // export * from './lib/components/lists/export-dialog/export-dialog.component';
3963
+ // export * from './lib/components/lists/bulk-delete-dialog/bulk-delete-dialog.component';
3964
+ // COMPONENTS - UI
3965
+ // export * from './lib/components/ui/breadcrumb/breadcrumb.component';
3966
+ // export * from './lib/components/ui/edit-inline/edit-inline.component';
3967
+ // export * from './lib/components/ui/lookup-field/lookup-field.component';
3968
+ // export * from './lib/components/ui/kanban-board/kanban-board.component';
3969
+ // export * from './lib/components/ui/theme-toggle/theme-toggle.component';
3970
+ // SERVICES
3971
+ // export * from './lib/services/entity.service';
3972
+ // export * from './lib/services/auth.service';
3973
+ // export * from './lib/services/translation.service';
3974
+ // export * from './lib/services/theme.service';
3975
+ // export * from './lib/services/permission.service';
3976
+ // export * from './lib/services/cookie.service';
3977
+ // export * from './lib/services/senior-token.service';
3978
+ // export * from './lib/services/mask.service';
3979
+ // DIRECTIVES
3980
+ // export * from './lib/directives/cep-mask.directive';
3981
+ // export * from './lib/directives/document-mask.directive';
3982
+ // export * from './lib/directives/phone-mask.directive';
3983
+ // PIPES
3984
+ // export * from './lib/pipes/translate.pipe';
3985
+ // INTERCEPTORS
3986
+ // export * from './lib/interceptors/api.interceptor';
3987
+ // MODELS - INTERFACES
3988
+ // export * from './lib/models/interfaces/base-entity.interface';
3989
+ // export * from './lib/models/interfaces/entity-config.interface';
3990
+ // export * from './lib/models/interfaces/entity-list.config';
3991
+ // MODELS - ENUMS
3992
+ // export * from './lib/models/enums/status.enum';
3993
+ // export * from './lib/models/enums/type-person.enum';
3994
+ // UTILS
3995
+ // export * from './lib/utils/utils';
3996
+ // I18N
3997
+ // export * from './lib/i18n/translation.config';
3998
+ // export * from './lib/i18n/locale.config';
3999
+ // export * from './lib/i18n/fallback';
4000
+
4001
+ /**
4002
+ * Generated bundle index. Do not edit.
4003
+ */
4004
+
4005
+ export { AngularComponentsModule, AuthService, BreadcrumbComponent, BulkDeleteDialogComponent, CookieService, DEFAULT_LANGUAGE, DynamicFieldDateComponent, DynamicFieldDropdownComponent, DynamicFieldLookupComponent, DynamicFieldNumberComponent, DynamicFieldTextComponent, DynamicFieldTextareaComponent, DynamicFieldWrapperComponent, DynamicFormComponent, EntityService, ExportDialogComponent, PermissionService, SUPPORTED_LANGUAGES, SeniorTokenService, TRANSLATION_CONFIG, ThemeService, TranslationService, apiInterceptor, getLanguageInfo, mapTokenLocaleToLanguage };
4006
+ //# sourceMappingURL=seniorsistemas-components-ai.mjs.map