@elite.framework/ng.ui.core 1.0.80 → 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.
@@ -2,8 +2,8 @@ import * as i0 from '@angular/core';
2
2
  import { NgModule, inject, Injectable, EventEmitter, Input, Output, Component, ChangeDetectorRef } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
- import * as i4$1 from '@angular/forms';
6
- import { FormGroup, ReactiveFormsModule, FormsModule } from '@angular/forms';
5
+ import * as i2$1 from '@angular/forms';
6
+ import { FormGroup, ReactiveFormsModule, FormsModule, FormControl, FormArray } from '@angular/forms';
7
7
  import { FormlyForm, FieldType, FormlyField, provideFormlyConfig } from '@ngx-formly/core';
8
8
  import * as i10 from '@ngx-translate/core';
9
9
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
@@ -32,13 +32,14 @@ import * as i6$1 from 'primeng/tooltip';
32
32
  import { TooltipModule } from 'primeng/tooltip';
33
33
  import * as i7$1 from 'primeng/listbox';
34
34
  import { ListboxModule } from 'primeng/listbox';
35
+ import * as i4$1 from 'primeng/checkbox';
35
36
  import { CheckboxModule } from 'primeng/checkbox';
36
37
  import { DatePickerModule } from 'primeng/datepicker';
37
38
  import { RadioButtonModule } from 'primeng/radiobutton';
38
39
  import { MenuModule } from 'primeng/menu';
39
40
  import * as i6$2 from 'primeng/popover';
40
41
  import { PopoverModule } from 'primeng/popover';
41
- import * as i2$1 from 'primeng/tabs';
42
+ import * as i2$2 from 'primeng/tabs';
42
43
  import { TabsModule } from 'primeng/tabs';
43
44
 
44
45
  class GenericSearchAdvancedModule {
@@ -68,36 +69,27 @@ class QueryBuilderService {
68
69
  'radio': { defaultOperator: 'eq', defaultValue: '', operators: ['eq', 'ne'] },
69
70
  'textarea': { defaultOperator: 'contains', defaultValue: '', operators: ['eq', 'ne', 'contains', 'startswith', 'endswith'] }
70
71
  };
71
- buildQueryUIFields(fields) {
72
+ buildQueryUIFields(fields, enableSelect, enableGroup) {
72
73
  return [
73
- // {
74
- // key: 'quickSearch',
75
- // type: 'input',
76
- // props: {
77
- // label: 'بحث سريع',
78
- // placeholder: 'ابحث في جميع الحقول...',
79
- // icon: 'pi pi-search'
80
- // }
81
- // },
82
74
  {
83
75
  type: 'tab-type',
84
- // key: 'queryTabs',
85
76
  props: {
86
77
  tabViewClass: 'advanced-query-tabs',
87
78
  scrollable: false,
88
79
  lazy: true,
89
80
  activeIndex: 0
90
81
  },
91
- fieldGroup: this.getQueryTabs(fields)
82
+ fieldGroup: this.getQueryTabs(fields, enableSelect, enableGroup)
92
83
  }
93
84
  ];
94
85
  }
95
- getQueryTabs(fields) {
96
- return [
86
+ getQueryTabs(fields, enableSelect, enableGroup) {
87
+ const tabs = [
97
88
  {
98
89
  type: 'group',
99
90
  props: {
100
- label: 'FILTERS',
91
+ tabLabel: 'FILTERS',
92
+ // label: 'FILTERS',
101
93
  leftIcon: 'pi pi-filter',
102
94
  badge: (field) => {
103
95
  const fieldGroup = (field?.fieldGroup ?? [])[0];
@@ -109,7 +101,8 @@ class QueryBuilderService {
109
101
  {
110
102
  type: 'group',
111
103
  props: {
112
- label: 'SORTING',
104
+ tabLabel: 'SORTING',
105
+ // label: 'SORTING',
113
106
  leftIcon: 'pi pi-sort-alt',
114
107
  badge: (field) => {
115
108
  const fieldGroup = (field?.fieldGroup ?? [])[0];
@@ -118,11 +111,47 @@ class QueryBuilderService {
118
111
  },
119
112
  fieldGroup: this.getSortingTabFields(fields)
120
113
  },
121
- {
114
+ enableGroup && {
122
115
  type: 'group',
123
116
  props: {
124
- label: 'PAGINATION',
117
+ tabLabel: 'GROUPING',
118
+ // label: 'GROUPING',
125
119
  leftIcon: 'pi pi-table',
120
+ badge: (field) => {
121
+ const fieldGroup = (field?.fieldGroup ?? [])[0];
122
+ return (fieldGroup?.model?.grouping ?? [])?.length || 0;
123
+ },
124
+ },
125
+ fieldGroup: this.getGroupingTabFields(fields)
126
+ },
127
+ enableSelect && {
128
+ type: 'group',
129
+ props: {
130
+ tabLabel: 'COLUMNS',
131
+ // label: 'COLUMNS',
132
+ leftIcon: 'pi pi-list',
133
+ badge: (field) => {
134
+ const fieldGroup = (field?.fieldGroup ?? [])[0];
135
+ const columns = fieldGroup?.model?.columns;
136
+ // Handle both array and object formats for columns
137
+ if (Array.isArray(columns)) {
138
+ return columns.filter((col) => col?.isVisible)?.length || 0;
139
+ }
140
+ else if (columns && typeof columns === 'object') {
141
+ // If columns is an object, count the visible ones
142
+ return Object.values(columns).filter((col) => col?.isVisible)?.length || 0;
143
+ }
144
+ return 0;
145
+ },
146
+ },
147
+ fieldGroup: this.getColumnsTabFields(fields)
148
+ },
149
+ {
150
+ type: 'group',
151
+ props: {
152
+ tabLabel: 'PAGINATION',
153
+ // label: 'PAGINATION',
154
+ leftIcon: 'pi pi-cog',
126
155
  badge: (field) => {
127
156
  return field?.model?.pagination && field?.model?.pagination?.top ? 1 : 0;
128
157
  },
@@ -130,6 +159,7 @@ class QueryBuilderService {
130
159
  fieldGroup: this.getPaginationTabFields()
131
160
  }
132
161
  ];
162
+ return tabs.filter(Boolean);
133
163
  }
134
164
  getFilterTabFields(fields) {
135
165
  return [
@@ -137,8 +167,8 @@ class QueryBuilderService {
137
167
  key: 'advancedFilters',
138
168
  type: 'query-builder',
139
169
  props: {
140
- label: 'مرشحات متقدمة',
141
- description: 'أنشئ مرشحات معقدة باستخدام مشغلين منطقيين',
170
+ label: 'فلاتر متقدمة',
171
+ description: 'أنشئ فلاتر معقدة باستخدام مشغلين منطقيين',
142
172
  fields: this.extractFilterableFields(fields)
143
173
  }
144
174
  }
@@ -157,6 +187,33 @@ class QueryBuilderService {
157
187
  }
158
188
  ];
159
189
  }
190
+ getGroupingTabFields(fields) {
191
+ return [
192
+ {
193
+ key: 'grouping',
194
+ type: 'group-builder',
195
+ props: {
196
+ label: 'تجميع البيانات',
197
+ description: 'قم بتجميع البيانات حسب الحقول المطلوبة',
198
+ fields: this.extractGroupableFields(fields),
199
+ maxGroups: 3
200
+ }
201
+ }
202
+ ];
203
+ }
204
+ getColumnsTabFields(fields) {
205
+ return [
206
+ {
207
+ key: 'columns',
208
+ type: 'columns-builder',
209
+ props: {
210
+ label: 'إدارة الأعمدة',
211
+ description: 'اختر الحقول المراد عرضها وتخصيص التجميع',
212
+ fields: this.extractSelectableFields(fields)
213
+ }
214
+ }
215
+ ];
216
+ }
160
217
  getPaginationTabFields() {
161
218
  return [
162
219
  {
@@ -181,36 +238,40 @@ class QueryBuilderService {
181
238
  description: 'أقصى عدد للسجلات المراد عرضها'
182
239
  }
183
240
  },
184
- // {
185
- // key: 'skip',
186
- // type: 'input',
187
- // props: {
188
- // type: 'number',
189
- // label: 'تخطي السجلات',
190
- // min: 0,
191
- // description: 'عدد السجلات التي سيتم تخطيها'
192
- // }
193
- // }
241
+ {
242
+ key: 'skip',
243
+ type: 'input',
244
+ props: {
245
+ type: 'number',
246
+ label: 'تخطي السجلات',
247
+ min: 0,
248
+ description: 'عدد السجلات التي سيتم تخطيها'
249
+ }
250
+ },
251
+ {
252
+ key: 'includeCount',
253
+ type: 'checkbox',
254
+ props: {
255
+ label: 'تضمين العدد الإجمالي',
256
+ description: 'تضمين عدد السجلات الإجمالي في الاستجابة'
257
+ }
258
+ }
194
259
  ]
195
260
  }
196
261
  ];
197
262
  }
198
- getFilterCount() {
199
- return '0';
200
- }
201
263
  extractFilterableFields(fields) {
202
264
  const filterableFields = [];
203
265
  this.traverseFields(fields, (field) => {
204
- if (field.key && field.props && field.props['filterable'] !== false && !field.props?.hidden) {
266
+ if (field.key && field.props && ((field.props['filter'] && field.props['filter'].hidden == false) || !field.props['filter']) && !field.props?.hidden) {
205
267
  const fieldType = this.mapFieldType(field.type);
206
268
  const fieldConfig = this.fieldTypeConfig[fieldType] || this.fieldTypeConfig.input;
207
269
  filterableFields.push({
208
- key: field.key,
270
+ key: field.props['entityKey'] ? field.props['entityKey'] : field.key,
209
271
  label: this.translate.instant(field.props.label || field.key),
210
272
  type: fieldType,
211
273
  props: { ...field.props },
212
274
  operators: fieldConfig.operators,
213
- // Add default values for QueryBuilderComponent compatibility
214
275
  defaultOperator: fieldConfig.defaultOperator,
215
276
  defaultValue: fieldConfig.defaultValue
216
277
  });
@@ -221,26 +282,44 @@ class QueryBuilderService {
221
282
  extractSortableFields(fields) {
222
283
  const sortableFields = [];
223
284
  this.traverseFields(fields, (field) => {
224
- if (field.key && field.props && field.props['sortable'] !== false && !field.props?.hidden) {
285
+ if (field.key && field.props && ((field.props['sort'] && field.props['sort'].hidden == false) || !field.props['sort']) && !field.props?.hidden) {
225
286
  sortableFields.push({
226
- key: field.key,
287
+ key: field.props['entityKey'] ? field.props['entityKey'] : field.key,
227
288
  label: this.translate.instant(field.props.label || field.key)
228
289
  });
229
290
  }
230
291
  });
231
292
  return sortableFields;
232
293
  }
233
- extractExpandableFields(fields) {
234
- const expandableFields = [];
294
+ extractGroupableFields(fields) {
295
+ const groupableFields = [];
235
296
  this.traverseFields(fields, (field) => {
236
- if (field.key && field.props && field.props['expandable'] && !field.props?.hidden) {
237
- expandableFields.push({
238
- label: field.props.label || field.key,
239
- value: field.key
297
+ if (field.key && field.props && ((field.props['group'] && field.props['group'].hidden == false) || !field.props['group']) && !field.props?.hidden) {
298
+ groupableFields.push({
299
+ key: field.props['entityKey'] ? field.props['entityKey'] : field.key,
300
+ label: this.translate.instant(field.props.label || field.key),
301
+ dataType: this.getDataType(field.type)
240
302
  });
241
303
  }
242
304
  });
243
- return expandableFields;
305
+ return groupableFields;
306
+ }
307
+ extractSelectableFields(fields) {
308
+ const selectableFields = [];
309
+ this.traverseFields(fields, (field) => {
310
+ if (field.key && field.props && ((field.props['select'] && field.props['select'].hidden == false) || !field.props['select']) && !field.props?.hidden) {
311
+ const isNumeric = ['number', 'currency'].includes(field.type);
312
+ selectableFields.push({
313
+ key: field.props['entityKey'] ? field.props['entityKey'] : field.key,
314
+ label: this.translate.instant(field.props.label || field.key),
315
+ dataType: this.getDataType(field.type),
316
+ isNumeric: isNumeric,
317
+ defaultVisible: field.props['defaultVisible'] !== false,
318
+ aggregatable: isNumeric && field.props['aggregatable'] !== false
319
+ });
320
+ }
321
+ });
322
+ return selectableFields;
244
323
  }
245
324
  traverseFields(fields, callback) {
246
325
  fields.forEach(field => {
@@ -250,6 +329,58 @@ class QueryBuilderService {
250
329
  }
251
330
  });
252
331
  }
332
+ getAppliedFiltersCount(model) {
333
+ const breakdown = {
334
+ filters: 0,
335
+ sorting: 0,
336
+ grouping: 0,
337
+ columns: 0,
338
+ pagination: 0,
339
+ expand: 0
340
+ };
341
+ // Advanced filters
342
+ if (model?.advancedFilters && Array.isArray(model.advancedFilters)) {
343
+ model.advancedFilters.forEach((filterGroup) => {
344
+ if (filterGroup.conditions && Array.isArray(filterGroup.conditions)) {
345
+ breakdown.filters += filterGroup.conditions.length;
346
+ }
347
+ });
348
+ }
349
+ // Sorting
350
+ if (model?.sorting && Array.isArray(model.sorting)) {
351
+ breakdown.sorting = model.sorting.length;
352
+ }
353
+ // Grouping
354
+ if (model?.grouping && Array.isArray(model.grouping)) {
355
+ breakdown.grouping = model.grouping.length;
356
+ }
357
+ // Columns
358
+ if (model?.columns) {
359
+ if (Array.isArray(model.columns)) {
360
+ breakdown.columns = model.columns.filter((col) => col.isVisible).length;
361
+ }
362
+ else if (typeof model.columns === 'object') {
363
+ breakdown.columns = Object.values(model.columns).filter((col) => col?.isVisible).length;
364
+ }
365
+ }
366
+ // Pagination
367
+ if (model?.pagination) {
368
+ if (model.pagination.top)
369
+ breakdown.pagination += 1;
370
+ if (model.pagination.skip)
371
+ breakdown.pagination += 1;
372
+ if (model.pagination.includeCount)
373
+ breakdown.pagination += 1;
374
+ }
375
+ // Expand
376
+ if (model?.expand && Array.isArray(model.expand)) {
377
+ breakdown.expand = model.expand.length;
378
+ }
379
+ return {
380
+ total: Object.values(breakdown).reduce((sum, count) => sum + count, 0),
381
+ breakdown
382
+ };
383
+ }
253
384
  mapFieldType(type) {
254
385
  const typeMap = {
255
386
  'input': 'input',
@@ -260,30 +391,49 @@ class QueryBuilderService {
260
391
  'generic-selector': 'generic-selector',
261
392
  'switch': 'switch',
262
393
  'radio': 'radio',
263
- 'textarea': 'textarea'
394
+ 'textarea': 'textarea',
395
+ 'currency': 'number'
264
396
  };
265
397
  return typeMap[type || 'input'] || 'input';
266
398
  }
399
+ getDataType(type) {
400
+ const dataTypeMap = {
401
+ 'input': 'string',
402
+ 'number': 'number',
403
+ 'datepicker': 'date',
404
+ 'select': 'string',
405
+ 'checkbox': 'boolean',
406
+ 'generic-selector': 'string',
407
+ 'switch': 'boolean',
408
+ 'radio': 'string',
409
+ 'textarea': 'string',
410
+ 'currency': 'number'
411
+ };
412
+ return dataTypeMap[type || 'input'] || 'string';
413
+ }
267
414
  getOperatorsForType(type) {
268
415
  const fieldType = this.mapFieldType(type);
269
416
  const config = this.fieldTypeConfig[fieldType];
270
417
  return config ? config.operators : ['eq', 'ne'];
271
418
  }
272
- // OData methods remain the same
419
+ // Enhanced OData methods with GroupBy and Select support
273
420
  buildODataFromQueryModel(model, originalFields) {
274
421
  const params = {
275
422
  filters: [],
276
423
  orderBy: [],
277
424
  groupBy: [],
278
- expand: model.expand || []
425
+ select: [],
426
+ aggregates: [],
427
+ expand: model.expand || [],
428
+ includeCount: model.pagination?.includeCount || false
279
429
  };
280
430
  // Quick search
281
- if (model.quickSearch) {
282
- const searchableFields = this.extractFilterableFields(originalFields);
431
+ if (model.filter) {
432
+ const searchableFields = this.extractSelectableFields(originalFields); // TODO get all strings filters
283
433
  const quickSearchConditions = searchableFields.map(field => ({
284
434
  field: field.key,
285
435
  operator: 'contains',
286
- value: model.quickSearch
436
+ value: model.filter
287
437
  }));
288
438
  if (quickSearchConditions.length > 0) {
289
439
  params.filters.push({
@@ -305,6 +455,80 @@ class QueryBuilderService {
305
455
  direction: sort.direction
306
456
  }));
307
457
  }
458
+ // Grouping
459
+ if (model.grouping && Array.isArray(model.grouping)) {
460
+ params.groupBy = model.grouping
461
+ .filter((group) => group.field)
462
+ .map((group, index) => ({
463
+ propertyName: group.field,
464
+ displayName: group.displayName || group.field,
465
+ showTotal: group.showTotal !== false,
466
+ order: index
467
+ }));
468
+ }
469
+ // Columns/Select - Handle both array and object formats
470
+ // extractSelectableFields
471
+ if (model.columns) {
472
+ let visibleColumns = [];
473
+ // Handle array format: [{field: 'name', isVisible: true, ...}, ...]
474
+ if (Array.isArray(model.columns)) {
475
+ visibleColumns = model.columns.filter((col) => col.isVisible);
476
+ }
477
+ // Handle object format: {field1: {isVisible: true, displayName: 'Name', ...}, field2: {...}}
478
+ else if (typeof model.columns === 'object') {
479
+ visibleColumns = Object.entries(model.columns)
480
+ .filter(([key, col]) => col.isVisible)
481
+ .map(([key, col]) => ({
482
+ field: key, // Use the object key as field name
483
+ ...col // Spread the column properties
484
+ }));
485
+ }
486
+ // Process visible columns for select and aggregates
487
+ if (visibleColumns.length > 0) {
488
+ params.select = visibleColumns.map((col, index) => ({
489
+ propertyName: col.field || col.key, // Support both 'field' and 'key' properties
490
+ displayName: col.displayName || col.field || col.key || col.label,
491
+ aggregateFunction: col.aggregateFunction,
492
+ isVisible: true,
493
+ order: index
494
+ }));
495
+ // Extract aggregates from columns with aggregate functions
496
+ const aggregateColumns = visibleColumns.filter((col) => col.aggregateFunction);
497
+ params.aggregates = aggregateColumns.map((col) => ({
498
+ propertyName: col.field || col.key,
499
+ aggregateFunction: col.aggregateFunction,
500
+ alias: `${col.aggregateFunction}_${col.field || col.key}`
501
+ }));
502
+ }
503
+ }
504
+ else {
505
+ // Fallback: auto-generate from formly fields
506
+ const selectableFields = this.extractSelectableFields(originalFields);
507
+ // Ensure a column for the ID field exists
508
+ // if (!selectableFields.some(f => f.key === this.idField)) {
509
+ // selectableFields.unshift({
510
+ // key: this.idField,
511
+ // label: this.translate.instant(this.idField),
512
+ // dataType: 'number', // or the correct type
513
+ // isNumeric: false,
514
+ // defaultVisible: true,
515
+ // aggregatable: false
516
+ // });
517
+ // }
518
+ params.select = selectableFields.map((f, i) => ({
519
+ propertyName: f.key,
520
+ displayName: f.label,
521
+ isVisible: f.defaultVisible,
522
+ order: i
523
+ }));
524
+ params.aggregates = selectableFields
525
+ .filter(f => f.aggregatable)
526
+ .map(f => ({
527
+ propertyName: f.key,
528
+ aggregateFunction: 'sum',
529
+ alias: `sum_${f.key}`
530
+ }));
531
+ }
308
532
  // Pagination
309
533
  if (model.pagination) {
310
534
  if (model.pagination.top) {
@@ -313,6 +537,9 @@ class QueryBuilderService {
313
537
  if (model.pagination.skip) {
314
538
  params.skip = Number(model.pagination.skip);
315
539
  }
540
+ if (model.pagination.includeCount !== undefined) {
541
+ params.includeCount = model.pagination.includeCount;
542
+ }
316
543
  }
317
544
  return params;
318
545
  }
@@ -322,60 +549,20 @@ class QueryBuilderService {
322
549
  parseODataQuery(queryString) {
323
550
  return QueryParser.parse(queryString);
324
551
  }
325
- parseODataToQueryModel(queryString, originalFields) {
326
- const params = this.parseODataQuery(queryString);
327
- const model = {
328
- advancedFilters: [],
329
- sorting: [],
330
- pagination: {}
331
- };
332
- // Parse filters back to UI model
333
- if (params.filters && params.filters.length > 0) {
334
- params.filters.forEach((group) => {
335
- if (group.logicalOperator === 'or' && group.conditions.length > 1) {
336
- const firstValue = group.conditions[0].value;
337
- if (group.conditions.every(cond => cond.operator === 'contains' && cond.value === firstValue)) {
338
- model.quickSearch = firstValue;
339
- return;
340
- }
341
- }
342
- group.conditions.forEach(condition => {
343
- model.advancedFilters.push({
344
- field: condition.field,
345
- operator: condition.operator,
346
- value: condition.value
347
- });
348
- });
349
- });
350
- }
351
- // Parse sorting
352
- if (params.orderBy) {
353
- model.sorting = params.orderBy.map(order => ({
354
- field: order.field,
355
- direction: order.direction
356
- }));
357
- }
358
- // Parse pagination
359
- if (params.top !== undefined) {
360
- model.pagination.top = params.top;
361
- }
362
- if (params.skip !== undefined) {
363
- model.pagination.skip = params.skip;
364
- }
365
- // Parse expand
366
- if (params.expand) {
367
- model.expand = params.expand;
368
- }
369
- return model;
370
- }
371
- buildODataQueryString(model, originalFields) {
372
- const params = this.buildODataFromQueryModel(model, originalFields);
373
- return this.toODataQueryString(params);
374
- }
375
- // Helper method to get field configuration for QueryBuilderComponent
552
+ // Helper method to get field configuration
376
553
  getFieldConfig(fieldKey, fields) {
377
554
  return fields.find(f => f.key === fieldKey);
378
555
  }
556
+ // Helper to get available aggregate functions for numeric fields
557
+ getAggregateFunctions() {
558
+ return [
559
+ { label: 'مجموع', value: 'sum' },
560
+ { label: 'متوسط', value: 'avg' },
561
+ { label: 'عدد', value: 'count' },
562
+ { label: 'الحد الأدنى', value: 'min' },
563
+ { label: 'الحد الأقصى', value: 'max' }
564
+ ];
565
+ }
379
566
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
380
567
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderService, providedIn: 'root' });
381
568
  }
@@ -399,7 +586,8 @@ class GenericSearchAdvanced {
399
586
  options = {};
400
587
  fields = [];
401
588
  fields_ = [];
402
- enableQueryBuilder = true;
589
+ enableSelect = false;
590
+ enableGroup = false;
403
591
  odataConfig = {};
404
592
  ngOnInit() {
405
593
  this.buildQueryUIFields();
@@ -411,12 +599,7 @@ class GenericSearchAdvanced {
411
599
  return filters;
412
600
  }
413
601
  buildQueryUIFields() {
414
- if (this.enableQueryBuilder) {
415
- this.fields_ = this.queryBuilder.buildQueryUIFields(this.fields);
416
- }
417
- else {
418
- this.fields_ = this.fields;
419
- }
602
+ this.fields_ = this.queryBuilder.buildQueryUIFields(this.fields, this.enableSelect, this.enableGroup);
420
603
  }
421
604
  onSubmit() {
422
605
  // Use the new QueryBuilderService methods
@@ -465,7 +648,7 @@ class GenericSearchAdvanced {
465
648
  this.drawerVisible = false;
466
649
  }
467
650
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericSearchAdvanced, deps: [], target: i0.ɵɵFactoryTarget.Component });
468
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.8", type: GenericSearchAdvanced, isStandalone: true, selector: "lib-generic-search-advanced", inputs: { model: "model", fields: "fields", enableQueryBuilder: "enableQueryBuilder", odataConfig: "odataConfig" }, outputs: { search: "search", paginationChange: "paginationChange", odataSearch: "odataSearch" }, ngImport: i0, template: "<div class=\"flex flex-wrap items-center gap-3 w-full\">\r\n <!-- Search Input with Icon -->\r\n <p-inputgroup class=\"w-full\">\r\n <input\r\n pInputText\r\n type=\"text\"\r\n [(ngModel)]=\"model['filter']\"\r\n placeholder=\"{{ 'SEARCH' | translate }}\"\r\n (keyup.enter)=\"onSubmit()\"\r\n />\r\n\r\n\r\n <p-inputgroup-addon>\r\n <p-button\r\n icon=\"pi pi-search\"\r\n severity=\"secondary\"\r\n (click)=\"onSubmit()\"\r\n ></p-button>\r\n </p-inputgroup-addon>\r\n<p-inputgroup-addon>\r\n <div class=\"relative inline-flex\">\r\n <p-button\r\n icon=\"pi pi-filter\"\r\n severity=\"secondary\"\r\n (onClick)=\"drawerVisible = true\">\r\n </p-button>\r\n\r\n <p-badge\r\n *ngIf=\"filterCount > 0\"\r\n [value]=\"filterCount\"\r\n severity=\"warn\"\r\n size=\"small\"\r\n class=\"absolute -top-0 -right-0\">\r\n </p-badge>\r\n </div>\r\n</p-inputgroup-addon>\r\n\r\n <p-inputgroup-addon *ngIf=\"model['filter'] || filterCount > 0\">\r\n <p-button\r\n icon=\"pi pi-times\"\r\n severity=\"danger\"\r\n (click)=\"model['filter']=''; onReset()\"\r\n ></p-button>\r\n </p-inputgroup-addon>\r\n</p-inputgroup>\r\n\r\n\r\n</div>\r\n\r\n<!-- Drawer for Advanced Multi-field Search -->\r\n @if(drawerVisible){\r\n<p-drawer\r\n [(visible)]=\"drawerVisible\"\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(); drawerVisible=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(); drawerVisible=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(); drawerVisible=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: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: ToolbarModule }, { kind: "directive", type: i2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { 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: "component", type: i4.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i4$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4$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: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "ngmodule", type: IconFieldModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { 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: "ngmodule", type: InputGroupModule }, { kind: "component", type: i7.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }, { kind: "ngmodule", type: InputGroupAddonModule }, { kind: "component", type: i8.InputGroupAddon, selector: "p-inputgroup-addon, p-inputGroupAddon", inputs: ["style", "styleClass"] }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i9.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "pipe", type: i10.TranslatePipe, name: "translate" }] });
651
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.8", type: GenericSearchAdvanced, isStandalone: true, selector: "lib-generic-search-advanced", inputs: { model: "model", fields: "fields", enableSelect: "enableSelect", enableGroup: "enableGroup", odataConfig: "odataConfig" }, outputs: { search: "search", paginationChange: "paginationChange", odataSearch: "odataSearch" }, ngImport: i0, template: "<div class=\"flex flex-wrap items-center gap-3 w-full\">\r\n <!-- Search Input with Icon -->\r\n <p-inputgroup class=\"w-full\">\r\n <input\r\n pInputText\r\n type=\"text\"\r\n [(ngModel)]=\"model['filter']\"\r\n placeholder=\"{{ 'SEARCH' | translate }}\"\r\n (keyup.enter)=\"onSubmit()\"\r\n />\r\n\r\n\r\n <p-inputgroup-addon>\r\n <p-button\r\n icon=\"pi pi-search\"\r\n severity=\"secondary\"\r\n (click)=\"onSubmit()\"\r\n ></p-button>\r\n </p-inputgroup-addon>\r\n<p-inputgroup-addon>\r\n <div class=\"relative inline-flex\">\r\n <p-button\r\n icon=\"pi pi-filter\"\r\n severity=\"secondary\"\r\n (onClick)=\"drawerVisible = true\">\r\n </p-button>\r\n\r\n <p-badge\r\n *ngIf=\"filterCount > 0\"\r\n [value]=\"filterCount\"\r\n severity=\"warn\"\r\n size=\"small\"\r\n class=\"absolute -top-0 -right-0\">\r\n </p-badge>\r\n </div>\r\n</p-inputgroup-addon>\r\n\r\n <p-inputgroup-addon *ngIf=\"model['filter'] || filterCount > 0\">\r\n <p-button\r\n icon=\"pi pi-times\"\r\n severity=\"danger\"\r\n (click)=\"model['filter']=''; onReset()\"\r\n ></p-button>\r\n </p-inputgroup-addon>\r\n</p-inputgroup>\r\n\r\n\r\n</div>\r\n\r\n<!-- Drawer for Advanced Multi-field Search -->\r\n @if(drawerVisible){\r\n<p-drawer\r\n [(visible)]=\"drawerVisible\"\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(); drawerVisible=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(); drawerVisible=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(); drawerVisible=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: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: ToolbarModule }, { kind: "directive", type: i2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { 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: "component", type: i4.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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: "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: InputIconModule }, { kind: "ngmodule", type: IconFieldModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { 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: "ngmodule", type: InputGroupModule }, { kind: "component", type: i7.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }, { kind: "ngmodule", type: InputGroupAddonModule }, { kind: "component", type: i8.InputGroupAddon, selector: "p-inputgroup-addon, p-inputGroupAddon", inputs: ["style", "styleClass"] }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i9.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "pipe", type: i10.TranslatePipe, name: "translate" }] });
469
652
  }
470
653
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericSearchAdvanced, decorators: [{
471
654
  type: Component,
@@ -495,7 +678,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
495
678
  type: Input
496
679
  }], fields: [{
497
680
  type: Input
498
- }], enableQueryBuilder: [{
681
+ }], enableSelect: [{
682
+ type: Input
683
+ }], enableGroup: [{
499
684
  type: Input
500
685
  }], odataConfig: [{
501
686
  type: Input
@@ -774,7 +959,7 @@ class SortBuilderComponent extends FieldType {
774
959
  </div>
775
960
  </div>
776
961
  </div>
777
- `, isInline: true, styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { 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: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5$1.Draggable, selector: "[pDraggable]", inputs: ["pDraggable", "dragEffect", "dragHandle", "pDraggableDisabled"], outputs: ["onDragStart", "onDragEnd", "onDrag"] }, { kind: "directive", type: i5$1.Droppable, selector: "[pDroppable]", inputs: ["pDroppable", "pDroppableDisabled", "dropEffect"], outputs: ["onDragEnter", "onDragLeave", "onDrop"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }] });
962
+ `, isInline: true, styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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: SelectModule }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { 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: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5$1.Draggable, selector: "[pDraggable]", inputs: ["pDraggable", "dragEffect", "dragHandle", "pDraggableDisabled"], outputs: ["onDragStart", "onDragEnd", "onDrag"] }, { kind: "directive", type: i5$1.Droppable, selector: "[pDroppable]", inputs: ["pDroppable", "pDroppableDisabled", "dropEffect"], outputs: ["onDragEnter", "onDragLeave", "onDrop"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }] });
778
963
  }
779
964
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SortBuilderComponent, decorators: [{
780
965
  type: Component,
@@ -1252,7 +1437,7 @@ class QueryBuilderComponent extends FieldType {
1252
1437
  }
1253
1438
  }
1254
1439
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1255
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: QueryBuilderComponent, isStandalone: true, selector: "formly-query-builder", usesInheritance: true, ngImport: i0, template: "<div>\r\n\r\n <!-- Groups Container with Connecting Lines -->\r\n <div class=\"groups-container\" #groupsContainer>\r\n <div *ngFor=\"let group of groups; let groupIndex = index\"\r\n class=\"group-wrapper relative\"\r\n #groupElement>\r\n\r\n <!-- Vertical Connector Line from Previous Group -->\r\n<div *ngIf=\"groupIndex > 0\" class=\"vertical-connector\">\r\n <div class=\"vertical-line\"></div>\r\n\r\n <!-- Operator Button -->\r\n <button\r\n type=\"button\"\r\n class=\"group-logical-operator-box\"\r\n [class.and]=\"group.groupLogicalOperator === 'and'\"\r\n [class.or]=\"group.groupLogicalOperator === 'or'\"\r\n (click)=\"opPopover.toggle($event)\">\r\n <span class=\"operator-text\">\r\n {{ getLogicalOperatorText(group.groupLogicalOperator || 'and') }}\r\n </span>\r\n <div class=\"connector-arrow\"></div>\r\n </button>\r\n\r\n <!-- Popover -->\r\n <p-popover #opPopover appendTo=\"body\">\r\n <div class=\"w-12rem\">\r\n <p-listbox\r\n [options]=\"logicalOperators\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [(ngModel)]=\"group.groupLogicalOperator\"\r\n (onChange)=\" opPopover.toggle($event)\"\r\n [style]=\"{ width: '100%' }\">\r\n </p-listbox>\r\n </div>\r\n </p-popover>\r\n</div>\r\n\r\n <div class=\"group-container mb-6 p-1 bg-white rounded-lg border-l-4 border-t-1 border-b-1 border-r-1 shadow-sm transition-all duration-300 relative\"\r\n [class.border-blue-500]=\"group.logicalOperator === 'and'\"\r\n [class.border-or-500]=\"group.logicalOperator === 'or'\"\r\n [class.pulse]=\"groupIndex === 0\">\r\n\r\n <!-- Rounded Button for \"\u0631\u0628\u0637 \u0627\u0644\u0634\u0631\u0648\u0637\" on left border -->\r\n<div\r\n class=\"absolute -left-4 top-1/2 transform -translate-y-1/2 z-10 transition-all duration-300\"\r\n\r\n>\r\n\r\n\r\n\r\n<!-- Logical Operator Popover Button -->\r\n<p-button\r\n [severity]=\"group.logicalOperator === 'and' ? 'success' : 'info'\"\r\n [styleClass]=\"group.logicalOperator === 'and'\r\n ? 'p-button-icon-only p-button-rounded p-button-sm logical-and'\r\n : 'p-button-icon-only p-button-rounded p-button-sm logical-or'\"\r\n (click)=\"opPopover.toggle($event)\">\r\n {{group.logicalOperator === 'and' ? ('and' | translate) : ('or' | translate)}}\r\n</p-button>\r\n\r\n<!-- Popover for Operator Selection -->\r\n<p-popover #opPopover appendTo=\"body\">\r\n <div class=\"w-10rem\">\r\n <p-listbox\r\n [options]=\"[\r\n { label: 'and' | translate, value: 'and' },\r\n { label: 'or' | translate, value: 'or' }\r\n ]\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [(ngModel)]=\"group.logicalOperator\"\r\n (onChange)=\"updateValue(); opPopover.toggle($event)\"\r\n [style]=\"{ width: '100%' }\">\r\n <ng-template let-item pTemplate=\"item\">\r\n {{ item.label }}\r\n </ng-template>\r\n </p-listbox>\r\n </div>\r\n</p-popover>\r\n\r\n</div>\r\n\r\n\r\n\r\n <!-- Group Header -->\r\n <div class=\"group-header-wrapper mb-4 pl-8\">\r\n <div class=\"group-header flex justify-between items-center\">\r\n\r\n <div class=\"flex gap-1\">\r\n <button\r\n pButton\r\n icon=\"pi pi-plus\"\r\n type=\"button\"\r\n class=\"p-button-success p-button p-button-sm !p-1.5 rounded-md transition-colors\"\r\n (click)=\"addCondition(groupIndex)\"\r\n pTooltip=\"{{ 'ADD_CONDITION' | translate }}\"\r\n tooltipPosition=\"top\">\r\n </button>\r\n <button\r\n *ngIf=\"groups.length > 1\"\r\n pButton\r\n icon=\"pi pi-times\"\r\n type=\"button\"\r\n class=\"p-button-danger p-button p-button-sm !p-1.5 rounded-md transition-colors\"\r\n (click)=\"removeGroup(groupIndex)\"\r\n pTooltip=\"{{ 'DELETE_GROUP' | translate }}\"\r\n tooltipPosition=\"top\">\r\n </button>\r\n</div>\r\n </div>\r\n </div>\r\n\r\n <!-- Conditions in this group -->\r\n <div *ngFor=\"let condition of group.conditions; let conditionIndex = index\"\r\n class=\"p-1 bg-gray-50 \">\r\n <div class=\"grid grid-cols-1 gap-1 items-center\">\r\n <!-- Field Selector -->\r\n <div class=\"md:col-span-2\">\r\n <div class=\"flex w-full rounded-md overflow-hidden border border-gray-300 bg-white shadow-sm\">\r\n <!-- Field Selector Button -->\r\n <div style=\"align-items: center;display: flex; min-width: 130px;max-width: 130px;\" class=\"flex-shrink-0 \">\r\n <button\r\n type=\"button\"\r\n style=\"height: -webkit-fill-available;\"\r\n class=\"flex items-center justify-between w-full px-3 py-2 text-sm bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:z-10\"\r\n (click)=\"overlay.toggle($event)\">\r\n <span style=\"\r\n display: inline-block;\r\n max-width: 100px;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n \">{{ getFieldLabel(condition.field) || ('SELECT_FIELD' | translate) }}</span>\r\n <i class=\"pi pi-chevron-down mr-2 ml-2 text-xs\"></i>\r\n </button>\r\n\r\n <!-- Overlay Content -->\r\n <p-popover #overlay appendTo=\"body\">\r\n <div class=\"w-60\">\r\n <p-listbox\r\n tabindex=\"0\"\r\n [options]=\"props['fields']\"\r\n optionLabel=\"label\"\r\n optionValue=\"key\"\r\n [(ngModel)]=\"condition.field\"\r\n (onChange)=\"onFieldChange(condition, groupIndex, conditionIndex); overlay.toggle($event)\"\r\n [style]=\"{ width: '100%' }\">\r\n </p-listbox>\r\n </div>\r\n </p-popover>\r\n </div>\r\n <!-- Operator Button -->\r\n <div style=\"align-items: center;display: flex;\" class=\"flex-shrink-0 border-l border-r border-gray-300\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center justify-center w-full px-3 py-2 text-sm bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:z-10\"\r\n style=\"height: -webkit-fill-available;\"\r\n pTooltip=\"{{ getOperatorLabel(condition.operator).label }}\"\r\n (click)=\"opPopover.toggle($event)\">\r\n <i [class]=\"getOperatorLabel(condition.operator).icon\"></i>\r\n </button>\r\n\r\n <p-popover #opPopover appendTo=\"body\">\r\n <div class=\"w-60\">\r\n <p-listbox\r\n [options]=\"condition.operatorsForField\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [(ngModel)]=\"condition.operator\"\r\n (onChange)=\"opPopover.toggle($event)\"\r\n [style]=\"{ width: '100%' }\">\r\n </p-listbox>\r\n </div>\r\n </p-popover>\r\n </div>\r\n <!-- Form Field -->\r\n <div style=\"background: #f9fafb\" class=\"flex-grow p-1\">\r\n <formly-form\r\n [form]=\"form\"\r\n [fields]=\"[condition.valueFieldConfig]\"\r\n [model]=\"model\">\r\n </formly-form>\r\n </div>\r\n\r\n\r\n\r\n <!-- Delete Button -->\r\n <div style=\"align-items: center;display: flex;\" class=\"flex-shrink-0\">\r\n <button\r\n style=\"height: -webkit-fill-available;\"\r\n class=\"flex items-center justify-center w-full px-3 py-2 text-sm bg-red-50 text-red-600 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-red-500 focus:z-10\"\r\n type=\"button\"\r\n (click)=\"removeCondition(groupIndex, conditionIndex)\"\r\n pTooltip=\"\u062D\u0630\u0641 \u0627\u0644\u0634\u0631\u0637\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-times\"></i>\r\n </button>\r\n </div>\r\n</div>\r\n </div>\r\n\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Group Footer -->\r\n <div class=\"group-footer flex justify-between items-center mt-3 pt-3 border-t border-gray-200 \">\r\n <small class=\"text-gray-500\">\r\n {{ group.conditions.length }} \u0634\u0631\u0637 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u0645\u062C\u0645\u0648\u0639\u0629\r\n </small>\r\n <small class=\"text-gray-500\">\r\n <span *ngIf=\"groupIndex > 0\">\r\n \u0645\u0631\u062A\u0628\u0637 \u0645\u0639 \u0627\u0644\u0633\u0627\u0628\u0642\u0629 \u0628\u0640 <strong [class.text-blue-600]=\"group.groupLogicalOperator === 'and'\"\r\n [class.text-amber-600]=\"group.groupLogicalOperator === 'or'\">\r\n {{ group.groupLogicalOperator === 'and' ? '\u0648' : '\u0623\u0648' }}\r\n </strong>\r\n </span>\r\n </small>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Actions -->\r\n<div class=\"flex flex-wrap gap-3 mt-6\">\r\n <button\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-plus\"\r\n [label]=\"'GROUP' | translate\"\r\n class=\"p-button-outlined p-button-sm !bg-white !border !border-blue-500 !text-blue-600 hover:!bg-blue-50 transition-colors flex items-center gap-2\"\r\n (click)=\"addGroup();\">\r\n </button>\r\n\r\n <button\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-plus\"\r\n [label]=\"'CONDITION' | translate\"\r\n class=\"p-button-outlined p-button-sm !bg-white !border !border-blue-500 !text-blue-600 hover:!bg-blue-50 transition-colors flex items-center gap-2\"\r\n (click)=\"addConditionToLastGroup()\">\r\n </button>\r\n\r\n <button\r\n *ngIf=\"groups.length > 0\"\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-trash\"\r\n [label]=\"'CLEAR' | translate\"\r\n class=\"p-button-danger p-button-outlined p-button-sm !bg-white !border !border-red-500 !text-red-600 hover:!bg-red-50 transition-colors flex items-center gap-2\"\r\n (click)=\"clearAll()\">\r\n </button>\r\n</div>\r\n\r\n<!-- Empty State -->\r\n<div *ngIf=\"groups.length === 0\" class=\"empty-state text-center p-6 border-2 border-dashed border-gray-300 rounded-xl bg-gray-50\">\r\n <i class=\"pi pi-search text-4xl text-gray-400 mb-3\"></i>\r\n <p class=\"text-gray-500 mb-4\">{{ 'NO_SEARCH_CONDITIONS' | translate }}</p>\r\n <button\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-plus\"\r\n [label]=\"'ADD_SEARCH_CONDITION' | translate\"\r\n class=\"p-button-outlined !px-4 !py-2\"\r\n (click)=\"addGroup()\">\r\n </button>\r\n</div>\r\n</div>\r\n", styles: [".vertical-connector{position:relative;display:flex;justify-content:center;margin-bottom:1rem}.vertical-line{position:absolute;top:-1rem;height:1rem;width:2px;background-color:#d1d5db}.group-logical-operator-box{position:relative;padding:.25rem .75rem;border-radius:.375rem;font-weight:500;font-size:.75rem;z-index:10;transition:all .3s ease}.group-logical-operator-box.and{background-color:#dbeafe;color:#1d4ed8;border:1px solid #93c5fd}.group-logical-operator-box.or{background-color:#fef3c7;color:#92400e;border:1px solid #fcd34d}.connector-arrow{position:absolute;top:100%;left:50%;transform:translate(-50%);width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent}.group-logical-operator-box.and .connector-arrow{border-top:6px solid #93c5fd}.group-logical-operator-box.or .connector-arrow{border-top:6px solid #fcd34d}.border-blue-500{border-color:#93c5fd}.border-or-500{border-color:#fcd34d}.border-t-1{border-top-width:1px}.border-b-1{border-bottom-width:1px}.border-r-1{border-right-width:1px}.p-popover-content{padding:5px!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: SelectModule }, { kind: "directive", type: i2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: InputTextModule }, { 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: "component", type: i4.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "ngmodule", type: CheckboxModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "ngmodule", type: MenuModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: InputGroupModule }, { kind: "ngmodule", type: InputGroupAddonModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i6$2.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ListboxModule }, { kind: "component", type: i7$1.Listbox, selector: "p-listbox, p-listBox, p-list-box", inputs: ["id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "ariaLabel", "selectOnFocus", "searchLocale", "focusOnHover", "filterMessage", "filterFields", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "scrollHeight", "tabindex", "multiple", "styleClass", "listStyle", "listStyleClass", "readonly", "checkbox", "filter", "filterBy", "filterMatchMode", "filterLocale", "metaKeySelection", "dataKey", "showToggleAll", "optionLabel", "optionValue", "optionGroupChildren", "optionGroupLabel", "optionDisabled", "ariaFilterLabel", "filterPlaceHolder", "emptyFilterMessage", "emptyMessage", "group", "options", "filterValue", "selectAll", "striped", "highlightOnSelect", "checkmark", "dragdrop", "fluid"], outputs: ["onChange", "onClick", "onDblClick", "onFilter", "onFocus", "onBlur", "onSelectAllChange", "onLazyLoad", "onDrop"] }, { kind: "pipe", type: i10.TranslatePipe, name: "translate" }] });
1440
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: QueryBuilderComponent, isStandalone: true, selector: "formly-query-builder", usesInheritance: true, ngImport: i0, template: "<div>\r\n\r\n <!-- Groups Container with Connecting Lines -->\r\n <div class=\"groups-container\" #groupsContainer>\r\n <div *ngFor=\"let group of groups; let groupIndex = index\"\r\n class=\"group-wrapper relative\"\r\n #groupElement>\r\n\r\n <!-- Vertical Connector Line from Previous Group -->\r\n<div *ngIf=\"groupIndex > 0\" class=\"vertical-connector\">\r\n <div class=\"vertical-line\"></div>\r\n\r\n <!-- Operator Button -->\r\n <button\r\n type=\"button\"\r\n class=\"group-logical-operator-box\"\r\n [class.and]=\"group.groupLogicalOperator === 'and'\"\r\n [class.or]=\"group.groupLogicalOperator === 'or'\"\r\n (click)=\"opPopover.toggle($event)\">\r\n <span class=\"operator-text\">\r\n {{ getLogicalOperatorText(group.groupLogicalOperator || 'and') }}\r\n </span>\r\n <div class=\"connector-arrow\"></div>\r\n </button>\r\n\r\n <!-- Popover -->\r\n <p-popover #opPopover appendTo=\"body\">\r\n <div class=\"w-12rem\">\r\n <p-listbox\r\n [options]=\"logicalOperators\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [(ngModel)]=\"group.groupLogicalOperator\"\r\n (onChange)=\" opPopover.toggle($event)\"\r\n [style]=\"{ width: '100%' }\">\r\n </p-listbox>\r\n </div>\r\n </p-popover>\r\n</div>\r\n\r\n <div class=\"group-container mb-6 p-1 bg-white rounded-lg border-l-4 border-t-1 border-b-1 border-r-1 shadow-sm transition-all duration-300 relative\"\r\n [class.border-blue-500]=\"group.logicalOperator === 'and'\"\r\n [class.border-or-500]=\"group.logicalOperator === 'or'\"\r\n [class.pulse]=\"groupIndex === 0\">\r\n\r\n <!-- Rounded Button for \"\u0631\u0628\u0637 \u0627\u0644\u0634\u0631\u0648\u0637\" on left border -->\r\n<div\r\n class=\"absolute -left-4 top-1/2 transform -translate-y-1/2 z-10 transition-all duration-300\"\r\n\r\n>\r\n\r\n\r\n\r\n<!-- Logical Operator Popover Button -->\r\n<p-button\r\n [severity]=\"group.logicalOperator === 'and' ? 'success' : 'info'\"\r\n [styleClass]=\"group.logicalOperator === 'and'\r\n ? 'p-button-icon-only p-button-rounded p-button-sm logical-and'\r\n : 'p-button-icon-only p-button-rounded p-button-sm logical-or'\"\r\n (click)=\"opPopover.toggle($event)\">\r\n {{group.logicalOperator === 'and' ? ('and' | translate) : ('or' | translate)}}\r\n</p-button>\r\n\r\n<!-- Popover for Operator Selection -->\r\n<p-popover #opPopover appendTo=\"body\">\r\n <div class=\"w-10rem\">\r\n <p-listbox\r\n [options]=\"[\r\n { label: 'and' | translate, value: 'and' },\r\n { label: 'or' | translate, value: 'or' }\r\n ]\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [(ngModel)]=\"group.logicalOperator\"\r\n (onChange)=\"updateValue(); opPopover.toggle($event)\"\r\n [style]=\"{ width: '100%' }\">\r\n <ng-template let-item pTemplate=\"item\">\r\n {{ item.label }}\r\n </ng-template>\r\n </p-listbox>\r\n </div>\r\n</p-popover>\r\n\r\n</div>\r\n\r\n\r\n\r\n <!-- Group Header -->\r\n <div class=\"group-header-wrapper mb-4 pl-8\">\r\n <div class=\"group-header flex justify-between items-center\">\r\n\r\n <div class=\"flex gap-1\">\r\n <button\r\n pButton\r\n icon=\"pi pi-plus\"\r\n type=\"button\"\r\n class=\"p-button-success p-button p-button-sm !p-1.5 rounded-md transition-colors\"\r\n (click)=\"addCondition(groupIndex)\"\r\n pTooltip=\"{{ 'ADD_CONDITION' | translate }}\"\r\n tooltipPosition=\"top\">\r\n </button>\r\n <button\r\n *ngIf=\"groups.length > 1\"\r\n pButton\r\n icon=\"pi pi-times\"\r\n type=\"button\"\r\n class=\"p-button-danger p-button p-button-sm !p-1.5 rounded-md transition-colors\"\r\n (click)=\"removeGroup(groupIndex)\"\r\n pTooltip=\"{{ 'DELETE_GROUP' | translate }}\"\r\n tooltipPosition=\"top\">\r\n </button>\r\n</div>\r\n </div>\r\n </div>\r\n\r\n <!-- Conditions in this group -->\r\n <div *ngFor=\"let condition of group.conditions; let conditionIndex = index\"\r\n class=\"p-1 bg-gray-50 \">\r\n <div class=\"grid grid-cols-1 gap-1 items-center\">\r\n <!-- Field Selector -->\r\n <div class=\"md:col-span-2\">\r\n <div class=\"flex w-full rounded-md overflow-hidden border border-gray-300 bg-white shadow-sm\">\r\n <!-- Field Selector Button -->\r\n <div style=\"align-items: center;display: flex; min-width: 130px;max-width: 130px;\" class=\"flex-shrink-0 \">\r\n <button\r\n type=\"button\"\r\n style=\"height: -webkit-fill-available;\"\r\n class=\"flex items-center justify-between w-full px-3 py-2 text-sm bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:z-10\"\r\n (click)=\"overlay.toggle($event)\">\r\n <span style=\"\r\n display: inline-block;\r\n max-width: 100px;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n \">{{ getFieldLabel(condition.field) || ('SELECT_FIELD' | translate) }}</span>\r\n <i class=\"pi pi-chevron-down mr-2 ml-2 text-xs\"></i>\r\n </button>\r\n\r\n <!-- Overlay Content -->\r\n <p-popover #overlay appendTo=\"body\">\r\n <div class=\"w-60\">\r\n <p-listbox\r\n tabindex=\"0\"\r\n [options]=\"props['fields']\"\r\n optionLabel=\"label\"\r\n optionValue=\"key\"\r\n [(ngModel)]=\"condition.field\"\r\n (onChange)=\"onFieldChange(condition, groupIndex, conditionIndex); overlay.toggle($event)\"\r\n [style]=\"{ width: '100%' }\">\r\n </p-listbox>\r\n </div>\r\n </p-popover>\r\n </div>\r\n <!-- Operator Button -->\r\n <div style=\"align-items: center;display: flex;\" class=\"flex-shrink-0 border-l border-r border-gray-300\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center justify-center w-full px-3 py-2 text-sm bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:z-10\"\r\n style=\"height: -webkit-fill-available;\"\r\n pTooltip=\"{{ getOperatorLabel(condition.operator).label }}\"\r\n (click)=\"opPopover.toggle($event)\">\r\n <i [class]=\"getOperatorLabel(condition.operator).icon\"></i>\r\n </button>\r\n\r\n <p-popover #opPopover appendTo=\"body\">\r\n <div class=\"w-60\">\r\n <p-listbox\r\n [options]=\"condition.operatorsForField\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [(ngModel)]=\"condition.operator\"\r\n (onChange)=\"opPopover.toggle($event)\"\r\n [style]=\"{ width: '100%' }\">\r\n </p-listbox>\r\n </div>\r\n </p-popover>\r\n </div>\r\n <!-- Form Field -->\r\n <div style=\"background: #f9fafb\" class=\"flex-grow p-1\">\r\n <formly-form\r\n [form]=\"form\"\r\n [fields]=\"[condition.valueFieldConfig]\"\r\n [model]=\"model\">\r\n </formly-form>\r\n </div>\r\n\r\n\r\n\r\n <!-- Delete Button -->\r\n <div style=\"align-items: center;display: flex;\" class=\"flex-shrink-0\">\r\n <button\r\n style=\"height: -webkit-fill-available;\"\r\n class=\"flex items-center justify-center w-full px-3 py-2 text-sm bg-red-50 text-red-600 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-red-500 focus:z-10\"\r\n type=\"button\"\r\n (click)=\"removeCondition(groupIndex, conditionIndex)\"\r\n pTooltip=\"\u062D\u0630\u0641 \u0627\u0644\u0634\u0631\u0637\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-times\"></i>\r\n </button>\r\n </div>\r\n</div>\r\n </div>\r\n\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Group Footer -->\r\n <div class=\"group-footer flex justify-between items-center mt-3 pt-3 border-t border-gray-200 \">\r\n <small class=\"text-gray-500\">\r\n {{ group.conditions.length }} \u0634\u0631\u0637 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u0645\u062C\u0645\u0648\u0639\u0629\r\n </small>\r\n <small class=\"text-gray-500\">\r\n <span *ngIf=\"groupIndex > 0\">\r\n \u0645\u0631\u062A\u0628\u0637 \u0645\u0639 \u0627\u0644\u0633\u0627\u0628\u0642\u0629 \u0628\u0640 <strong [class.text-blue-600]=\"group.groupLogicalOperator === 'and'\"\r\n [class.text-amber-600]=\"group.groupLogicalOperator === 'or'\">\r\n {{ group.groupLogicalOperator === 'and' ? '\u0648' : '\u0623\u0648' }}\r\n </strong>\r\n </span>\r\n </small>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Actions -->\r\n<div class=\"flex flex-wrap gap-3 mt-6\">\r\n <button\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-plus\"\r\n [label]=\"'GROUP' | translate\"\r\n class=\"p-button-outlined p-button-sm !bg-white !border !border-blue-500 !text-blue-600 hover:!bg-blue-50 transition-colors flex items-center gap-2\"\r\n (click)=\"addGroup();\">\r\n </button>\r\n\r\n <button\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-plus\"\r\n [label]=\"'CONDITION' | translate\"\r\n class=\"p-button-outlined p-button-sm !bg-white !border !border-blue-500 !text-blue-600 hover:!bg-blue-50 transition-colors flex items-center gap-2\"\r\n (click)=\"addConditionToLastGroup()\">\r\n </button>\r\n\r\n <button\r\n *ngIf=\"groups.length > 0\"\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-trash\"\r\n [label]=\"'CLEAR' | translate\"\r\n class=\"p-button-danger p-button-outlined p-button-sm !bg-white !border !border-red-500 !text-red-600 hover:!bg-red-50 transition-colors flex items-center gap-2\"\r\n (click)=\"clearAll()\">\r\n </button>\r\n</div>\r\n\r\n<!-- Empty State -->\r\n<div *ngIf=\"groups.length === 0\" class=\"empty-state text-center p-6 border-2 border-dashed border-gray-300 rounded-xl bg-gray-50\">\r\n <i class=\"pi pi-search text-4xl text-gray-400 mb-3\"></i>\r\n <p class=\"text-gray-500 mb-4\">{{ 'NO_SEARCH_CONDITIONS' | translate }}</p>\r\n <button\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-plus\"\r\n [label]=\"'ADD_SEARCH_CONDITION' | translate\"\r\n class=\"p-button-outlined !px-4 !py-2\"\r\n (click)=\"addGroup()\">\r\n </button>\r\n</div>\r\n</div>\r\n", styles: [".vertical-connector{position:relative;display:flex;justify-content:center;margin-bottom:1rem}.vertical-line{position:absolute;top:-1rem;height:1rem;width:2px;background-color:#d1d5db}.group-logical-operator-box{position:relative;padding:.25rem .75rem;border-radius:.375rem;font-weight:500;font-size:.75rem;z-index:10;transition:all .3s ease}.group-logical-operator-box.and{background-color:#dbeafe;color:#1d4ed8;border:1px solid #93c5fd}.group-logical-operator-box.or{background-color:#fef3c7;color:#92400e;border:1px solid #fcd34d}.connector-arrow{position:absolute;top:100%;left:50%;transform:translate(-50%);width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent}.group-logical-operator-box.and .connector-arrow{border-top:6px solid #93c5fd}.group-logical-operator-box.or .connector-arrow{border-top:6px solid #fcd34d}.border-blue-500{border-color:#93c5fd}.border-or-500{border-color:#fcd34d}.border-t-1{border-top-width:1px}.border-b-1{border-bottom-width:1px}.border-r-1{border-right-width:1px}.p-popover-content{padding:5px!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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: ReactiveFormsModule }, { kind: "ngmodule", type: SelectModule }, { kind: "directive", type: i2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: InputTextModule }, { 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: "component", type: i4.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "ngmodule", type: CheckboxModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "ngmodule", type: MenuModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: InputGroupModule }, { kind: "ngmodule", type: InputGroupAddonModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i6$2.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ListboxModule }, { kind: "component", type: i7$1.Listbox, selector: "p-listbox, p-listBox, p-list-box", inputs: ["id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "ariaLabel", "selectOnFocus", "searchLocale", "focusOnHover", "filterMessage", "filterFields", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "scrollHeight", "tabindex", "multiple", "styleClass", "listStyle", "listStyleClass", "readonly", "checkbox", "filter", "filterBy", "filterMatchMode", "filterLocale", "metaKeySelection", "dataKey", "showToggleAll", "optionLabel", "optionValue", "optionGroupChildren", "optionGroupLabel", "optionDisabled", "ariaFilterLabel", "filterPlaceHolder", "emptyFilterMessage", "emptyMessage", "group", "options", "filterValue", "selectAll", "striped", "highlightOnSelect", "checkmark", "dragdrop", "fluid"], outputs: ["onChange", "onClick", "onDblClick", "onFilter", "onFocus", "onBlur", "onSelectAllChange", "onLazyLoad", "onDrop"] }, { kind: "pipe", type: i10.TranslatePipe, name: "translate" }] });
1256
1441
  }
1257
1442
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderComponent, decorators: [{
1258
1443
  type: Component,
@@ -1322,7 +1507,7 @@ class TabTypeComponent extends FieldType {
1322
1507
  <i *ngIf="tab.props['leftIcon']" [class]="tab.props['leftIcon']" class="text-sm"></i>
1323
1508
 
1324
1509
  <!-- Label -->
1325
- <span>{{ (tab.props['label'] ?? '') | translate }}</span>
1510
+ <span>{{ (tab.props['tabLabel'] ?? '') | translate }}</span>
1326
1511
 
1327
1512
  <!-- Badge -->
1328
1513
 
@@ -1358,7 +1543,7 @@ class TabTypeComponent extends FieldType {
1358
1543
  </p-tabpanel>
1359
1544
  </p-tabpanels>
1360
1545
  </p-tabs>
1361
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i2$1.Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: i2$1.TabPanels, selector: "p-tabpanels" }, { kind: "component", type: i2$1.TabPanel, selector: "p-tabpanel", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: i2$1.TabList, selector: "p-tablist" }, { kind: "component", type: i2$1.Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i9.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "component", type: FormlyField, selector: "formly-field", inputs: ["field"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i10.TranslatePipe, name: "translate" }] });
1546
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i2$2.Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: i2$2.TabPanels, selector: "p-tabpanels" }, { kind: "component", type: i2$2.TabPanel, selector: "p-tabpanel", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: i2$2.TabList, selector: "p-tablist" }, { kind: "component", type: i2$2.Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i9.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "component", type: FormlyField, selector: "formly-field", inputs: ["field"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i10.TranslatePipe, name: "translate" }] });
1362
1547
  }
1363
1548
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: TabTypeComponent, decorators: [{
1364
1549
  type: Component,
@@ -1383,7 +1568,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
1383
1568
  <i *ngIf="tab.props['leftIcon']" [class]="tab.props['leftIcon']" class="text-sm"></i>
1384
1569
 
1385
1570
  <!-- Label -->
1386
- <span>{{ (tab.props['label'] ?? '') | translate }}</span>
1571
+ <span>{{ (tab.props['tabLabel'] ?? '') | translate }}</span>
1387
1572
 
1388
1573
  <!-- Badge -->
1389
1574
 
@@ -1425,6 +1610,999 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
1425
1610
  }]
1426
1611
  }] });
1427
1612
 
1613
+ // columns-builder.component.ts
1614
+ class ColumnsBuilderComponent extends FieldType {
1615
+ dragStartIndex = -1;
1616
+ _columnsForm;
1617
+ aggregateFunctions = [
1618
+ { label: 'Sum', value: 'sum' },
1619
+ { label: 'Average', value: 'avg' },
1620
+ { label: 'Count', value: 'count' },
1621
+ { label: 'Minimum', value: 'min' },
1622
+ { label: 'Maximum', value: 'max' }
1623
+ ];
1624
+ get columns() {
1625
+ return this.props['fields'] || [];
1626
+ }
1627
+ get columnsForm() {
1628
+ return this._columnsForm;
1629
+ }
1630
+ get visibleColumnsCount() {
1631
+ return this.columns.filter(col => this.isColumnVisible(col.key)).length;
1632
+ }
1633
+ get numericColumnsCount() {
1634
+ return this.columns.filter(col => col.isNumeric).length;
1635
+ }
1636
+ get aggregatedColumnsCount() {
1637
+ return this.columns.filter(col => this.hasAggregateFunction(col.key)).length;
1638
+ }
1639
+ ngOnInit() {
1640
+ this.initializeForm();
1641
+ this.setupValueChanges();
1642
+ }
1643
+ // Form Methods
1644
+ initializeForm() {
1645
+ const parentForm = this.form;
1646
+ const fieldKey = this.field.key;
1647
+ if (!fieldKey)
1648
+ return;
1649
+ // Get or create the FormGroup in the parent form
1650
+ let formGroup = parentForm.get(fieldKey);
1651
+ if (!formGroup || !(formGroup instanceof FormGroup)) {
1652
+ // Create new FormGroup
1653
+ formGroup = new FormGroup({});
1654
+ parentForm.setControl(fieldKey, formGroup);
1655
+ // Initialize with existing model data
1656
+ const initialModel = this.model[fieldKey] || {};
1657
+ this.columns.forEach(column => {
1658
+ const columnConfig = initialModel[column.key] || {};
1659
+ const columnGroup = new FormGroup({
1660
+ isVisible: new FormControl(columnConfig.isVisible ?? column.defaultVisible),
1661
+ displayName: new FormControl(columnConfig.displayName ?? column.label),
1662
+ aggregateFunction: new FormControl(columnConfig.aggregateFunction ?? ''),
1663
+ order: new FormControl(columnConfig.order ?? 0)
1664
+ });
1665
+ formGroup.addControl(column.key, columnGroup);
1666
+ });
1667
+ }
1668
+ // Store the FormGroup reference
1669
+ this._columnsForm = formGroup;
1670
+ // Initialize the model with current form values
1671
+ this.updateModel();
1672
+ }
1673
+ setupValueChanges() {
1674
+ // Listen to form changes and update the model
1675
+ this.columnsForm.valueChanges.subscribe(() => {
1676
+ this.updateModel();
1677
+ });
1678
+ }
1679
+ updateModel() {
1680
+ const fieldKey = this.field.key;
1681
+ if (fieldKey) {
1682
+ // Update the model with current form values
1683
+ this.model[fieldKey] = this.columnsForm.value;
1684
+ // Trigger form control update to notify Formly
1685
+ this.formControl.setValue(this.columnsForm.value);
1686
+ }
1687
+ }
1688
+ // Called when form controls change
1689
+ onFormChange() {
1690
+ this.updateModel();
1691
+ }
1692
+ // Drag & Drop Methods
1693
+ onDragStart(event, index) {
1694
+ this.dragStartIndex = index;
1695
+ event.dataTransfer?.setData('text/plain', index.toString());
1696
+ }
1697
+ onDragOver(event) {
1698
+ event.preventDefault();
1699
+ }
1700
+ onDrop(event, dropIndex) {
1701
+ event.preventDefault();
1702
+ const dragIndex = this.dragStartIndex;
1703
+ if (dragIndex !== -1 && dragIndex !== dropIndex) {
1704
+ // Reorder the columns array
1705
+ const movedItem = this.columns[dragIndex];
1706
+ const newColumns = [...this.columns];
1707
+ newColumns.splice(dragIndex, 1);
1708
+ newColumns.splice(dropIndex, 0, movedItem);
1709
+ // Update the props to trigger change detection
1710
+ this.props['fields'] = newColumns;
1711
+ // Update order in form controls
1712
+ this.updateColumnOrders();
1713
+ // Update model after reordering
1714
+ this.updateModel();
1715
+ }
1716
+ this.removeDragStyles(event);
1717
+ }
1718
+ onDragEnter(event) {
1719
+ event.currentTarget.classList.add('border-blue-500', 'bg-blue-50');
1720
+ }
1721
+ onDragLeave(event) {
1722
+ this.removeDragStyles(event);
1723
+ }
1724
+ removeDragStyles(event) {
1725
+ event.currentTarget.classList.remove('border-blue-500', 'bg-blue-50');
1726
+ }
1727
+ updateColumnOrders() {
1728
+ // Store the order in form controls
1729
+ this.columns.forEach((column, index) => {
1730
+ const orderControl = this.getColumnControl(column.key, 'order');
1731
+ if (!orderControl) {
1732
+ const columnGroup = this.columnsForm.get(column.key);
1733
+ if (columnGroup && !columnGroup.get('order')) {
1734
+ columnGroup.addControl('order', new FormControl(index));
1735
+ }
1736
+ }
1737
+ else {
1738
+ orderControl.setValue(index);
1739
+ }
1740
+ });
1741
+ }
1742
+ getColumnControl(columnKey, controlName) {
1743
+ const columnGroup = this.columnsForm.get(columnKey);
1744
+ if (!columnGroup) {
1745
+ const fallbackControl = new FormControl(this.getDefaultValue(controlName));
1746
+ return fallbackControl;
1747
+ }
1748
+ const control = columnGroup.get(controlName);
1749
+ if (!control) {
1750
+ const fallbackControl = new FormControl(this.getDefaultValue(controlName));
1751
+ columnGroup.addControl(controlName, fallbackControl);
1752
+ return fallbackControl;
1753
+ }
1754
+ return control;
1755
+ }
1756
+ getDefaultValue(controlName) {
1757
+ switch (controlName) {
1758
+ case 'isVisible': return true;
1759
+ case 'displayName': return '';
1760
+ case 'aggregateFunction': return '';
1761
+ case 'order': return 0;
1762
+ default: return null;
1763
+ }
1764
+ }
1765
+ isColumnVisible(columnKey) {
1766
+ const control = this.getColumnControl(columnKey, 'isVisible');
1767
+ return control?.value === true;
1768
+ }
1769
+ hasAggregateFunction(columnKey) {
1770
+ const control = this.getColumnControl(columnKey, 'aggregateFunction');
1771
+ return !!control?.value;
1772
+ }
1773
+ getAggregateFunctionValue(columnKey) {
1774
+ const control = this.getColumnControl(columnKey, 'aggregateFunction');
1775
+ return control?.value || '';
1776
+ }
1777
+ toggleAll(visible) {
1778
+ this.columns.forEach(column => {
1779
+ const visibleControl = this.getColumnControl(column.key, 'isVisible');
1780
+ if (visibleControl) {
1781
+ visibleControl.setValue(visible);
1782
+ }
1783
+ });
1784
+ this.updateModel();
1785
+ }
1786
+ resetToDefault() {
1787
+ this.columns.forEach((column, index) => {
1788
+ const columnGroup = this.columnsForm.get(column.key);
1789
+ if (columnGroup) {
1790
+ columnGroup.get('isVisible')?.setValue(column.defaultVisible);
1791
+ columnGroup.get('displayName')?.setValue(column.label);
1792
+ columnGroup.get('aggregateFunction')?.setValue('');
1793
+ columnGroup.get('order')?.setValue(index);
1794
+ }
1795
+ });
1796
+ this.updateModel();
1797
+ }
1798
+ getAggregateLabel(functionName) {
1799
+ const func = this.aggregateFunctions.find(f => f.value === functionName);
1800
+ return func ? func.label : functionName;
1801
+ }
1802
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: ColumnsBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1803
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: ColumnsBuilderComponent, isStandalone: true, selector: "formly-columns-builder", usesInheritance: true, ngImport: i0, template: `
1804
+ <div>
1805
+ <!-- Header -->
1806
+ <div class="mb-6">
1807
+ <h5 class="text-lg font-semibold text-gray-900 mb-2">{{ props.label || 'Columns Management' }}</h5>
1808
+ <p class="text-gray-600 text-sm">{{ props.description || 'Select and configure columns to display' }}</p>
1809
+ </div>
1810
+
1811
+ <!-- Quick Actions -->
1812
+ <div class="flex flex-wrap gap-2 mb-6">
1813
+ <button
1814
+ type="button"
1815
+ (click)="toggleAll(true)"
1816
+ class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
1817
+ >
1818
+ <i class="pi pi-eye"></i>
1819
+ Show All
1820
+ </button>
1821
+ <button
1822
+ type="button"
1823
+ (click)="toggleAll(false)"
1824
+ class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
1825
+ >
1826
+ <i class="pi pi-eye-slash"></i>
1827
+ Hide All
1828
+ </button>
1829
+ <button
1830
+ type="button"
1831
+ (click)="resetToDefault()"
1832
+ class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
1833
+ >
1834
+ <i class="pi pi-refresh"></i>
1835
+ Reset to Default
1836
+ </button>
1837
+ </div>
1838
+
1839
+ <!-- Drag Info -->
1840
+ <div class="mb-4 p-2 bg-blue-50 border border-blue-200 rounded text-sm text-blue-700">
1841
+ <i class="pi pi-info-circle mr-2"></i>
1842
+ Drag the handle to reorder columns
1843
+ </div>
1844
+
1845
+ <!-- Columns List -->
1846
+ <div class="space-y-3">
1847
+ <div *ngIf="columns.length === 0" class="text-center py-8 px-4 border-2 border-dashed border-gray-300 rounded-lg bg-gray-50">
1848
+ <i class="pi pi-table text-4xl text-gray-400 mb-3"></i>
1849
+ <p class="text-gray-500">No columns available</p>
1850
+ </div>
1851
+
1852
+ <div *ngFor="let column of columns; let i = index"
1853
+ class="bg-gray-50 rounded-lg border border-gray-200 p-1 transition-colors hover:bg-gray-100"
1854
+ draggable="true"
1855
+ (dragstart)="onDragStart($event, i)"
1856
+ (dragover)="onDragOver($event)"
1857
+ (drop)="onDrop($event, i)"
1858
+ (dragenter)="onDragEnter($event)"
1859
+ (dragleave)="onDragLeave($event)">
1860
+
1861
+ <div class="grid grid-cols-12 gap-4 items-center">
1862
+ <!-- Drag Handle -->
1863
+ <div class="col-span-1 flex justify-center cursor-move text-gray-400 hover:text-gray-600"
1864
+ draggable="true"
1865
+ (dragstart)="onDragStart($event, i)">
1866
+ <i class="pi pi-bars"></i>
1867
+ </div>
1868
+
1869
+ <!-- Visibility Toggle -->
1870
+ <div class="col-span-1 flex justify-center">
1871
+ <p-checkbox
1872
+ [binary]="true"
1873
+ [formControl]="getColumnControl(column.key, 'isVisible')"
1874
+ [inputId]="'visible_' + column.key"
1875
+ styleClass="[&>div]:border-gray-300 [&>div]:hover:border-gray-400"
1876
+ (onChange)="onFormChange()"
1877
+ >
1878
+ </p-checkbox>
1879
+ </div>
1880
+
1881
+ <!-- Column Info -->
1882
+ <div class="col-span-12 md:col-span-4">
1883
+ <label [for]="'visible_' + column.key" class="block font-medium text-gray-900 cursor-pointer hover:text-blue-600">
1884
+ {{ column.label }}
1885
+ </label>
1886
+ <p class="text-xs text-gray-500 mt-1">{{ column.key }}</p>
1887
+ </div>
1888
+
1889
+ <!-- Display Name -->
1890
+ <div class="col-span-12 md:col-span-3">
1891
+ <input
1892
+ *ngIf="isColumnVisible(column.key)"
1893
+ type="text"
1894
+ [formControl]="getColumnControl(column.key, 'displayName')"
1895
+ [placeholder]="column.label"
1896
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
1897
+ (blur)="onFormChange()"
1898
+ >
1899
+ </div>
1900
+
1901
+ <!-- Aggregate Function -->
1902
+ <div class="col-span-12 md:col-span-3">
1903
+ <div *ngIf="column.aggregatable && isColumnVisible(column.key)">
1904
+ <p-select
1905
+ [options]="aggregateFunctions"
1906
+ optionLabel="label"
1907
+ optionValue="value"
1908
+ [formControl]="getColumnControl(column.key, 'aggregateFunction')"
1909
+ placeholder="Select aggregate"
1910
+ [showClear]="true"
1911
+ styleClass="w-full"
1912
+ (onChange)="onFormChange()"
1913
+ >
1914
+ </p-select>
1915
+ </div>
1916
+ <span *ngIf="!column.aggregatable && isColumnVisible(column.key)" class="text-xs text-gray-500">
1917
+ No aggregates available
1918
+ </span>
1919
+ </div>
1920
+ </div>
1921
+
1922
+ <!-- Aggregate Info -->
1923
+ <div *ngIf="hasAggregateFunction(column.key) && isColumnVisible(column.key)"
1924
+ class="mt-3 p-3 bg-blue-50 border border-blue-200 rounded-lg">
1925
+ <div class="flex items-center gap-2">
1926
+ <i class="pi pi-info-circle text-blue-500 text-sm"></i>
1927
+ <span class="text-sm text-blue-800">
1928
+ Will calculate <strong>{{ getAggregateLabel(getAggregateFunctionValue(column.key)) }}</strong>
1929
+ for this column in grouped data
1930
+ </span>
1931
+ </div>
1932
+ </div>
1933
+ </div>
1934
+ </div>
1935
+
1936
+ <!-- Summary -->
1937
+ <div class="flex flex-col sm:flex-row justify-between items-center gap-4 mt-6 pt-4 border-t border-gray-200">
1938
+ <span class="text-sm text-gray-600">
1939
+ {{ visibleColumnsCount }} of {{ columns.length }} columns visible
1940
+ </span>
1941
+ <div class="flex gap-4 text-sm">
1942
+ <span class="inline-flex items-center gap-1 text-green-600">
1943
+ <i class="pi pi-chart-line"></i>
1944
+ {{ numericColumnsCount }} numeric
1945
+ </span>
1946
+ <span class="inline-flex items-center gap-1 text-blue-600">
1947
+ <i class="pi pi-calculator"></i>
1948
+ {{ aggregatedColumnsCount }} aggregated
1949
+ </span>
1950
+ </div>
1951
+ </div>
1952
+ </div>
1953
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { 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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: DatePickerModule }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$1.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: RadioButtonModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "ngmodule", type: MenuModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: InputGroupModule }, { kind: "ngmodule", type: InputGroupAddonModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ListboxModule }] });
1954
+ }
1955
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: ColumnsBuilderComponent, decorators: [{
1956
+ type: Component,
1957
+ args: [{
1958
+ selector: 'formly-columns-builder',
1959
+ template: `
1960
+ <div>
1961
+ <!-- Header -->
1962
+ <div class="mb-6">
1963
+ <h5 class="text-lg font-semibold text-gray-900 mb-2">{{ props.label || 'Columns Management' }}</h5>
1964
+ <p class="text-gray-600 text-sm">{{ props.description || 'Select and configure columns to display' }}</p>
1965
+ </div>
1966
+
1967
+ <!-- Quick Actions -->
1968
+ <div class="flex flex-wrap gap-2 mb-6">
1969
+ <button
1970
+ type="button"
1971
+ (click)="toggleAll(true)"
1972
+ class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
1973
+ >
1974
+ <i class="pi pi-eye"></i>
1975
+ Show All
1976
+ </button>
1977
+ <button
1978
+ type="button"
1979
+ (click)="toggleAll(false)"
1980
+ class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
1981
+ >
1982
+ <i class="pi pi-eye-slash"></i>
1983
+ Hide All
1984
+ </button>
1985
+ <button
1986
+ type="button"
1987
+ (click)="resetToDefault()"
1988
+ class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
1989
+ >
1990
+ <i class="pi pi-refresh"></i>
1991
+ Reset to Default
1992
+ </button>
1993
+ </div>
1994
+
1995
+ <!-- Drag Info -->
1996
+ <div class="mb-4 p-2 bg-blue-50 border border-blue-200 rounded text-sm text-blue-700">
1997
+ <i class="pi pi-info-circle mr-2"></i>
1998
+ Drag the handle to reorder columns
1999
+ </div>
2000
+
2001
+ <!-- Columns List -->
2002
+ <div class="space-y-3">
2003
+ <div *ngIf="columns.length === 0" class="text-center py-8 px-4 border-2 border-dashed border-gray-300 rounded-lg bg-gray-50">
2004
+ <i class="pi pi-table text-4xl text-gray-400 mb-3"></i>
2005
+ <p class="text-gray-500">No columns available</p>
2006
+ </div>
2007
+
2008
+ <div *ngFor="let column of columns; let i = index"
2009
+ class="bg-gray-50 rounded-lg border border-gray-200 p-1 transition-colors hover:bg-gray-100"
2010
+ draggable="true"
2011
+ (dragstart)="onDragStart($event, i)"
2012
+ (dragover)="onDragOver($event)"
2013
+ (drop)="onDrop($event, i)"
2014
+ (dragenter)="onDragEnter($event)"
2015
+ (dragleave)="onDragLeave($event)">
2016
+
2017
+ <div class="grid grid-cols-12 gap-4 items-center">
2018
+ <!-- Drag Handle -->
2019
+ <div class="col-span-1 flex justify-center cursor-move text-gray-400 hover:text-gray-600"
2020
+ draggable="true"
2021
+ (dragstart)="onDragStart($event, i)">
2022
+ <i class="pi pi-bars"></i>
2023
+ </div>
2024
+
2025
+ <!-- Visibility Toggle -->
2026
+ <div class="col-span-1 flex justify-center">
2027
+ <p-checkbox
2028
+ [binary]="true"
2029
+ [formControl]="getColumnControl(column.key, 'isVisible')"
2030
+ [inputId]="'visible_' + column.key"
2031
+ styleClass="[&>div]:border-gray-300 [&>div]:hover:border-gray-400"
2032
+ (onChange)="onFormChange()"
2033
+ >
2034
+ </p-checkbox>
2035
+ </div>
2036
+
2037
+ <!-- Column Info -->
2038
+ <div class="col-span-12 md:col-span-4">
2039
+ <label [for]="'visible_' + column.key" class="block font-medium text-gray-900 cursor-pointer hover:text-blue-600">
2040
+ {{ column.label }}
2041
+ </label>
2042
+ <p class="text-xs text-gray-500 mt-1">{{ column.key }}</p>
2043
+ </div>
2044
+
2045
+ <!-- Display Name -->
2046
+ <div class="col-span-12 md:col-span-3">
2047
+ <input
2048
+ *ngIf="isColumnVisible(column.key)"
2049
+ type="text"
2050
+ [formControl]="getColumnControl(column.key, 'displayName')"
2051
+ [placeholder]="column.label"
2052
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
2053
+ (blur)="onFormChange()"
2054
+ >
2055
+ </div>
2056
+
2057
+ <!-- Aggregate Function -->
2058
+ <div class="col-span-12 md:col-span-3">
2059
+ <div *ngIf="column.aggregatable && isColumnVisible(column.key)">
2060
+ <p-select
2061
+ [options]="aggregateFunctions"
2062
+ optionLabel="label"
2063
+ optionValue="value"
2064
+ [formControl]="getColumnControl(column.key, 'aggregateFunction')"
2065
+ placeholder="Select aggregate"
2066
+ [showClear]="true"
2067
+ styleClass="w-full"
2068
+ (onChange)="onFormChange()"
2069
+ >
2070
+ </p-select>
2071
+ </div>
2072
+ <span *ngIf="!column.aggregatable && isColumnVisible(column.key)" class="text-xs text-gray-500">
2073
+ No aggregates available
2074
+ </span>
2075
+ </div>
2076
+ </div>
2077
+
2078
+ <!-- Aggregate Info -->
2079
+ <div *ngIf="hasAggregateFunction(column.key) && isColumnVisible(column.key)"
2080
+ class="mt-3 p-3 bg-blue-50 border border-blue-200 rounded-lg">
2081
+ <div class="flex items-center gap-2">
2082
+ <i class="pi pi-info-circle text-blue-500 text-sm"></i>
2083
+ <span class="text-sm text-blue-800">
2084
+ Will calculate <strong>{{ getAggregateLabel(getAggregateFunctionValue(column.key)) }}</strong>
2085
+ for this column in grouped data
2086
+ </span>
2087
+ </div>
2088
+ </div>
2089
+ </div>
2090
+ </div>
2091
+
2092
+ <!-- Summary -->
2093
+ <div class="flex flex-col sm:flex-row justify-between items-center gap-4 mt-6 pt-4 border-t border-gray-200">
2094
+ <span class="text-sm text-gray-600">
2095
+ {{ visibleColumnsCount }} of {{ columns.length }} columns visible
2096
+ </span>
2097
+ <div class="flex gap-4 text-sm">
2098
+ <span class="inline-flex items-center gap-1 text-green-600">
2099
+ <i class="pi pi-chart-line"></i>
2100
+ {{ numericColumnsCount }} numeric
2101
+ </span>
2102
+ <span class="inline-flex items-center gap-1 text-blue-600">
2103
+ <i class="pi pi-calculator"></i>
2104
+ {{ aggregatedColumnsCount }} aggregated
2105
+ </span>
2106
+ </div>
2107
+ </div>
2108
+ </div>
2109
+ `,
2110
+ imports: [
2111
+ CommonModule,
2112
+ FormsModule,
2113
+ ReactiveFormsModule,
2114
+ SelectModule,
2115
+ InputTextModule,
2116
+ ButtonModule,
2117
+ DatePickerModule,
2118
+ CheckboxModule,
2119
+ RadioButtonModule,
2120
+ FormlyForm,
2121
+ TooltipModule,
2122
+ MenuModule,
2123
+ TranslateModule,
2124
+ InputGroupModule,
2125
+ InputGroupAddonModule,
2126
+ PopoverModule,
2127
+ ListboxModule
2128
+ ]
2129
+ }]
2130
+ }] });
2131
+
2132
+ // group-builder.component.ts
2133
+ class GroupBuilderComponent extends FieldType {
2134
+ _groups;
2135
+ dragStartIndex = -1;
2136
+ get groups() {
2137
+ return this._groups;
2138
+ }
2139
+ get availableFields() {
2140
+ return this.props['fields'] || [];
2141
+ }
2142
+ get maxGroups() {
2143
+ return this.props['maxGroups'] || 3;
2144
+ }
2145
+ ngOnInit() {
2146
+ this.initializeFormArray();
2147
+ this.setupValueChanges();
2148
+ }
2149
+ initializeFormArray() {
2150
+ const parentForm = this.form;
2151
+ const fieldKey = this.field.key;
2152
+ if (!fieldKey)
2153
+ return;
2154
+ // Get or create the FormArray in the parent form
2155
+ let formArray = parentForm.get(fieldKey);
2156
+ if (!formArray || !(formArray instanceof FormArray)) {
2157
+ // Create new FormArray
2158
+ formArray = new FormArray([]);
2159
+ parentForm.setControl(fieldKey, formArray);
2160
+ // Initialize with existing model data
2161
+ const initialValue = this.model[fieldKey];
2162
+ if (Array.isArray(initialValue)) {
2163
+ initialValue.forEach(group => {
2164
+ formArray.push(this.createGroupControl(group));
2165
+ });
2166
+ }
2167
+ }
2168
+ // Store the FormArray reference
2169
+ this._groups = formArray;
2170
+ }
2171
+ setupValueChanges() {
2172
+ // Listen to form array changes and update the model
2173
+ this.groups.valueChanges.subscribe(() => {
2174
+ this.updateModel();
2175
+ });
2176
+ }
2177
+ updateModel() {
2178
+ const fieldKey = this.field.key;
2179
+ if (fieldKey) {
2180
+ // Update the model with current form values
2181
+ this.model[fieldKey] = this.groups.value;
2182
+ // Trigger form control update to notify Formly
2183
+ this.formControl.setValue(this.groups.value);
2184
+ // Mark form control as touched/dirty if needed
2185
+ this.formControl.markAsTouched();
2186
+ this.formControl.markAsDirty();
2187
+ }
2188
+ }
2189
+ // Called when form controls change
2190
+ onFormChange() {
2191
+ this.updateModel();
2192
+ }
2193
+ createGroupControl(group) {
2194
+ return new FormGroup({
2195
+ field: new FormControl(group?.field || ''),
2196
+ displayName: new FormControl(group?.displayName || ''),
2197
+ showTotal: new FormControl(group?.showTotal !== false)
2198
+ });
2199
+ }
2200
+ addGroup() {
2201
+ if (this.canAddMoreGroups()) {
2202
+ this.groups.push(this.createGroupControl());
2203
+ this.updateModel();
2204
+ }
2205
+ }
2206
+ removeGroup(index) {
2207
+ this.groups.removeAt(index);
2208
+ this.updateModel();
2209
+ }
2210
+ // Drag & Drop Methods
2211
+ onDragStart(event, index) {
2212
+ this.dragStartIndex = index;
2213
+ event.dataTransfer?.setData('text/plain', index.toString());
2214
+ event.currentTarget.classList.add('opacity-50', 'border-blue-300');
2215
+ }
2216
+ onDragOver(event) {
2217
+ event.preventDefault();
2218
+ }
2219
+ onDrop(event, dropIndex) {
2220
+ event.preventDefault();
2221
+ const dragIndex = this.dragStartIndex;
2222
+ if (dragIndex !== -1 && dragIndex !== dropIndex) {
2223
+ this.moveGroup(dragIndex, dropIndex);
2224
+ }
2225
+ this.removeDragStyles(event);
2226
+ }
2227
+ onDragEnter(event) {
2228
+ event.currentTarget.classList.add('border-blue-500', 'bg-blue-50', 'border-2');
2229
+ }
2230
+ onDragLeave(event) {
2231
+ this.removeDragStyles(event);
2232
+ }
2233
+ onDragEnd(event) {
2234
+ this.removeDragStyles(event);
2235
+ this.dragStartIndex = -1;
2236
+ }
2237
+ removeDragStyles(event) {
2238
+ const element = event.currentTarget;
2239
+ element.classList.remove('border-blue-500', 'bg-blue-50', 'opacity-50', 'border-blue-300', 'border-2');
2240
+ }
2241
+ moveGroup(fromIndex, toIndex) {
2242
+ const movedGroup = this.groups.at(fromIndex);
2243
+ this.groups.removeAt(fromIndex);
2244
+ this.groups.insert(toIndex, movedGroup);
2245
+ this.updateModel();
2246
+ }
2247
+ getGroupControl(index, controlName) {
2248
+ const group = this.groups.at(index);
2249
+ return group.get(controlName);
2250
+ }
2251
+ onFieldChange(index, fieldKey) {
2252
+ const displayNameControl = this.getGroupControl(index, 'displayName');
2253
+ if (!displayNameControl.value && fieldKey) {
2254
+ const field = this.availableFields.find(f => f.key === fieldKey);
2255
+ if (field) {
2256
+ displayNameControl.setValue(field.label);
2257
+ }
2258
+ }
2259
+ this.updateModel();
2260
+ }
2261
+ getFieldLabel(fieldKey) {
2262
+ const field = this.availableFields.find(f => f.key === fieldKey);
2263
+ return field ? field.label : fieldKey;
2264
+ }
2265
+ canAddMoreGroups() {
2266
+ return this.groups.length < this.maxGroups;
2267
+ }
2268
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GroupBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2269
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: GroupBuilderComponent, isStandalone: true, selector: "formly-group-builder", usesInheritance: true, ngImport: i0, template: `
2270
+ <div>
2271
+ <!-- Header -->
2272
+ <div class="mb-6">
2273
+ <h5 class="text-lg font-semibold text-gray-900 mb-2">{{ props.label || 'Data Grouping' }}</h5>
2274
+ <p class="text-gray-600 text-sm">{{ props.description || 'Group data by fields to create hierarchical views' }}</p>
2275
+ </div>
2276
+
2277
+ <!-- Group Configuration -->
2278
+ <div class="space-y-4">
2279
+ <div class="flex items-center justify-between">
2280
+ <span class="font-medium text-gray-700">Group Levels</span>
2281
+ <button
2282
+ type="button"
2283
+ *ngIf="canAddMoreGroups()"
2284
+ (click)="addGroup()"
2285
+ [disabled]="!canAddMoreGroups()"
2286
+ class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-blue-700 bg-blue-50 rounded-lg hover:bg-blue-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
2287
+ >
2288
+ <i class="pi pi-plus"></i>
2289
+ Add Group Level
2290
+ </button>
2291
+ </div>
2292
+
2293
+ <!-- Drag Info -->
2294
+ <div *ngIf="groups.length > 1" class="mb-4 p-2 bg-blue-50 border border-blue-200 rounded text-sm text-blue-700">
2295
+ <i class="pi pi-info-circle mr-2"></i>
2296
+ Drag the handle to reorder group levels
2297
+ </div>
2298
+
2299
+ <!-- Empty State -->
2300
+ <div *ngIf="groups.length === 0" class="text-center py-8 px-4 border-2 border-dashed border-gray-300 rounded-lg bg-gray-50">
2301
+ <i class="pi pi-layer-group text-4xl text-gray-400 mb-3"></i>
2302
+ <p class="text-gray-500 mb-4">No grouping levels defined</p>
2303
+ <button
2304
+ type="button"
2305
+ (click)="addGroup()"
2306
+ class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors"
2307
+ >
2308
+ <i class="pi pi-plus"></i>
2309
+ Add First Group Level
2310
+ </button>
2311
+ </div>
2312
+
2313
+ <!-- Groups List with Drag & Drop -->
2314
+ <div class="space-y-3">
2315
+ <div *ngFor="let group of groups.controls; let i = index"
2316
+ class="bg-gray-50 rounded-lg border border-gray-200 p-4 transition-colors hover:bg-gray-100"
2317
+ draggable="true"
2318
+ (dragstart)="onDragStart($event, i)"
2319
+ (dragover)="onDragOver($event)"
2320
+ (drop)="onDrop($event, i)"
2321
+ (dragenter)="onDragEnter($event)"
2322
+ (dragleave)="onDragLeave($event)"
2323
+ (dragend)="onDragEnd($event)">
2324
+
2325
+ <div class="grid grid-cols-12 gap-4 items-start">
2326
+ <!-- Drag Handle -->
2327
+ <div *ngIf="groups.length > 1"
2328
+ class="col-span-1 flex justify-center cursor-move text-gray-400 hover:text-gray-600 pt-2"
2329
+ draggable="true"
2330
+ (dragstart)="onDragStart($event, i)">
2331
+ <i class="pi pi-bars"></i>
2332
+ </div>
2333
+
2334
+ <!-- Group Content -->
2335
+ <div class="col-span-11">
2336
+ <div class="flex items-center justify-between mb-3">
2337
+ <div class="flex items-center gap-2">
2338
+ <i class="pi pi-sort-alt text-gray-500"></i>
2339
+ <span class="font-medium text-gray-700">Group Level {{ i + 1 }}</span>
2340
+ </div>
2341
+ <div class="flex items-center gap-1">
2342
+ <button
2343
+ type="button"
2344
+ (click)="removeGroup(i)"
2345
+ class="p-1 text-red-500 hover:text-red-700 hover:bg-red-50 rounded transition-colors"
2346
+ [pTooltip]="'Remove'"
2347
+ >
2348
+ <i class="pi pi-times"></i>
2349
+ </button>
2350
+ </div>
2351
+ </div>
2352
+
2353
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
2354
+ <!-- Field Selection -->
2355
+ <div>
2356
+ <label class="block text-sm font-medium text-gray-700 mb-2">Field</label>
2357
+ <p-select
2358
+ [options]="availableFields"
2359
+ optionLabel="label"
2360
+ optionValue="key"
2361
+ [formControl]="getGroupControl(i, 'field')"
2362
+ placeholder="Select field"
2363
+ [showClear]="true"
2364
+ (onChange)="onFieldChange(i, $event.value)"
2365
+ styleClass="w-full"
2366
+ >
2367
+ <ng-template pTemplate="selectedItem">
2368
+ <span *ngIf="getGroupControl(i, 'field').value" class="text-gray-900">
2369
+ {{ getFieldLabel(getGroupControl(i, 'field').value) }}
2370
+ </span>
2371
+ <span *ngIf="!getGroupControl(i, 'field').value" class="text-gray-500">Select field</span>
2372
+ </ng-template>
2373
+ </p-select>
2374
+ </div>
2375
+
2376
+ <!-- Display Name -->
2377
+ <div>
2378
+ <label class="block text-sm font-medium text-gray-700 mb-2">Display Name</label>
2379
+ <input
2380
+ type="text"
2381
+ pInputText
2382
+ [formControl]="getGroupControl(i, 'displayName')"
2383
+ placeholder="Display name (optional)"
2384
+ class="w-full text-gray-900"
2385
+ (blur)="onFormChange()"
2386
+ >
2387
+ </div>
2388
+ </div>
2389
+
2390
+ <!-- Show Total Toggle -->
2391
+ <div class="flex items-center gap-2 mt-3">
2392
+ <p-checkbox
2393
+ [binary]="true"
2394
+ [formControl]="getGroupControl(i, 'showTotal')"
2395
+ [inputId]="'showTotal' + i"
2396
+ styleClass="[&>div]:border-gray-300 [&>div]:hover:border-gray-400"
2397
+ (onChange)="onFormChange()"
2398
+ >
2399
+ </p-checkbox>
2400
+ <label [for]="'showTotal' + i" class="text-sm text-gray-700 cursor-pointer hover:text-gray-900">
2401
+ Show total for this group level
2402
+ </label>
2403
+ </div>
2404
+ </div>
2405
+ </div>
2406
+ </div>
2407
+ </div>
2408
+ </div>
2409
+
2410
+ <!-- Grouping Tips -->
2411
+ <div *ngIf="groups.length > 0" class="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
2412
+ <div class="flex items-center gap-3">
2413
+ <i class="pi pi-info-circle text-blue-500"></i>
2414
+ <div>
2415
+ <p class="text-sm text-blue-800">
2416
+ Data will be grouped hierarchically in the specified order.
2417
+ {{ groups.length }} level{{ groups.length > 1 ? 's' : '' }} defined.
2418
+ </p>
2419
+ </div>
2420
+ </div>
2421
+ </div>
2422
+ </div>
2423
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { 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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: i2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: DatePickerModule }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$1.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: RadioButtonModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "ngmodule", type: MenuModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: InputGroupModule }, { kind: "ngmodule", type: InputGroupAddonModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ListboxModule }] });
2424
+ }
2425
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GroupBuilderComponent, decorators: [{
2426
+ type: Component,
2427
+ args: [{
2428
+ selector: 'formly-group-builder',
2429
+ template: `
2430
+ <div>
2431
+ <!-- Header -->
2432
+ <div class="mb-6">
2433
+ <h5 class="text-lg font-semibold text-gray-900 mb-2">{{ props.label || 'Data Grouping' }}</h5>
2434
+ <p class="text-gray-600 text-sm">{{ props.description || 'Group data by fields to create hierarchical views' }}</p>
2435
+ </div>
2436
+
2437
+ <!-- Group Configuration -->
2438
+ <div class="space-y-4">
2439
+ <div class="flex items-center justify-between">
2440
+ <span class="font-medium text-gray-700">Group Levels</span>
2441
+ <button
2442
+ type="button"
2443
+ *ngIf="canAddMoreGroups()"
2444
+ (click)="addGroup()"
2445
+ [disabled]="!canAddMoreGroups()"
2446
+ class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-blue-700 bg-blue-50 rounded-lg hover:bg-blue-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
2447
+ >
2448
+ <i class="pi pi-plus"></i>
2449
+ Add Group Level
2450
+ </button>
2451
+ </div>
2452
+
2453
+ <!-- Drag Info -->
2454
+ <div *ngIf="groups.length > 1" class="mb-4 p-2 bg-blue-50 border border-blue-200 rounded text-sm text-blue-700">
2455
+ <i class="pi pi-info-circle mr-2"></i>
2456
+ Drag the handle to reorder group levels
2457
+ </div>
2458
+
2459
+ <!-- Empty State -->
2460
+ <div *ngIf="groups.length === 0" class="text-center py-8 px-4 border-2 border-dashed border-gray-300 rounded-lg bg-gray-50">
2461
+ <i class="pi pi-layer-group text-4xl text-gray-400 mb-3"></i>
2462
+ <p class="text-gray-500 mb-4">No grouping levels defined</p>
2463
+ <button
2464
+ type="button"
2465
+ (click)="addGroup()"
2466
+ class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors"
2467
+ >
2468
+ <i class="pi pi-plus"></i>
2469
+ Add First Group Level
2470
+ </button>
2471
+ </div>
2472
+
2473
+ <!-- Groups List with Drag & Drop -->
2474
+ <div class="space-y-3">
2475
+ <div *ngFor="let group of groups.controls; let i = index"
2476
+ class="bg-gray-50 rounded-lg border border-gray-200 p-4 transition-colors hover:bg-gray-100"
2477
+ draggable="true"
2478
+ (dragstart)="onDragStart($event, i)"
2479
+ (dragover)="onDragOver($event)"
2480
+ (drop)="onDrop($event, i)"
2481
+ (dragenter)="onDragEnter($event)"
2482
+ (dragleave)="onDragLeave($event)"
2483
+ (dragend)="onDragEnd($event)">
2484
+
2485
+ <div class="grid grid-cols-12 gap-4 items-start">
2486
+ <!-- Drag Handle -->
2487
+ <div *ngIf="groups.length > 1"
2488
+ class="col-span-1 flex justify-center cursor-move text-gray-400 hover:text-gray-600 pt-2"
2489
+ draggable="true"
2490
+ (dragstart)="onDragStart($event, i)">
2491
+ <i class="pi pi-bars"></i>
2492
+ </div>
2493
+
2494
+ <!-- Group Content -->
2495
+ <div class="col-span-11">
2496
+ <div class="flex items-center justify-between mb-3">
2497
+ <div class="flex items-center gap-2">
2498
+ <i class="pi pi-sort-alt text-gray-500"></i>
2499
+ <span class="font-medium text-gray-700">Group Level {{ i + 1 }}</span>
2500
+ </div>
2501
+ <div class="flex items-center gap-1">
2502
+ <button
2503
+ type="button"
2504
+ (click)="removeGroup(i)"
2505
+ class="p-1 text-red-500 hover:text-red-700 hover:bg-red-50 rounded transition-colors"
2506
+ [pTooltip]="'Remove'"
2507
+ >
2508
+ <i class="pi pi-times"></i>
2509
+ </button>
2510
+ </div>
2511
+ </div>
2512
+
2513
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
2514
+ <!-- Field Selection -->
2515
+ <div>
2516
+ <label class="block text-sm font-medium text-gray-700 mb-2">Field</label>
2517
+ <p-select
2518
+ [options]="availableFields"
2519
+ optionLabel="label"
2520
+ optionValue="key"
2521
+ [formControl]="getGroupControl(i, 'field')"
2522
+ placeholder="Select field"
2523
+ [showClear]="true"
2524
+ (onChange)="onFieldChange(i, $event.value)"
2525
+ styleClass="w-full"
2526
+ >
2527
+ <ng-template pTemplate="selectedItem">
2528
+ <span *ngIf="getGroupControl(i, 'field').value" class="text-gray-900">
2529
+ {{ getFieldLabel(getGroupControl(i, 'field').value) }}
2530
+ </span>
2531
+ <span *ngIf="!getGroupControl(i, 'field').value" class="text-gray-500">Select field</span>
2532
+ </ng-template>
2533
+ </p-select>
2534
+ </div>
2535
+
2536
+ <!-- Display Name -->
2537
+ <div>
2538
+ <label class="block text-sm font-medium text-gray-700 mb-2">Display Name</label>
2539
+ <input
2540
+ type="text"
2541
+ pInputText
2542
+ [formControl]="getGroupControl(i, 'displayName')"
2543
+ placeholder="Display name (optional)"
2544
+ class="w-full text-gray-900"
2545
+ (blur)="onFormChange()"
2546
+ >
2547
+ </div>
2548
+ </div>
2549
+
2550
+ <!-- Show Total Toggle -->
2551
+ <div class="flex items-center gap-2 mt-3">
2552
+ <p-checkbox
2553
+ [binary]="true"
2554
+ [formControl]="getGroupControl(i, 'showTotal')"
2555
+ [inputId]="'showTotal' + i"
2556
+ styleClass="[&>div]:border-gray-300 [&>div]:hover:border-gray-400"
2557
+ (onChange)="onFormChange()"
2558
+ >
2559
+ </p-checkbox>
2560
+ <label [for]="'showTotal' + i" class="text-sm text-gray-700 cursor-pointer hover:text-gray-900">
2561
+ Show total for this group level
2562
+ </label>
2563
+ </div>
2564
+ </div>
2565
+ </div>
2566
+ </div>
2567
+ </div>
2568
+ </div>
2569
+
2570
+ <!-- Grouping Tips -->
2571
+ <div *ngIf="groups.length > 0" class="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
2572
+ <div class="flex items-center gap-3">
2573
+ <i class="pi pi-info-circle text-blue-500"></i>
2574
+ <div>
2575
+ <p class="text-sm text-blue-800">
2576
+ Data will be grouped hierarchically in the specified order.
2577
+ {{ groups.length }} level{{ groups.length > 1 ? 's' : '' }} defined.
2578
+ </p>
2579
+ </div>
2580
+ </div>
2581
+ </div>
2582
+ </div>
2583
+ `,
2584
+ imports: [
2585
+ CommonModule,
2586
+ FormsModule,
2587
+ ReactiveFormsModule,
2588
+ SelectModule,
2589
+ InputTextModule,
2590
+ ButtonModule,
2591
+ DatePickerModule,
2592
+ CheckboxModule,
2593
+ RadioButtonModule,
2594
+ FormlyForm,
2595
+ TooltipModule,
2596
+ MenuModule,
2597
+ TranslateModule,
2598
+ InputGroupModule,
2599
+ InputGroupAddonModule,
2600
+ PopoverModule,
2601
+ ListboxModule
2602
+ ]
2603
+ }]
2604
+ }] });
2605
+
1428
2606
  // group-type.component.ts
1429
2607
  class GroupTypeComponent extends FieldType {
1430
2608
  collapsed = this.props.collapsedByDefault ?? false;
@@ -1557,6 +2735,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
1557
2735
 
1558
2736
  const formlyConfig = provideFormlyConfig({
1559
2737
  types: [
2738
+ {
2739
+ name: 'tab-type',
2740
+ component: TabTypeComponent
2741
+ },
1560
2742
  {
1561
2743
  name: 'query-builder',
1562
2744
  component: QueryBuilderComponent
@@ -1566,12 +2748,14 @@ const formlyConfig = provideFormlyConfig({
1566
2748
  component: SortBuilderComponent
1567
2749
  },
1568
2750
  {
1569
- name: 'group', component: GroupTypeComponent
2751
+ name: 'group-builder',
2752
+ component: GroupBuilderComponent
1570
2753
  },
1571
2754
  {
1572
- name: 'tab-type',
1573
- component: TabTypeComponent
2755
+ name: 'columns-builder',
2756
+ component: ColumnsBuilderComponent
1574
2757
  },
2758
+ { name: 'group', component: GroupTypeComponent },
1575
2759
  ],
1576
2760
  });
1577
2761
 
@@ -1579,5 +2763,5 @@ const formlyConfig = provideFormlyConfig({
1579
2763
  * Generated bundle index. Do not edit.
1580
2764
  */
1581
2765
 
1582
- export { GenericSearchAdvanced, GenericSearchAdvancedModule, GroupTypeComponent, QueryBuilderComponent, QueryBuilderService, SortBuilderComponent, TabTypeComponent, formlyConfig };
2766
+ export { ColumnsBuilderComponent, GenericSearchAdvanced, GenericSearchAdvancedModule, GroupBuilderComponent, GroupTypeComponent, QueryBuilderComponent, QueryBuilderService, SortBuilderComponent, TabTypeComponent, formlyConfig };
1583
2767
  //# sourceMappingURL=elite.framework-ng.ui.core-generic-search-advanced.mjs.map