@elite.framework/ng.ui.core 1.0.81 → 1.0.82

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.
@@ -1,33 +1,31 @@
1
1
  import * as i0 from '@angular/core';
2
- import { NgModule, inject, Input, ViewChild, Component } from '@angular/core';
3
- import * as i2 from '@angular/common';
2
+ import { NgModule, inject, Component, Input, ViewChild } from '@angular/core';
3
+ import * as i2$1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
- import * as i3 from '@angular/forms';
6
- import { FormGroup, ReactiveFormsModule, FormsModule } from '@angular/forms';
7
- import * as i9 from '@ngx-formly/core';
8
- import { FormlyModule } from '@ngx-formly/core';
9
- import * as i11 from '@ngx-translate/core';
5
+ import * as i1 from '@angular/forms';
6
+ import { FormsModule, FormGroup, ReactiveFormsModule } from '@angular/forms';
7
+ import { FormlyForm } from '@ngx-formly/core';
8
+ import * as i4 from '@ngx-translate/core';
10
9
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
11
- import * as i4 from 'primeng/button';
10
+ import * as i2 from 'primeng/button';
12
11
  import { ButtonModule } from 'primeng/button';
13
12
  import { DialogModule } from 'primeng/dialog';
14
- import { MultiSelectModule } from 'primeng/multiselect';
13
+ import * as i3 from 'primeng/inputtext';
15
14
  import { InputTextModule } from 'primeng/inputtext';
16
- import * as i6 from 'primeng/radiobutton';
17
15
  import { RadioButtonModule } from 'primeng/radiobutton';
18
- import * as i7 from 'primeng/checkbox';
19
16
  import { CheckboxModule } from 'primeng/checkbox';
20
- import { CardModule } from 'primeng/card';
21
- import { DividerModule } from 'primeng/divider';
22
- import * as i8 from 'primeng/tabs';
23
- import { TabsModule } from 'primeng/tabs';
24
- import * as i1 from 'primeng/dynamicdialog';
25
- import { DynamicDialogModule, DialogService } from 'primeng/dynamicdialog';
26
- import { SelectModule } from 'primeng/select';
17
+ import * as i1$1 from 'primeng/dynamicdialog';
18
+ import { DynamicDialogRef, DynamicDialogConfig, DialogService } from 'primeng/dynamicdialog';
27
19
  import { RestService } from '@elite.framework/ng.core/services';
28
20
  import { Subject, takeUntil } from 'rxjs';
29
- import * as i10 from 'primeng/drawer';
21
+ import * as i6 from 'primeng/drawer';
30
22
  import { DrawerModule } from 'primeng/drawer';
23
+ import { TableModule } from 'primeng/table';
24
+ import * as i7 from 'primeng/splitbutton';
25
+ import { SplitButtonModule } from 'primeng/splitbutton';
26
+ import * as i8 from 'primeng/tooltip';
27
+ import { TooltipModule } from 'primeng/tooltip';
28
+ import { DomSanitizer } from '@angular/platform-browser';
31
29
  import { QueryBuilderService } from '@elite.framework/ng.ui.core/generic-search-advanced';
32
30
  import * as i5 from 'primeng/api';
33
31
 
@@ -43,6 +41,103 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
43
41
  }]
44
42
  }] });
45
43
 
44
+ // report-name-dialog.component.ts
45
+ class ReportNameDialogComponent {
46
+ fileName = '';
47
+ ref = inject(DynamicDialogRef);
48
+ config = inject(DynamicDialogConfig);
49
+ translate = inject(TranslateService);
50
+ ngOnInit() {
51
+ this.fileName = this.config.data?.fileName || '';
52
+ }
53
+ save() {
54
+ if (this.fileName?.trim()) {
55
+ this.ref.close(this.fileName.trim());
56
+ }
57
+ }
58
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: ReportNameDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
59
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: ReportNameDialogComponent, isStandalone: true, selector: "app-report-name-dialog", ngImport: i0, template: `
60
+ <div class="p-fluid">
61
+ <div class="field">
62
+ <label for="fileName" class="block text-sm font-medium mb-2">
63
+ {{ 'REPORT_NAME' | translate }}
64
+ </label>
65
+ <input
66
+ id="fileName"
67
+ type="text"
68
+ [(ngModel)]="fileName"
69
+ pInputText
70
+ class="w-full"
71
+ [placeholder]="'ENTER_REPORT_NAME' | translate"
72
+ autofocus>
73
+ </div>
74
+ </div>
75
+
76
+ <div class="flex justify-end gap-2 mt-4">
77
+ <button
78
+ type="button"
79
+ pButton
80
+ class="p-button-text"
81
+ (click)="ref.close()"
82
+ [label]="'CANCEL' | translate">
83
+ </button>
84
+ <button
85
+ type="button"
86
+ pButton
87
+ (click)="save()"
88
+ [label]="'SAVE' | translate">
89
+ </button>
90
+ </div>
91
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }] });
92
+ }
93
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: ReportNameDialogComponent, decorators: [{
94
+ type: Component,
95
+ args: [{
96
+ selector: 'app-report-name-dialog',
97
+ standalone: true,
98
+ imports: [
99
+ CommonModule,
100
+ FormsModule,
101
+ TranslateModule,
102
+ ButtonModule,
103
+ InputTextModule
104
+ ],
105
+ template: `
106
+ <div class="p-fluid">
107
+ <div class="field">
108
+ <label for="fileName" class="block text-sm font-medium mb-2">
109
+ {{ 'REPORT_NAME' | translate }}
110
+ </label>
111
+ <input
112
+ id="fileName"
113
+ type="text"
114
+ [(ngModel)]="fileName"
115
+ pInputText
116
+ class="w-full"
117
+ [placeholder]="'ENTER_REPORT_NAME' | translate"
118
+ autofocus>
119
+ </div>
120
+ </div>
121
+
122
+ <div class="flex justify-end gap-2 mt-4">
123
+ <button
124
+ type="button"
125
+ pButton
126
+ class="p-button-text"
127
+ (click)="ref.close()"
128
+ [label]="'CANCEL' | translate">
129
+ </button>
130
+ <button
131
+ type="button"
132
+ pButton
133
+ (click)="save()"
134
+ [label]="'SAVE' | translate">
135
+ </button>
136
+ </div>
137
+ `
138
+ }]
139
+ }] });
140
+
46
141
  var ExportType;
47
142
  (function (ExportType) {
48
143
  ExportType[ExportType["LIST"] = 0] = "LIST";
@@ -57,14 +152,15 @@ class GenericReportComponent {
57
152
  previewContainer;
58
153
  filterFields = [];
59
154
  fields_ = [];
60
- availableColumns = [];
61
- exportType = ExportType.EXCEL;
155
+ exportType = ExportType.PDF;
62
156
  fileName = '';
63
157
  showFilters = false;
64
158
  loading = false;
65
- activeTabIndex = 0;
66
159
  currentDate = new Date();
67
160
  previewData = [];
161
+ htmlExportContent = '';
162
+ showHtmlInIframe = false;
163
+ previewColumns = [];
68
164
  options = {};
69
165
  enableQueryBuilder = true;
70
166
  localExport = false;
@@ -72,40 +168,49 @@ class GenericReportComponent {
72
168
  apiName = '';
73
169
  model = {};
74
170
  form = new FormGroup({});
75
- exportConfig = {
76
- columns: {},
77
- filters: this.model,
78
- exportType: this.exportType,
79
- fileName: this.fileName
80
- };
171
+ exportConfig;
81
172
  exportTypes = [
82
173
  { value: ExportType.EXCEL, label: 'EXCEL', icon: 'pi pi-file-excel' },
83
174
  { value: ExportType.PDF, label: 'PDF', icon: 'pi pi-file-pdf' },
84
175
  { value: ExportType.HTML, label: 'HTML', icon: 'pi pi-code' },
85
176
  { value: ExportType.PRINT, label: 'PRINT', icon: 'pi pi-print' }
86
177
  ];
178
+ exportMenuItems = [];
87
179
  destroy$ = new Subject();
180
+ dialogService = inject(DialogService);
88
181
  translate = inject(TranslateService);
89
182
  restService = inject(RestService);
90
183
  queryBuilder = inject(QueryBuilderService);
184
+ sanitizer = inject(DomSanitizer);
91
185
  constructor(ref, config) {
92
186
  this.ref = ref;
93
187
  this.config = config;
94
188
  }
189
+ ngOnDestroy() {
190
+ this.destroy$.next();
191
+ this.destroy$.complete();
192
+ if (this.ref) {
193
+ try {
194
+ this.ref.close();
195
+ }
196
+ catch (e) {
197
+ // Ignore errors during cleanup
198
+ }
199
+ }
200
+ }
95
201
  ngOnInit() {
96
202
  this.filterFields = this.config.data?.filterFields || [];
97
- this.initializeComponent();
98
203
  this.buildQueryUIFields();
204
+ this.initializeComponent();
205
+ this.initializeExportConfig();
206
+ this.initializeExportMenu();
99
207
  }
100
208
  get filterCount() {
101
- const filters = ((this.model?.advancedFilters ?? [])[0]?.conditions?.length || 0) +
102
- ((this.model?.sorting ?? [])?.length || 0) +
103
- (this.model?.pagination && this.model?.pagination?.top ? 1 : 0);
104
- return filters;
209
+ return this.queryBuilder.getAppliedFiltersCount(this.model).total;
105
210
  }
106
211
  buildQueryUIFields() {
107
212
  if (this.enableQueryBuilder) {
108
- this.fields_ = this.queryBuilder.buildQueryUIFields(this.filterFields);
213
+ this.fields_ = this.queryBuilder.buildQueryUIFields(this.filterFields, true, true);
109
214
  }
110
215
  else {
111
216
  this.fields_ = this.filterFields;
@@ -113,32 +218,52 @@ class GenericReportComponent {
113
218
  }
114
219
  initializeComponent() {
115
220
  this.apiName = this.config.data?.apiName || '';
116
- const columnDefinitions = this.config.data?.columns || {};
117
- if (Object.keys(columnDefinitions).length > 0) {
118
- this.availableColumns = Object.keys(columnDefinitions).map(key => ({
119
- key: String(key),
120
- displayName: String(columnDefinitions[key]),
121
- selected: true,
122
- customName: String(columnDefinitions[key])
123
- }));
124
- }
125
- else {
126
- this.availableColumns = (this.filterFields || []).map(f => {
127
- const key = String(f.key ?? '');
128
- const label = String(f.props?.label ?? key);
129
- return {
130
- key,
131
- displayName: this.translate.instant(label),
132
- selected: true,
133
- customName: label
134
- };
135
- });
136
- }
221
+ this.initializeFilterFields();
222
+ this.onExport();
223
+ }
224
+ initializeFilterFields() {
137
225
  this.filterFields = this.config.data?.filterFields || this.getDefaultFilterFields();
138
226
  this.model = this.config.data?.model || {};
139
227
  this.fileName = this.config.data?.defaultFileName || `export_${new Date().toISOString().split('T')[0]}`;
140
- // Load sample data for preview
141
- this.loadPreviewData();
228
+ }
229
+ initializeExportConfig() {
230
+ this.exportConfig = {
231
+ filters: this.model,
232
+ exportType: this.exportType,
233
+ fileName: this.fileName
234
+ };
235
+ }
236
+ initializeExportMenu() {
237
+ this.exportMenuItems = [
238
+ // Edit Report Name menu item
239
+ {
240
+ label: this.translate.instant('EDIT_REPORT_NAME'),
241
+ icon: 'pi pi-pencil',
242
+ command: () => this.openReportNameDialog()
243
+ },
244
+ // Separator
245
+ { separator: true },
246
+ // Export type menu items
247
+ ...this.exportTypes.map(type => ({
248
+ label: this.translate.instant(type.label),
249
+ icon: type.icon,
250
+ command: () => this.onExportTypeChange(type.value)
251
+ }))
252
+ ];
253
+ }
254
+ openReportNameDialog() {
255
+ const ref = this.dialogService.open(ReportNameDialogComponent, {
256
+ header: this.translate.instant('EDIT_REPORT_NAME'),
257
+ width: '500px',
258
+ data: {
259
+ fileName: this.fileName
260
+ }
261
+ });
262
+ ref.onClose.subscribe((result) => {
263
+ if (result) {
264
+ this.fileName = result;
265
+ }
266
+ });
142
267
  }
143
268
  getDefaultFilterFields() {
144
269
  return [
@@ -152,173 +277,44 @@ class GenericReportComponent {
152
277
  }
153
278
  ];
154
279
  }
155
- get selectedColumns() {
156
- return this.availableColumns.filter(col => col.selected);
157
- }
158
- onExportTypeChange(type) {
159
- this.exportType = type;
160
- // Auto-switch to preview tab for HTML/Print
161
- if ((type === ExportType.HTML || type === ExportType.PRINT) && this.selectedColumns.length > 0) {
162
- this.activeTabIndex = 1;
163
- }
164
- }
165
- onColumnToggle(column) {
166
- if (!column.selected) {
167
- column.customName = column.displayName;
168
- }
169
- // Refresh preview when columns change
170
- if (this.activeTabIndex === 1) {
171
- this.generatePreview();
172
- }
173
- }
174
- onCustomNameChange(column) {
175
- if (!column.customName?.trim()) {
176
- column.customName = column.displayName;
177
- }
178
- // Refresh preview when column names change
179
- if (this.activeTabIndex === 1) {
180
- this.generatePreview();
181
- }
182
- }
183
- selectAllColumns() {
184
- this.availableColumns.forEach(col => {
185
- col.selected = true;
186
- col.customName = col.displayName;
187
- });
188
- this.generatePreview();
189
- }
190
- deselectAllColumns() {
191
- this.availableColumns.forEach(col => {
192
- col.selected = false;
193
- });
194
- this.previewData = [];
195
- }
280
+ // Query and Preview Methods
196
281
  generatePreview() {
197
- if (this.selectedColumns.length === 0) {
198
- this.previewData = [];
199
- return;
200
- }
201
- // In a real application, you would call your API here
202
- // For demo purposes, we're using the sample data
203
- this.loadPreviewData();
204
- }
205
- getPreviewValue(row, columnKey) {
206
- // Handle nested properties with dot notation
207
- return columnKey.split('.').reduce((obj, key) => obj?.[key], row) || '-';
208
- }
209
- getExportButtonText() {
210
- switch (this.exportType) {
211
- case ExportType.PRINT:
212
- return this.translate.instant('PRINT');
213
- case ExportType.HTML:
214
- return this.translate.instant('GENERATE_REPORT');
215
- default:
216
- return this.translate.instant('EXPORT');
217
- }
282
+ this.onExport();
218
283
  }
219
284
  onSubmit() {
220
- // Use the new QueryBuilderService methods
221
285
  const odataParams = this.queryBuilder.buildODataFromQueryModel(this.model, this.filterFields);
222
- // this.odataSearch.emit(odataParams);
223
- // Generate query string using the unified method
224
286
  this.queryString = this.queryBuilder.toODataQueryString(odataParams);
225
- // console.log('OData Query String:', queryString);
226
- if (this.model?.pagination && this.model?.pagination?.top) {
227
- // this.paginationChange.emit({
228
- // top:this.model?.pagination?.top,
229
- // // skip:,
230
- // });
231
- }
232
- // this.search.emit(
233
- // {
234
- // filter:this.model['filter'],
235
- // query:this.queryString,
236
- // // sorting:,
237
- // // top:,
238
- // // skip:,
239
- // }
240
- // );
241
- this.loadPreviewData();
287
+ this.onExport();
242
288
  }
243
289
  onReset() {
244
- this.availableColumns.forEach(col => {
245
- col.selected = true;
246
- col.customName = col.displayName;
247
- });
248
- this.exportType = ExportType.EXCEL;
290
+ this.exportType = ExportType.PDF;
249
291
  this.fileName = `export_${new Date().toISOString().split('T')[0]}`;
250
292
  this.form.reset();
251
293
  this.model = {};
252
- this.activeTabIndex = 0;
253
294
  this.generatePreview();
254
295
  }
296
+ // Export Methods
255
297
  onExport() {
256
- if (this.selectedColumns.length === 0)
257
- return;
258
298
  this.loading = true;
259
- // Prepare columns mapping
260
- const columnsMapping = {};
261
- this.selectedColumns.forEach(col => {
262
- columnsMapping[col.key] = col.customName || col.displayName;
263
- });
264
- const exportConfig = {
265
- columns: columnsMapping,
299
+ const exportConfig = this.createExportConfig();
300
+ this.handleApiExport(exportConfig);
301
+ }
302
+ createExportConfig() {
303
+ return {
266
304
  filters: this.model,
267
305
  exportType: this.exportType,
268
306
  fileName: this.fileName
269
307
  };
270
- // Handle export based on type
271
- switch (this.exportType) {
272
- case ExportType.PRINT:
273
- this.printPreview();
274
- this.loading = false;
275
- break;
276
- case ExportType.HTML:
277
- this.generateHtmlReport(exportConfig);
278
- this.loading = false;
279
- break;
280
- default:
281
- this.handleApiExport(exportConfig);
282
- break;
283
- }
284
- }
285
- generateHtmlReport(config) {
286
- const htmlContent = this.previewContainer.nativeElement.innerHTML;
287
- const blob = new Blob([htmlContent], { type: 'text/html' });
288
- this.downloadFile(blob, `${config.fileName}.html`);
289
308
  }
290
309
  handleApiExport(config) {
291
310
  if (this.form.invalid)
292
311
  return;
293
- this.loading = true;
294
- const filters = {
295
- fileName: config.fileName || '',
296
- query: this.queryString,
297
- exportType: config.exportType,
298
- columns: JSON.stringify(config.columns),
299
- // ...this.currentFilters,
300
- // ...this.pageFilters,
301
- // ...(this.searchGlobal ? { filter: this.searchGlobal } : {}),
302
- };
303
- const params = Object.keys(filters).reduce((acc, key) => {
304
- acc[key] = filters[key];
305
- return acc;
306
- }, {});
307
- this.restService.request({
308
- method: 'GET',
309
- url: `/${this.apiName}/export`,
310
- // body: { ...config.filters, ...this.model },
311
- params: params
312
- }, { apiName: this.apiName })
312
+ const requestOptions = this.createRequestOptions(config);
313
+ this.restService.request(requestOptions, { apiName: this.apiName })
313
314
  .pipe(takeUntil(this.destroy$))
314
315
  .subscribe({
315
316
  next: (response) => {
316
- this.loading = false;
317
- if (config.exportType == ExportType.EXCEL) {
318
- const blob = this.base64ToBlob(response, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
319
- this.downloadFile(blob, `${config.fileName}.${this.getFileExtension(config.exportType)}`);
320
- }
321
- this.loading = false;
317
+ this.handleExportResponse(response, config);
322
318
  },
323
319
  error: (error) => {
324
320
  console.error('Export failed:', error);
@@ -326,45 +322,149 @@ class GenericReportComponent {
326
322
  }
327
323
  });
328
324
  }
329
- loadPreviewData() {
330
- if (this.form.invalid)
331
- return;
332
- this.loading = true;
325
+ createRequestOptions(config) {
326
+ // Default query
327
+ if (!this.queryString || this.queryString == '') {
328
+ // Use the new QueryBuilderService methods
329
+ const odataParams = this.queryBuilder.buildODataFromQueryModel(this.model, this.filterFields);
330
+ // this.odataSearch.emit(odataParams);
331
+ // Generate query string using the unified method
332
+ this.queryString = this.queryBuilder.toODataQueryString(odataParams);
333
+ // console.log('OData Query String:', queryString);
334
+ }
333
335
  const filters = {
336
+ fileName: config.fileName || '',
334
337
  query: this.queryString,
335
- exportType: ExportType.LIST,
336
- columns: JSON.stringify(this.getPreviewColumns()),
337
- // ...this.currentFilters,
338
- // ...this.pageFilters,
339
- // ...(this.searchGlobal ? { filter: this.searchGlobal } : {}),
338
+ exportType: config.exportType,
340
339
  };
341
340
  const params = Object.keys(filters).reduce((acc, key) => {
342
341
  acc[key] = filters[key];
343
342
  return acc;
344
343
  }, {});
345
- this.restService.request({
344
+ const requestOptions = {
346
345
  method: 'GET',
347
346
  url: `/${this.apiName}/export`,
348
347
  params: params
349
- }, { apiName: this.apiName })
350
- .pipe(takeUntil(this.destroy$))
351
- .subscribe({
352
- next: (response) => {
353
- this.previewData = response;
354
- this.loading = false;
355
- },
356
- error: (error) => {
357
- console.error('Preview load failed:', error);
358
- this.loading = false;
359
- }
360
- });
348
+ };
349
+ if (config.exportType === ExportType.HTML) {
350
+ requestOptions.responseType = 'text';
351
+ }
352
+ return requestOptions;
361
353
  }
362
- getPreviewColumns() {
363
- const columns = {};
364
- this.selectedColumns.forEach(col => {
365
- columns[col.key] = col.customName || col.displayName;
366
- });
367
- return columns;
354
+ // private handleExportResponse(response: any, config: any) {
355
+ // this.loading = false;
356
+ // if (config.exportType === ExportType.HTML) {
357
+ // this.showHtmlExport(response);
358
+ // } else {
359
+ // this.downloadBinaryExport(response, config);
360
+ // }
361
+ // }
362
+ handleExportResponse(response, config) {
363
+ this.loading = false;
364
+ if (config.exportType === ExportType.HTML) {
365
+ this.showHtmlExport(response);
366
+ // } else if (config.exportType === ExportType.PDF && this.isMobileDevice()) {
367
+ }
368
+ else if (config.exportType === ExportType.PDF) {
369
+ this.showPdfInMobile(response, config);
370
+ }
371
+ else {
372
+ this.downloadBinaryExport(response, config);
373
+ }
374
+ }
375
+ // Platform detection methods
376
+ isMobileDevice() {
377
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
378
+ }
379
+ isIOS() {
380
+ return /iPad|iPhone|iPod/.test(navigator.userAgent);
381
+ }
382
+ isAndroid() {
383
+ return /Android/.test(navigator.userAgent);
384
+ }
385
+ showPdfModal = false;
386
+ currentPdfUrl;
387
+ currentFileName = '';
388
+ showPdfInMobile(base64Data, config) {
389
+ try {
390
+ const blob = this.base64ToBlob(base64Data, this.getMimeType(ExportType.PDF));
391
+ this.currentPdfUrl = this.sanitizeUrl(URL.createObjectURL(blob));
392
+ this.currentFileName = `${config.fileName}.pdf`;
393
+ this.showPdfModal = true;
394
+ }
395
+ catch (error) {
396
+ var error = 'PDF is too large for mobile viewing. Please download instead.';
397
+ }
398
+ finally {
399
+ // this.loading = false;
400
+ }
401
+ }
402
+ sanitizeUrl(url) {
403
+ return this.sanitizer.bypassSecurityTrustResourceUrl(url);
404
+ }
405
+ // onPdfModalClose(): void {
406
+ // this.showPdfModal = false;
407
+ // if (this.currentPdfUrl) {
408
+ // URL.revokeObjectURL(this.currentPdfUrl);
409
+ // this.currentPdfUrl = '';
410
+ // }
411
+ // }
412
+ onPdfDownload() {
413
+ // Trigger download
414
+ // const blob = this.base64ToBlob(this.getCurrentBase64(), this.getMimeType(ExportType.PDF));
415
+ // this.downloadFile(blob, this.currentFileName);
416
+ }
417
+ downloadBinaryExport(response, config) {
418
+ const blob = this.base64ToBlob(response, this.getMimeType(config.exportType));
419
+ this.downloadFile(blob, `${config.fileName}.${this.getFileExtension(config.exportType)}`);
420
+ }
421
+ // HTML Export Methods
422
+ showHtmlExport(htmlContent) {
423
+ const cleanHtml = this.cleanHtmlContent(htmlContent);
424
+ this.htmlExportContent = cleanHtml;
425
+ this.showHtmlInIframe = true;
426
+ setTimeout(() => {
427
+ this.setIframeContent(cleanHtml);
428
+ }, 100);
429
+ }
430
+ setIframeContent(htmlContent) {
431
+ const iframe = document.getElementById('reportIframe');
432
+ if (!iframe) {
433
+ console.error('Iframe not found');
434
+ return;
435
+ }
436
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
437
+ if (iframeDoc) {
438
+ iframeDoc.open();
439
+ iframeDoc.write(htmlContent);
440
+ iframeDoc.close();
441
+ }
442
+ }
443
+ print() {
444
+ // const iframe = document.getElementById('reportIframe') as HTMLIFrameElement | null;
445
+ // if (!iframe || !iframe.contentWindow) return;
446
+ // const iframeWindow = iframe.contentWindow;
447
+ // iframeWindow.focus();
448
+ // iframeWindow.print();
449
+ document.getElementById('reportIframe').contentWindow.print();
450
+ }
451
+ // Utility Methods
452
+ getExportButtonText() {
453
+ switch (this.exportType) {
454
+ case ExportType.PRINT:
455
+ return this.translate.instant('PRINT');
456
+ case ExportType.HTML:
457
+ return this.translate.instant('EXPORT');
458
+ default:
459
+ return this.translate.instant('EXPORT');
460
+ }
461
+ }
462
+ onExportTypeChange(type) {
463
+ this.exportType = type;
464
+ }
465
+ getCurrentExportTypeIcon() {
466
+ const currentType = this.exportTypes.find(type => type.value === this.exportType);
467
+ return currentType?.icon || 'pi pi-download';
368
468
  }
369
469
  base64ToBlob(base64, mime) {
370
470
  const byteChars = atob(base64);
@@ -390,80 +490,48 @@ class GenericReportComponent {
390
490
  case ExportType.HTML:
391
491
  return 'html';
392
492
  default:
393
- return 'xlsx';
493
+ return 'pdf';
394
494
  }
395
495
  }
396
- printPreview() {
397
- const printContent = this.previewContainer.nativeElement.innerHTML;
398
- const originalContent = document.body.innerHTML;
399
- // Create print window
400
- const printWindow = window.open('', '_blank', 'width=800,height=600');
401
- if (printWindow) {
402
- printWindow.document.write(`
403
- <!DOCTYPE html>
404
- <html>
405
- <head>
406
- <title>${this.fileName}</title>
407
- <style>
408
- body { font-family: Arial, sans-serif; margin: 20px; }
409
- table { width: 100%; border-collapse: collapse; }
410
- th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
411
- th { background-color: #f5f5f5; font-weight: bold; }
412
- .header { text-align: center; margin-bottom: 20px; }
413
- .footer { text-align: center; margin-top: 20px; font-size: 12px; color: #666; }
414
- @media print {
415
- body { margin: 0; }
416
- .no-print { display: none; }
417
- }
418
- </style>
419
- </head>
420
- <body>
421
- ${printContent}
422
- <div class="footer no-print">
423
- <button onclick="window.print()" style="padding: 10px 20px; margin: 10px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;">
424
- Print Report
425
- </button>
426
- <button onclick="window.close()" style="padding: 10px 20px; margin: 10px; background: #6c757d; color: white; border: none; border-radius: 4px; cursor: pointer;">
427
- Close
428
- </button>
429
- </div>
430
- </body>
431
- </html>
432
- `);
433
- printWindow.document.close();
434
- // Auto-print when window loads
435
- printWindow.onload = () => {
436
- printWindow.focus();
437
- // Uncomment the line below for auto-print
438
- // printWindow.print();
439
- };
496
+ getMimeType(exportType) {
497
+ switch (exportType) {
498
+ case ExportType.EXCEL:
499
+ return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
500
+ case ExportType.PDF:
501
+ return 'application/pdf';
502
+ default:
503
+ return 'application/octet-stream';
440
504
  }
441
505
  }
442
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericReportComponent, deps: [{ token: i1.DynamicDialogRef }, { token: i1.DynamicDialogConfig }], target: i0.ɵɵFactoryTarget.Component });
443
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.8", type: GenericReportComponent, isStandalone: true, selector: "app-generic-report", inputs: { filterFields: "filterFields", enableQueryBuilder: "enableQueryBuilder", localExport: "localExport", queryString: "queryString", apiName: "apiName", model: "model" }, providers: [DialogService], viewQueries: [{ propertyName: "previewContainer", first: true, predicate: ["previewContainer"], descendants: true }], ngImport: i0, template: "\r\n <div class=\"flex flex-col h-full bg-white dark:bg-gray-900 rounded-lg max-w-6xl mx-auto\">\r\n <!-- Header -->\r\n <!-- <div class=\"flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700\">\r\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white\">\r\n {{ 'EXPORT_DATA' | translate }}\r\n </h2>\r\n <button\r\n (click)=\"ref?.close()\"\r\n class=\"p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\r\n aria-label=\"Close\">\r\n <i class=\"pi pi-times text-gray-500 dark:text-gray-400\"></i>\r\n </button>\r\n </div> -->\r\n\r\n <!-- Content -->\r\n <div class=\"flex-1 overflow-hidden\">\r\n <p-tabs [value]=\"activeTabIndex\">\r\n <!-- Configuration Tab -->\r\n <p-tablist>\r\n <p-tab value=\"0\">{{'CONFIGURATION' | translate}}</p-tab>\r\n <p-tab value=\"1\">{{'PREVIEW' | translate}}</p-tab>\r\n </p-tablist>\r\n <p-tabpanels>\r\n <p-tabpanel value=\"0\">\r\n <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-6 p-4 max-h-[60vh] overflow-y-auto\">\r\n <!-- Left Column - Export Settings -->\r\n <div class=\"space-y-6\">\r\n <!-- File Name -->\r\n <div class=\"space-y-2\">\r\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\r\n {{ 'FILE_NAME' | translate }}\r\n </label>\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"fileName\"\r\n class=\"w-full p-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\r\n [placeholder]=\"'ENTER_FILE_NAME' | translate\">\r\n </div>\r\n\r\n <!-- Export Type -->\r\n <div class=\"space-y-3\">\r\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\r\n {{ 'EXPORT_FORMAT' | translate }}\r\n </label>\r\n <div class=\"grid grid-cols-2 gap-3\">\r\n <div *ngFor=\"let type of exportTypes\" class=\"flex items-center\">\r\n <p-radioButton\r\n [name]=\"'exportType'\"\r\n [value]=\"type.value\"\r\n [(ngModel)]=\"exportType\"\r\n [inputId]=\"`${type.value}`\"\r\n (onClick)=\"onExportTypeChange(type.value)\"\r\n class=\"mr-3\">\r\n </p-radioButton>\r\n <label [for]=\"type.value\" class=\"flex items-center cursor-pointer text-sm text-gray-700 dark:text-gray-300\">\r\n <i [class]=\"type.icon + ' mr-2'\"></i>\r\n {{ type.label | translate }}\r\n </label>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Filters Section -->\r\n <div class=\"space-y-3\">\r\n <div class=\"flex items-center justify-between\">\r\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\r\n {{ 'SEARCH_FILTERS' | translate }}\r\n </label>\r\n <button\r\n type=\"button\"\r\n (click)=\"showFilters = !showFilters\"\r\n class=\"text-sm text-blue-600 dark:text-blue-400 hover:underline\">\r\n {{ showFilters ? ('HIDE_FILTERS' | translate) : ('SHOW_FILTERS' | translate) }}\r\n </button>\r\n </div>\r\n\r\n <!-- <div *ngIf=\"showFilters\" class=\"space-y-4 p-4 border border-gray-200 dark:border-gray-700 rounded-lg bg-gray-50 dark:bg-gray-800\">\r\n <formly-form\r\n [form]=\"form\"\r\n [fields]=\"fields_\"\r\n [model]=\"model\">\r\n </formly-form>\r\n </div> -->\r\n </div>\r\n </div>\r\n\r\n <!-- Right Column - Column Selection -->\r\n <div class=\"space-y-4\">\r\n <div class=\"flex items-center justify-between\">\r\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\r\n {{ 'SELECT_COLUMNS' | translate }}\r\n </label>\r\n <div class=\"flex gap-2\">\r\n <button\r\n type=\"button\"\r\n (click)=\"selectAllColumns()\"\r\n class=\"text-xs text-blue-600 dark:text-blue-400 hover:underline\">\r\n {{ 'SELECT_ALL' | translate }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n (click)=\"deselectAllColumns()\"\r\n class=\"text-xs text-blue-600 dark:text-blue-400 hover:underline\">\r\n {{ 'DESELECT_ALL' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div class=\"max-h-80 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-lg\">\r\n <div *ngFor=\"let column of availableColumns\" class=\"flex items-center p-3 border-b border-gray-100 dark:border-gray-600 last:border-b-0 hover:bg-gray-50 dark:hover:bg-gray-700\">\r\n <p-checkbox\r\n [binary]=\"true\"\r\n [(ngModel)]=\"column.selected\"\r\n [inputId]=\"column.key\"\r\n (onChange)=\"onColumnToggle(column)\"\r\n class=\"mr-3\">\r\n </p-checkbox>\r\n <label [for]=\"column.key\" class=\"flex-1 cursor-pointer text-sm text-gray-700 dark:text-gray-300\">\r\n {{ column.displayName }}\r\n </label>\r\n <div *ngIf=\"column.selected\" class=\"flex items-center gap-2\">\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"column.customName\"\r\n (blur)=\"onCustomNameChange(column)\"\r\n [placeholder]=\"column.displayName\"\r\n class=\"text-xs p-1 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-white w-32\"\r\n [title]=\"'CUSTOM_COLUMN_NAME' | translate\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Selected Columns Summary -->\r\n <div *ngIf=\"selectedColumns.length > 0\" class=\"p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg\">\r\n <p class=\"text-sm text-blue-700 dark:text-blue-300\">\r\n {{ 'SELECTED_COLUMNS_COUNT' | translate: { count: selectedColumns.length } }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </p-tabpanel>\r\n\r\n <!-- Preview Tab -->\r\n <!-- [disabled]=\"selectedColumns.length === 0\" -->\r\n <p-tabpanel value=\"1\" >\r\n <div class=\"p-4 max-h-[60vh] overflow-y-auto\">\r\n <!-- Preview Actions -->\r\n <div class=\"flex justify-between items-center mb-4\">\r\n <h3 class=\"text-lg font-semibold text-gray-900 dark:text-white\">\r\n {{ 'REPORT_PREVIEW' | translate }}\r\n </h3>\r\n <div class=\"flex gap-2\">\r\n <button\r\n *ngIf=\"exportType === 1 || exportType === 4\"\r\n (click)=\"printPreview()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\">\r\n <i class=\"pi pi-print mr-2\"></i>\r\n {{ 'PRINT' | translate }}\r\n </button>\r\n <button\r\n (click)=\"generatePreview()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors\">\r\n <i class=\"pi pi-refresh mr-2\"></i>\r\n {{ 'REFRESH_PREVIEW' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Preview Content -->\r\n <div #previewContainer class=\"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-6\">\r\n <!-- Report Header -->\r\n <div class=\"text-center mb-6 border-b border-gray-200 dark:border-gray-700 pb-4\">\r\n <h1 class=\"text-2xl font-bold text-gray-900 dark:text-white\">{{ fileName }}</h1>\r\n <p class=\"text-gray-600 dark:text-gray-400 mt-2\">\r\n {{ 'GENERATED_ON' | translate }}: {{ currentDate | date:'medium' }}\r\n </p>\r\n </div>\r\n\r\n <!-- Report Table -->\r\n <div class=\"overflow-x-auto\">\r\n <table class=\"min-w-full divide-y divide-gray-200 dark:divide-gray-700\">\r\n <thead class=\"bg-gray-50 dark:bg-gray-700\">\r\n <tr>\r\n <th\r\n *ngFor=\"let column of selectedColumns\"\r\n class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-600\">\r\n {{ column.customName || column.displayName }}\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody class=\"bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700\">\r\n <tr *ngFor=\"let row of previewData; let i = index\"\r\n [class.bg-gray-50]=\"i % 2 === 0\"\r\n [class.dark:bg-gray-900]=\"i % 2 === 0\">\r\n <td\r\n *ngFor=\"let column of selectedColumns\"\r\n class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white border border-gray-200 dark:border-gray-600\">\r\n {{ getPreviewValue(row, column.key) }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n <!-- Empty State -->\r\n <div *ngIf=\"previewData.length === 0\" class=\"text-center py-8\">\r\n <i class=\"pi pi-inbox text-4xl text-gray-400 dark:text-gray-500 mb-4\"></i>\r\n <p class=\"text-gray-500 dark:text-gray-400\">{{ 'NO_PREVIEW_DATA' | translate }}</p>\r\n <button\r\n (click)=\"generatePreview()\"\r\n class=\"mt-4 px-4 py-2 text-sm font-medium text-blue-600 dark:text-blue-400 hover:underline\">\r\n {{ 'GENERATE_PREVIEW' | translate }}\r\n </button>\r\n </div>\r\n\r\n <!-- Report Footer -->\r\n <div class=\"text-center mt-6 border-t border-gray-200 dark:border-gray-700 pt-4\">\r\n <p class=\"text-sm text-gray-500 dark:text-gray-400\">\r\n {{ 'PAGE' | translate }} 1 of 1\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Preview Info -->\r\n <div class=\"mt-4 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg\">\r\n <p class=\"text-sm text-green-700 dark:text-green-300\">\r\n <i class=\"pi pi-info-circle mr-2\"></i>\r\n {{ 'PREVIEW_INFO' | translate }}\r\n </p>\r\n </div>\r\n </div>\r\n </p-tabpanel>\r\n\r\n </p-tabpanels>\r\n </p-tabs>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"border-t border-gray-200 dark:border-gray-700 px-6 py-4 bg-gray-50 dark:bg-gray-800\">\r\n <div class=\"flex justify-between items-center\">\r\n <button\r\n type=\"button\"\r\n (click)=\"onReset()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors\"\r\n [disabled]=\"loading\">\r\n <i class=\"pi pi-refresh ml-2\"></i>\r\n {{ 'RESET' | translate }}\r\n </button>\r\n\r\n <div class=\"flex gap-2\">\r\n <button\r\n type=\"button\"\r\n (click)=\"ref?.close()\"\r\n class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"\r\n [disabled]=\"loading\">\r\n {{ 'CANCEL' | translate }}\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n (click)=\"onExport()\"\r\n [disabled]=\"loading || selectedColumns.length === 0\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors\">\r\n <i class=\"pi pi-download ml-2\" [class.animate-spin]=\"loading\"></i>\r\n {{ getExportButtonText() }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n @if(showFilters){\r\n<p-drawer\r\n [(visible)]=\"showFilters\"\r\n position=\"right\"\r\n [styleClass]=\"'!w-full md:!w-80 lg:!w-[40rem] !h-full' \"\r\n [modal]=\"true\"\r\n [dismissible]=\"true\"\r\n styleClass=\"p-4 w-full max-w-md max-h-[90vh] flex flex-col\"\r\n>\r\n <!-- Entire form wrapper -->\r\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit(); showFilters=false\" class=\"flex flex-col flex-1\">\r\n <!-- Scrollable Form -->\r\n <div class=\"flex-1 overflow-auto\">\r\n <formly-form\r\n [form]=\"form\"\r\n [fields]=\"fields_\"\r\n [model]=\"model\"\r\n [options]=\"options\"\r\n >\r\n </formly-form>\r\n </div>\r\n\r\n </form>\r\n <!-- Action Buttons -->\r\n <ng-template pTemplate=\"footer\">\r\n <div class=\"flex justify-end mt-2 space-x-2 flex-none\">\r\n <button\r\n type=\"submit\"\r\n pButton size=\"small\"\r\n (click)=\" onSubmit(); showFilters=false\"\r\n label=\"{{ 'SEARCH' | translate }}\">\r\n </button>\r\n <button\r\n type=\"button\"\r\n pButton\r\n size=\"small\"\r\n class=\"p-button-text\"\r\n (click)=\"onReset(); showFilters=false\"\r\n >\r\n {{ 'CLEAR' | translate }}\r\n </button>\r\n </div>\r\n </ng-template>\r\n</p-drawer>\r\n\r\n }\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i4.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "directive", type: i5.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i6.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "styleClass", "autofocus", "binary", "variant", "size"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i7.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "ngmodule", type: DividerModule }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i8.Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: i8.TabPanels, selector: "p-tabpanels" }, { kind: "component", type: i8.TabPanel, selector: "p-tabpanel", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: i8.TabList, selector: "p-tablist" }, { kind: "component", type: i8.Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "component", type: i9.LegacyFormlyForm, selector: "formly-form" }, { kind: "ngmodule", type: DynamicDialogModule }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i10.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: "pipe", type: i2.DatePipe, name: "date" }, { kind: "pipe", type: i11.TranslatePipe, name: "translate" }] });
506
+ cleanHtmlContent(html) {
507
+ return html
508
+ .replace(/\\r\\n/g, '\n')
509
+ .replace(/\\"/g, '"')
510
+ .replace(/\\t/g, ' ')
511
+ .trim();
512
+ }
513
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericReportComponent, deps: [{ token: i1$1.DynamicDialogRef }, { token: i1$1.DynamicDialogConfig }], target: i0.ɵɵFactoryTarget.Component });
514
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.8", type: GenericReportComponent, isStandalone: true, selector: "app-generic-report", inputs: { filterFields: "filterFields", enableQueryBuilder: "enableQueryBuilder", localExport: "localExport", queryString: "queryString", apiName: "apiName", model: "model" }, providers: [DialogService, RestService], viewQueries: [{ propertyName: "previewContainer", first: true, predicate: ["previewContainer"], descendants: true }], ngImport: i0, template: "<!-- class=\"flex flex-col h-full bg-white dark:bg-gray-900 rounded-lg max-w-6xl mx-auto\" -->\r\n<div >\r\n <!-- Content -->\r\n <div class=\"flex-1 overflow-hidden p-2\">\r\n\r\n <!-- Preview Actions -->\r\n <div class=\"flex justify-between items-center mb-6 p-2 bg-white rounded-lg\">\r\n <h3 class=\"text-lg font-semibold text-gray-900 dark:text-white\">\r\n {{ 'REPORT' | translate }}\r\n </h3>\r\n <div class=\"flex gap-2\">\r\n <!-- Filters Button -->\r\n <button\r\n type=\"button\"\r\n (click)=\"showFilters = !showFilters\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"\r\n [pTooltip]=\"'SEARCH_FILTERS' | translate\">\r\n <i class=\"pi pi-filter\"></i>\r\n <span *ngIf=\"filterCount > 0\" class=\"ml-2 bg-blue-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center\">\r\n {{ filterCount }}\r\n </span>\r\n </button>\r\n\r\n <!-- Refresh Preview -->\r\n <button\r\n (click)=\"generatePreview()\"\r\n [disabled]=\"loading\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors\"\r\n [pTooltip]=\"'REFRESH' | translate\">\r\n <i class=\"pi pi-refresh mr-2 ml-2\" [class.animate-spin]=\"loading\"></i>\r\n {{ 'REFRESH' | translate }}\r\n </button>\r\n\r\n <!-- Print Button -->\r\n <button\r\n *ngIf=\"exportType === 4 || exportType === 1 || exportType === 2\"\r\n (click)=\"print()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"\r\n [pTooltip]=\"'PRINT' | translate\">\r\n <i class=\"pi pi-print mr-2 ml-2\"></i>\r\n {{ 'PRINT' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Preview Content -->\r\n <div #previewContainer class=\"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg\">\r\n <!-- HTML Export Preview (Iframe) -->\r\n <div *ngIf=\"showHtmlInIframe && htmlExportContent || showPdfModal\" class=\"html-export-preview\">\r\n <iframe\r\n id=\"reportIframe\"\r\n class=\"w-full border border-gray-300 dark:border-gray-600 rounded-b-lg\"\r\n [style.height]=\"'600px'\"\r\n title=\"HTML Export Preview\"\r\n [src]=\"currentPdfUrl\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n frameborder=\"0\"\r\n allowfullscreen\r\n\r\n >\r\n <!-- allowfullscreen -->\r\n </iframe>\r\n </div>\r\n <!-- <app-pdf-modal\r\n *ngIf=\"showPdfModal\"\r\n [pdfUrl]=\"currentPdfUrl\"\r\n [fileName]=\"currentFileName\"\r\n (closed)=\"onPdfModalClose()\"\r\n (downloadRequested)=\"onPdfDownload()\">\r\n</app-pdf-modal> -->\r\n\r\n <!-- Regular Preview Table -->\r\n <!-- <div *ngIf=\"!showHtmlInIframe\" class=\"p-4\">\r\n <p-table\r\n [value]=\"previewData\"\r\n [columns]=\"previewColumns\"\r\n [paginator]=\"true\"\r\n [rows]=\"10\"\r\n [showCurrentPageReport]=\"true\"\r\n currentPageReportTemplate=\"Showing {first} to {last} of {totalRecords} entries\"\r\n styleClass=\"p-datatable-sm\">\r\n <ng-template pTemplate=\"header\">\r\n <tr>\r\n <th *ngFor=\"let col of previewColumns\" class=\"text-left\">\r\n {{ col.header }}\r\n </th>\r\n </tr>\r\n </ng-template>\r\n <ng-template pTemplate=\"body\" let-rowData>\r\n <tr>\r\n <td *ngFor=\"let col of previewColumns\" class=\"text-left\">\r\n {{ rowData[col.field] }}\r\n </td>\r\n </tr>\r\n </ng-template>\r\n <ng-template pTemplate=\"emptymessage\">\r\n <tr>\r\n <td [attr.colspan]=\"previewColumns.length\" class=\"text-center py-4\">\r\n <div class=\"text-center py-8\">\r\n <i class=\"pi pi-inbox text-4xl text-gray-400 dark:text-gray-500 mb-4\"></i>\r\n <p class=\"text-gray-500 dark:text-gray-400\">{{ 'NO_PREVIEW_DATA' | translate }}</p>\r\n </div>\r\n </td>\r\n </tr>\r\n </ng-template>\r\n </p-table>\r\n </div> -->\r\n </div>\r\n\r\n <!-- <div class=\"mt-4 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg\">\r\n <p class=\"text-sm text-green-700 dark:text-green-300\">\r\n <i class=\"pi pi-info-circle mr-2\"></i>\r\n {{ 'PREVIEW_INFO' | translate }}\r\n </p>\r\n </div> -->\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"border-t border-gray-200 dark:border-gray-700 px-2 py-2 bg-gray-50 dark:bg-gray-800\">\r\n <div class=\"flex justify-between items-center\">\r\n <button\r\n type=\"button\"\r\n (click)=\"onReset()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors\"\r\n [disabled]=\"loading\">\r\n <i class=\"pi pi-refresh mr-2\"></i>\r\n {{ 'RESET' | translate }}\r\n </button>\r\n\r\n <div class=\"flex gap-2\">\r\n <button\r\n type=\"button\"\r\n (click)=\"ref?.close()\"\r\n class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"\r\n [disabled]=\"loading\">\r\n {{ 'CANCEL' | translate }}\r\n </button>\r\n\r\n <!-- SplitButton for Export -->\r\n <!-- || previewData.length === 0 -->\r\n <p-splitButton\r\n [label]=\"getExportButtonText()\"\r\n [icon]=\"getCurrentExportTypeIcon()\"\r\n [model]=\"exportMenuItems\"\r\n (onClick)=\"onExport()\"\r\n [disabled]=\"loading\"\r\n styleClass=\"p-button-primary\">\r\n </p-splitButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Filters Drawer -->\r\n @if(showFilters){\r\n <p-drawer\r\n [(visible)]=\"showFilters\"\r\n position=\"right\"\r\n [styleClass]=\"'!w-full md:!w-80 lg:!w-[40rem] !h-full' \"\r\n [modal]=\"true\"\r\n [dismissible]=\"true\"\r\n styleClass=\"p-4 w-full max-w-md max-h-[90vh] flex flex-col\">\r\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit(); showFilters=false\" class=\"flex flex-col flex-1\">\r\n <div class=\"flex-1 overflow-auto\">\r\n <formly-form\r\n [form]=\"form\"\r\n [fields]=\"fields_\"\r\n [model]=\"model\"\r\n [options]=\"options\">\r\n </formly-form>\r\n </div>\r\n </form>\r\n <ng-template pTemplate=\"footer\">\r\n <div class=\"flex justify-end mt-2 space-x-2 flex-none\">\r\n <button\r\n type=\"submit\"\r\n pButton size=\"small\"\r\n (click)=\" onSubmit(); showFilters=false\"\r\n label=\"{{ 'SEARCH' | translate }}\">\r\n </button>\r\n <button\r\n type=\"button\"\r\n pButton\r\n size=\"small\"\r\n class=\"p-button-text\"\r\n (click)=\"onReset(); showFilters=false\">\r\n {{ 'CLEAR' | translate }}\r\n </button>\r\n </div>\r\n </ng-template>\r\n </p-drawer>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "directive", type: i5.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "ngmodule", type: CheckboxModule }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i6.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: "component", type: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "ngmodule", type: TableModule }, { kind: "ngmodule", type: SplitButtonModule }, { kind: "component", type: i7.SplitButton, selector: "p-splitbutton, p-splitButton, p-split-button", inputs: ["model", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "icon", "iconPos", "label", "tooltip", "tooltipOptions", "styleClass", "menuStyle", "menuStyleClass", "dropdownIcon", "appendTo", "dir", "expandAriaLabel", "showTransitionOptions", "hideTransitionOptions", "buttonProps", "menuButtonProps", "autofocus", "disabled", "tabindex", "menuButtonDisabled", "buttonDisabled"], outputs: ["onClick", "onMenuHide", "onMenuShow", "onDropdownClick"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i8.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }] });
444
515
  }
445
516
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericReportComponent, decorators: [{
446
517
  type: Component,
447
- args: [{ selector: 'app-generic-report', providers: [DialogService], imports: [
518
+ args: [{ selector: 'app-generic-report', imports: [
448
519
  CommonModule,
449
520
  ReactiveFormsModule,
450
521
  FormsModule,
451
522
  TranslateModule,
452
523
  ButtonModule,
453
524
  DialogModule,
454
- SelectModule,
455
- MultiSelectModule,
456
525
  InputTextModule,
457
526
  RadioButtonModule,
458
527
  CheckboxModule,
459
- CardModule,
460
- DividerModule,
461
- TabsModule,
462
- FormlyModule,
463
- DynamicDialogModule,
464
- DrawerModule
465
- ], template: "\r\n <div class=\"flex flex-col h-full bg-white dark:bg-gray-900 rounded-lg max-w-6xl mx-auto\">\r\n <!-- Header -->\r\n <!-- <div class=\"flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700\">\r\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white\">\r\n {{ 'EXPORT_DATA' | translate }}\r\n </h2>\r\n <button\r\n (click)=\"ref?.close()\"\r\n class=\"p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\r\n aria-label=\"Close\">\r\n <i class=\"pi pi-times text-gray-500 dark:text-gray-400\"></i>\r\n </button>\r\n </div> -->\r\n\r\n <!-- Content -->\r\n <div class=\"flex-1 overflow-hidden\">\r\n <p-tabs [value]=\"activeTabIndex\">\r\n <!-- Configuration Tab -->\r\n <p-tablist>\r\n <p-tab value=\"0\">{{'CONFIGURATION' | translate}}</p-tab>\r\n <p-tab value=\"1\">{{'PREVIEW' | translate}}</p-tab>\r\n </p-tablist>\r\n <p-tabpanels>\r\n <p-tabpanel value=\"0\">\r\n <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-6 p-4 max-h-[60vh] overflow-y-auto\">\r\n <!-- Left Column - Export Settings -->\r\n <div class=\"space-y-6\">\r\n <!-- File Name -->\r\n <div class=\"space-y-2\">\r\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\r\n {{ 'FILE_NAME' | translate }}\r\n </label>\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"fileName\"\r\n class=\"w-full p-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\r\n [placeholder]=\"'ENTER_FILE_NAME' | translate\">\r\n </div>\r\n\r\n <!-- Export Type -->\r\n <div class=\"space-y-3\">\r\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\r\n {{ 'EXPORT_FORMAT' | translate }}\r\n </label>\r\n <div class=\"grid grid-cols-2 gap-3\">\r\n <div *ngFor=\"let type of exportTypes\" class=\"flex items-center\">\r\n <p-radioButton\r\n [name]=\"'exportType'\"\r\n [value]=\"type.value\"\r\n [(ngModel)]=\"exportType\"\r\n [inputId]=\"`${type.value}`\"\r\n (onClick)=\"onExportTypeChange(type.value)\"\r\n class=\"mr-3\">\r\n </p-radioButton>\r\n <label [for]=\"type.value\" class=\"flex items-center cursor-pointer text-sm text-gray-700 dark:text-gray-300\">\r\n <i [class]=\"type.icon + ' mr-2'\"></i>\r\n {{ type.label | translate }}\r\n </label>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Filters Section -->\r\n <div class=\"space-y-3\">\r\n <div class=\"flex items-center justify-between\">\r\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\r\n {{ 'SEARCH_FILTERS' | translate }}\r\n </label>\r\n <button\r\n type=\"button\"\r\n (click)=\"showFilters = !showFilters\"\r\n class=\"text-sm text-blue-600 dark:text-blue-400 hover:underline\">\r\n {{ showFilters ? ('HIDE_FILTERS' | translate) : ('SHOW_FILTERS' | translate) }}\r\n </button>\r\n </div>\r\n\r\n <!-- <div *ngIf=\"showFilters\" class=\"space-y-4 p-4 border border-gray-200 dark:border-gray-700 rounded-lg bg-gray-50 dark:bg-gray-800\">\r\n <formly-form\r\n [form]=\"form\"\r\n [fields]=\"fields_\"\r\n [model]=\"model\">\r\n </formly-form>\r\n </div> -->\r\n </div>\r\n </div>\r\n\r\n <!-- Right Column - Column Selection -->\r\n <div class=\"space-y-4\">\r\n <div class=\"flex items-center justify-between\">\r\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\r\n {{ 'SELECT_COLUMNS' | translate }}\r\n </label>\r\n <div class=\"flex gap-2\">\r\n <button\r\n type=\"button\"\r\n (click)=\"selectAllColumns()\"\r\n class=\"text-xs text-blue-600 dark:text-blue-400 hover:underline\">\r\n {{ 'SELECT_ALL' | translate }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n (click)=\"deselectAllColumns()\"\r\n class=\"text-xs text-blue-600 dark:text-blue-400 hover:underline\">\r\n {{ 'DESELECT_ALL' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div class=\"max-h-80 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-lg\">\r\n <div *ngFor=\"let column of availableColumns\" class=\"flex items-center p-3 border-b border-gray-100 dark:border-gray-600 last:border-b-0 hover:bg-gray-50 dark:hover:bg-gray-700\">\r\n <p-checkbox\r\n [binary]=\"true\"\r\n [(ngModel)]=\"column.selected\"\r\n [inputId]=\"column.key\"\r\n (onChange)=\"onColumnToggle(column)\"\r\n class=\"mr-3\">\r\n </p-checkbox>\r\n <label [for]=\"column.key\" class=\"flex-1 cursor-pointer text-sm text-gray-700 dark:text-gray-300\">\r\n {{ column.displayName }}\r\n </label>\r\n <div *ngIf=\"column.selected\" class=\"flex items-center gap-2\">\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"column.customName\"\r\n (blur)=\"onCustomNameChange(column)\"\r\n [placeholder]=\"column.displayName\"\r\n class=\"text-xs p-1 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-white w-32\"\r\n [title]=\"'CUSTOM_COLUMN_NAME' | translate\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Selected Columns Summary -->\r\n <div *ngIf=\"selectedColumns.length > 0\" class=\"p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg\">\r\n <p class=\"text-sm text-blue-700 dark:text-blue-300\">\r\n {{ 'SELECTED_COLUMNS_COUNT' | translate: { count: selectedColumns.length } }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </p-tabpanel>\r\n\r\n <!-- Preview Tab -->\r\n <!-- [disabled]=\"selectedColumns.length === 0\" -->\r\n <p-tabpanel value=\"1\" >\r\n <div class=\"p-4 max-h-[60vh] overflow-y-auto\">\r\n <!-- Preview Actions -->\r\n <div class=\"flex justify-between items-center mb-4\">\r\n <h3 class=\"text-lg font-semibold text-gray-900 dark:text-white\">\r\n {{ 'REPORT_PREVIEW' | translate }}\r\n </h3>\r\n <div class=\"flex gap-2\">\r\n <button\r\n *ngIf=\"exportType === 1 || exportType === 4\"\r\n (click)=\"printPreview()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\">\r\n <i class=\"pi pi-print mr-2\"></i>\r\n {{ 'PRINT' | translate }}\r\n </button>\r\n <button\r\n (click)=\"generatePreview()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors\">\r\n <i class=\"pi pi-refresh mr-2\"></i>\r\n {{ 'REFRESH_PREVIEW' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Preview Content -->\r\n <div #previewContainer class=\"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-6\">\r\n <!-- Report Header -->\r\n <div class=\"text-center mb-6 border-b border-gray-200 dark:border-gray-700 pb-4\">\r\n <h1 class=\"text-2xl font-bold text-gray-900 dark:text-white\">{{ fileName }}</h1>\r\n <p class=\"text-gray-600 dark:text-gray-400 mt-2\">\r\n {{ 'GENERATED_ON' | translate }}: {{ currentDate | date:'medium' }}\r\n </p>\r\n </div>\r\n\r\n <!-- Report Table -->\r\n <div class=\"overflow-x-auto\">\r\n <table class=\"min-w-full divide-y divide-gray-200 dark:divide-gray-700\">\r\n <thead class=\"bg-gray-50 dark:bg-gray-700\">\r\n <tr>\r\n <th\r\n *ngFor=\"let column of selectedColumns\"\r\n class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-600\">\r\n {{ column.customName || column.displayName }}\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody class=\"bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700\">\r\n <tr *ngFor=\"let row of previewData; let i = index\"\r\n [class.bg-gray-50]=\"i % 2 === 0\"\r\n [class.dark:bg-gray-900]=\"i % 2 === 0\">\r\n <td\r\n *ngFor=\"let column of selectedColumns\"\r\n class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white border border-gray-200 dark:border-gray-600\">\r\n {{ getPreviewValue(row, column.key) }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n <!-- Empty State -->\r\n <div *ngIf=\"previewData.length === 0\" class=\"text-center py-8\">\r\n <i class=\"pi pi-inbox text-4xl text-gray-400 dark:text-gray-500 mb-4\"></i>\r\n <p class=\"text-gray-500 dark:text-gray-400\">{{ 'NO_PREVIEW_DATA' | translate }}</p>\r\n <button\r\n (click)=\"generatePreview()\"\r\n class=\"mt-4 px-4 py-2 text-sm font-medium text-blue-600 dark:text-blue-400 hover:underline\">\r\n {{ 'GENERATE_PREVIEW' | translate }}\r\n </button>\r\n </div>\r\n\r\n <!-- Report Footer -->\r\n <div class=\"text-center mt-6 border-t border-gray-200 dark:border-gray-700 pt-4\">\r\n <p class=\"text-sm text-gray-500 dark:text-gray-400\">\r\n {{ 'PAGE' | translate }} 1 of 1\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Preview Info -->\r\n <div class=\"mt-4 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg\">\r\n <p class=\"text-sm text-green-700 dark:text-green-300\">\r\n <i class=\"pi pi-info-circle mr-2\"></i>\r\n {{ 'PREVIEW_INFO' | translate }}\r\n </p>\r\n </div>\r\n </div>\r\n </p-tabpanel>\r\n\r\n </p-tabpanels>\r\n </p-tabs>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"border-t border-gray-200 dark:border-gray-700 px-6 py-4 bg-gray-50 dark:bg-gray-800\">\r\n <div class=\"flex justify-between items-center\">\r\n <button\r\n type=\"button\"\r\n (click)=\"onReset()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors\"\r\n [disabled]=\"loading\">\r\n <i class=\"pi pi-refresh ml-2\"></i>\r\n {{ 'RESET' | translate }}\r\n </button>\r\n\r\n <div class=\"flex gap-2\">\r\n <button\r\n type=\"button\"\r\n (click)=\"ref?.close()\"\r\n class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"\r\n [disabled]=\"loading\">\r\n {{ 'CANCEL' | translate }}\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n (click)=\"onExport()\"\r\n [disabled]=\"loading || selectedColumns.length === 0\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors\">\r\n <i class=\"pi pi-download ml-2\" [class.animate-spin]=\"loading\"></i>\r\n {{ getExportButtonText() }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n @if(showFilters){\r\n<p-drawer\r\n [(visible)]=\"showFilters\"\r\n position=\"right\"\r\n [styleClass]=\"'!w-full md:!w-80 lg:!w-[40rem] !h-full' \"\r\n [modal]=\"true\"\r\n [dismissible]=\"true\"\r\n styleClass=\"p-4 w-full max-w-md max-h-[90vh] flex flex-col\"\r\n>\r\n <!-- Entire form wrapper -->\r\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit(); showFilters=false\" class=\"flex flex-col flex-1\">\r\n <!-- Scrollable Form -->\r\n <div class=\"flex-1 overflow-auto\">\r\n <formly-form\r\n [form]=\"form\"\r\n [fields]=\"fields_\"\r\n [model]=\"model\"\r\n [options]=\"options\"\r\n >\r\n </formly-form>\r\n </div>\r\n\r\n </form>\r\n <!-- Action Buttons -->\r\n <ng-template pTemplate=\"footer\">\r\n <div class=\"flex justify-end mt-2 space-x-2 flex-none\">\r\n <button\r\n type=\"submit\"\r\n pButton size=\"small\"\r\n (click)=\" onSubmit(); showFilters=false\"\r\n label=\"{{ 'SEARCH' | translate }}\">\r\n </button>\r\n <button\r\n type=\"button\"\r\n pButton\r\n size=\"small\"\r\n class=\"p-button-text\"\r\n (click)=\"onReset(); showFilters=false\"\r\n >\r\n {{ 'CLEAR' | translate }}\r\n </button>\r\n </div>\r\n </ng-template>\r\n</p-drawer>\r\n\r\n }\r\n" }]
466
- }], ctorParameters: () => [{ type: i1.DynamicDialogRef }, { type: i1.DynamicDialogConfig }], propDecorators: { previewContainer: [{
528
+ DrawerModule,
529
+ FormlyForm,
530
+ TableModule,
531
+ SplitButtonModule,
532
+ TooltipModule,
533
+ ], providers: [DialogService, RestService], template: "<!-- class=\"flex flex-col h-full bg-white dark:bg-gray-900 rounded-lg max-w-6xl mx-auto\" -->\r\n<div >\r\n <!-- Content -->\r\n <div class=\"flex-1 overflow-hidden p-2\">\r\n\r\n <!-- Preview Actions -->\r\n <div class=\"flex justify-between items-center mb-6 p-2 bg-white rounded-lg\">\r\n <h3 class=\"text-lg font-semibold text-gray-900 dark:text-white\">\r\n {{ 'REPORT' | translate }}\r\n </h3>\r\n <div class=\"flex gap-2\">\r\n <!-- Filters Button -->\r\n <button\r\n type=\"button\"\r\n (click)=\"showFilters = !showFilters\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"\r\n [pTooltip]=\"'SEARCH_FILTERS' | translate\">\r\n <i class=\"pi pi-filter\"></i>\r\n <span *ngIf=\"filterCount > 0\" class=\"ml-2 bg-blue-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center\">\r\n {{ filterCount }}\r\n </span>\r\n </button>\r\n\r\n <!-- Refresh Preview -->\r\n <button\r\n (click)=\"generatePreview()\"\r\n [disabled]=\"loading\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors\"\r\n [pTooltip]=\"'REFRESH' | translate\">\r\n <i class=\"pi pi-refresh mr-2 ml-2\" [class.animate-spin]=\"loading\"></i>\r\n {{ 'REFRESH' | translate }}\r\n </button>\r\n\r\n <!-- Print Button -->\r\n <button\r\n *ngIf=\"exportType === 4 || exportType === 1 || exportType === 2\"\r\n (click)=\"print()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"\r\n [pTooltip]=\"'PRINT' | translate\">\r\n <i class=\"pi pi-print mr-2 ml-2\"></i>\r\n {{ 'PRINT' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Preview Content -->\r\n <div #previewContainer class=\"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg\">\r\n <!-- HTML Export Preview (Iframe) -->\r\n <div *ngIf=\"showHtmlInIframe && htmlExportContent || showPdfModal\" class=\"html-export-preview\">\r\n <iframe\r\n id=\"reportIframe\"\r\n class=\"w-full border border-gray-300 dark:border-gray-600 rounded-b-lg\"\r\n [style.height]=\"'600px'\"\r\n title=\"HTML Export Preview\"\r\n [src]=\"currentPdfUrl\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n frameborder=\"0\"\r\n allowfullscreen\r\n\r\n >\r\n <!-- allowfullscreen -->\r\n </iframe>\r\n </div>\r\n <!-- <app-pdf-modal\r\n *ngIf=\"showPdfModal\"\r\n [pdfUrl]=\"currentPdfUrl\"\r\n [fileName]=\"currentFileName\"\r\n (closed)=\"onPdfModalClose()\"\r\n (downloadRequested)=\"onPdfDownload()\">\r\n</app-pdf-modal> -->\r\n\r\n <!-- Regular Preview Table -->\r\n <!-- <div *ngIf=\"!showHtmlInIframe\" class=\"p-4\">\r\n <p-table\r\n [value]=\"previewData\"\r\n [columns]=\"previewColumns\"\r\n [paginator]=\"true\"\r\n [rows]=\"10\"\r\n [showCurrentPageReport]=\"true\"\r\n currentPageReportTemplate=\"Showing {first} to {last} of {totalRecords} entries\"\r\n styleClass=\"p-datatable-sm\">\r\n <ng-template pTemplate=\"header\">\r\n <tr>\r\n <th *ngFor=\"let col of previewColumns\" class=\"text-left\">\r\n {{ col.header }}\r\n </th>\r\n </tr>\r\n </ng-template>\r\n <ng-template pTemplate=\"body\" let-rowData>\r\n <tr>\r\n <td *ngFor=\"let col of previewColumns\" class=\"text-left\">\r\n {{ rowData[col.field] }}\r\n </td>\r\n </tr>\r\n </ng-template>\r\n <ng-template pTemplate=\"emptymessage\">\r\n <tr>\r\n <td [attr.colspan]=\"previewColumns.length\" class=\"text-center py-4\">\r\n <div class=\"text-center py-8\">\r\n <i class=\"pi pi-inbox text-4xl text-gray-400 dark:text-gray-500 mb-4\"></i>\r\n <p class=\"text-gray-500 dark:text-gray-400\">{{ 'NO_PREVIEW_DATA' | translate }}</p>\r\n </div>\r\n </td>\r\n </tr>\r\n </ng-template>\r\n </p-table>\r\n </div> -->\r\n </div>\r\n\r\n <!-- <div class=\"mt-4 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg\">\r\n <p class=\"text-sm text-green-700 dark:text-green-300\">\r\n <i class=\"pi pi-info-circle mr-2\"></i>\r\n {{ 'PREVIEW_INFO' | translate }}\r\n </p>\r\n </div> -->\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"border-t border-gray-200 dark:border-gray-700 px-2 py-2 bg-gray-50 dark:bg-gray-800\">\r\n <div class=\"flex justify-between items-center\">\r\n <button\r\n type=\"button\"\r\n (click)=\"onReset()\"\r\n class=\"flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors\"\r\n [disabled]=\"loading\">\r\n <i class=\"pi pi-refresh mr-2\"></i>\r\n {{ 'RESET' | translate }}\r\n </button>\r\n\r\n <div class=\"flex gap-2\">\r\n <button\r\n type=\"button\"\r\n (click)=\"ref?.close()\"\r\n class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"\r\n [disabled]=\"loading\">\r\n {{ 'CANCEL' | translate }}\r\n </button>\r\n\r\n <!-- SplitButton for Export -->\r\n <!-- || previewData.length === 0 -->\r\n <p-splitButton\r\n [label]=\"getExportButtonText()\"\r\n [icon]=\"getCurrentExportTypeIcon()\"\r\n [model]=\"exportMenuItems\"\r\n (onClick)=\"onExport()\"\r\n [disabled]=\"loading\"\r\n styleClass=\"p-button-primary\">\r\n </p-splitButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Filters Drawer -->\r\n @if(showFilters){\r\n <p-drawer\r\n [(visible)]=\"showFilters\"\r\n position=\"right\"\r\n [styleClass]=\"'!w-full md:!w-80 lg:!w-[40rem] !h-full' \"\r\n [modal]=\"true\"\r\n [dismissible]=\"true\"\r\n styleClass=\"p-4 w-full max-w-md max-h-[90vh] flex flex-col\">\r\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit(); showFilters=false\" class=\"flex flex-col flex-1\">\r\n <div class=\"flex-1 overflow-auto\">\r\n <formly-form\r\n [form]=\"form\"\r\n [fields]=\"fields_\"\r\n [model]=\"model\"\r\n [options]=\"options\">\r\n </formly-form>\r\n </div>\r\n </form>\r\n <ng-template pTemplate=\"footer\">\r\n <div class=\"flex justify-end mt-2 space-x-2 flex-none\">\r\n <button\r\n type=\"submit\"\r\n pButton size=\"small\"\r\n (click)=\" onSubmit(); showFilters=false\"\r\n label=\"{{ 'SEARCH' | translate }}\">\r\n </button>\r\n <button\r\n type=\"button\"\r\n pButton\r\n size=\"small\"\r\n class=\"p-button-text\"\r\n (click)=\"onReset(); showFilters=false\">\r\n {{ 'CLEAR' | translate }}\r\n </button>\r\n </div>\r\n </ng-template>\r\n </p-drawer>\r\n }\r\n</div>\r\n" }]
534
+ }], ctorParameters: () => [{ type: i1$1.DynamicDialogRef }, { type: i1$1.DynamicDialogConfig }], propDecorators: { previewContainer: [{
467
535
  type: ViewChild,
468
536
  args: ['previewContainer']
469
537
  }], filterFields: [{
@@ -484,5 +552,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
484
552
  * Generated bundle index. Do not edit.
485
553
  */
486
554
 
487
- export { ExportType, GenericReportComponent, GenericReportModule };
555
+ export { ExportType, GenericReportComponent, GenericReportModule, ReportNameDialogComponent };
488
556
  //# sourceMappingURL=elite.framework-ng.ui.core-generic-report.mjs.map