@elite.framework/ng.ui.core 1.0.56 → 1.0.59

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.
@@ -0,0 +1,808 @@
1
+ import * as i0 from '@angular/core';
2
+ import { NgModule, Injectable, Component, inject, EventEmitter, Input, Output, ViewChild } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i2 from '@angular/forms';
6
+ import { FormsModule, FormGroup, ReactiveFormsModule } from '@angular/forms';
7
+ import * as i6$1 from '@ngx-formly/core';
8
+ import { FieldType, FormlyForm, FormlyModule } from '@ngx-formly/core';
9
+ import * as i7 from '@ngx-translate/core';
10
+ import { TranslateService, TranslateModule } from '@ngx-translate/core';
11
+ import { ToolbarModule } from 'primeng/toolbar';
12
+ import * as i1$1 from 'primeng/button';
13
+ import { ButtonModule } from 'primeng/button';
14
+ import { PopoverModule } from 'primeng/popover';
15
+ import { InputIconModule } from 'primeng/inputicon';
16
+ import * as i3$1 from 'primeng/iconfield';
17
+ import { IconFieldModule } from 'primeng/iconfield';
18
+ import * as i4 from 'primeng/inputtext';
19
+ import { InputTextModule } from 'primeng/inputtext';
20
+ import * as i5 from 'primeng/drawer';
21
+ import { DrawerModule } from 'primeng/drawer';
22
+ import * as i6 from 'primeng/checkbox';
23
+ import { CheckboxModule } from 'primeng/checkbox';
24
+ import * as i3 from 'primeng/select';
25
+ import { SelectModule } from 'primeng/select';
26
+ import { DatePickerModule } from 'primeng/datepicker';
27
+
28
+ class GenericSearchAdvancedModule {
29
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericSearchAdvancedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
30
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.8", ngImport: i0, type: GenericSearchAdvancedModule, imports: [CommonModule] });
31
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericSearchAdvancedModule, imports: [CommonModule] });
32
+ }
33
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericSearchAdvancedModule, decorators: [{
34
+ type: NgModule,
35
+ args: [{
36
+ imports: [CommonModule],
37
+ }]
38
+ }] });
39
+
40
+ // query-builder.service.ts
41
+ class QueryBuilderService {
42
+ buildQueryUIFields(fields) {
43
+ return [
44
+ {
45
+ key: 'quickSearch',
46
+ type: 'input',
47
+ props: {
48
+ label: 'بحث سريع',
49
+ placeholder: 'ابحث في جميع الحقول...',
50
+ icon: 'pi pi-search'
51
+ }
52
+ },
53
+ {
54
+ key: 'advancedFilters',
55
+ type: 'query-builder',
56
+ props: {
57
+ label: 'مرشحات متقدمة',
58
+ fields: this.extractFilterableFields(fields)
59
+ }
60
+ },
61
+ {
62
+ key: 'sorting',
63
+ type: 'sort-builder',
64
+ props: {
65
+ label: 'ترتيب النتائج',
66
+ fields: this.extractSortableFields(fields)
67
+ }
68
+ },
69
+ {
70
+ key: 'pagination',
71
+ type: 'group',
72
+ props: { label: 'التقسيم الصفحي' },
73
+ fieldGroup: [
74
+ {
75
+ key: 'top',
76
+ type: 'input',
77
+ props: {
78
+ type: 'number',
79
+ label: 'عدد السجلات',
80
+ min: 1,
81
+ max: 1000,
82
+ description: 'أقصى عدد للسجلات المراد عرضها'
83
+ }
84
+ },
85
+ {
86
+ key: 'skip',
87
+ type: 'input',
88
+ props: {
89
+ type: 'number',
90
+ label: 'تخطي السجلات',
91
+ min: 0,
92
+ description: 'عدد السجلات التي سيتم تخطيها'
93
+ }
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ key: 'expand',
99
+ type: 'multiselect',
100
+ props: {
101
+ label: 'توسيع الحقول',
102
+ options: this.extractExpandableFields(fields),
103
+ placeholder: 'اختر الحقول للتوسيع'
104
+ }
105
+ }
106
+ ];
107
+ }
108
+ extractFilterableFields(fields) {
109
+ const filterableFields = [];
110
+ this.traverseFields(fields, (field) => {
111
+ if (field.key && field.props && field.props['filterable'] !== false && !field.props?.hidden) {
112
+ filterableFields.push({
113
+ key: field.key,
114
+ label: field.props.label || field.key,
115
+ type: this.mapFieldType(field.type),
116
+ operators: this.getOperatorsForType(field.type)
117
+ });
118
+ }
119
+ });
120
+ return filterableFields;
121
+ }
122
+ extractSortableFields(fields) {
123
+ const sortableFields = [];
124
+ this.traverseFields(fields, (field) => {
125
+ if (field.key && field.props && field.props['sortable'] !== false && !field.props?.hidden) {
126
+ sortableFields.push({
127
+ key: field.key,
128
+ label: field.props.label || field.key
129
+ });
130
+ }
131
+ });
132
+ return sortableFields;
133
+ }
134
+ extractExpandableFields(fields) {
135
+ const expandableFields = [];
136
+ this.traverseFields(fields, (field) => {
137
+ if (field.key && field.props && field.props['expandable'] && !field.props?.hidden) {
138
+ expandableFields.push({
139
+ label: field.props.label || field.key,
140
+ value: field.key
141
+ });
142
+ }
143
+ });
144
+ return expandableFields;
145
+ }
146
+ traverseFields(fields, callback) {
147
+ fields.forEach(field => {
148
+ callback(field);
149
+ if (field.fieldGroup) {
150
+ this.traverseFields(field.fieldGroup, callback);
151
+ }
152
+ if (field.fieldGroup) {
153
+ this.traverseFields(field.fieldGroup ?? [], callback);
154
+ }
155
+ });
156
+ }
157
+ mapFieldType(type) {
158
+ const typeMap = {
159
+ 'input': 'string',
160
+ 'number': 'number',
161
+ 'datepicker': 'date',
162
+ 'select': 'string',
163
+ 'checkbox': 'boolean',
164
+ 'radio': 'string',
165
+ 'textarea': 'string'
166
+ };
167
+ return typeMap[type || 'input'] || 'string';
168
+ }
169
+ getOperatorsForType(type) {
170
+ const operatorsMap = {
171
+ 'string': ['eq', 'ne', 'contains', 'startswith', 'endswith'],
172
+ 'number': ['eq', 'ne', 'gt', 'ge', 'lt', 'le'],
173
+ 'date': ['eq', 'ne', 'gt', 'ge', 'lt', 'le'],
174
+ 'boolean': ['eq', 'ne']
175
+ };
176
+ return operatorsMap[this.mapFieldType(type)] || ['eq', 'ne'];
177
+ }
178
+ buildODataFromQueryModel(model, originalFields) {
179
+ const params = {
180
+ filters: [],
181
+ orderBy: [],
182
+ groupBy: [],
183
+ expand: model.expand || []
184
+ };
185
+ // Quick search
186
+ if (model.quickSearch) {
187
+ const searchableFields = this.extractFilterableFields(originalFields);
188
+ searchableFields.forEach(field => {
189
+ params.filters.push({
190
+ field: field.key,
191
+ operator: 'contains',
192
+ value: model.quickSearch,
193
+ logicalOperator: 'or'
194
+ });
195
+ });
196
+ }
197
+ // Advanced filters
198
+ if (model.advancedFilters && Array.isArray(model.advancedFilters)) {
199
+ model.advancedFilters.forEach((filter) => {
200
+ if (filter.field && filter.operator && filter.value !== undefined && filter.value !== '') {
201
+ params.filters.push({
202
+ field: filter.field,
203
+ operator: filter.operator,
204
+ value: filter.value,
205
+ logicalOperator: 'and'
206
+ });
207
+ }
208
+ });
209
+ }
210
+ // Sorting
211
+ if (model.sorting && Array.isArray(model.sorting)) {
212
+ model.sorting.forEach((sort) => {
213
+ if (sort.field && sort.direction) {
214
+ params.orderBy.push({
215
+ field: sort.field,
216
+ direction: sort.direction
217
+ });
218
+ }
219
+ });
220
+ }
221
+ // Pagination
222
+ if (model.pagination) {
223
+ if (model.pagination.top) {
224
+ params.top = Number(model.pagination.top);
225
+ }
226
+ if (model.pagination.skip) {
227
+ params.skip = Number(model.pagination.skip);
228
+ }
229
+ }
230
+ return params;
231
+ }
232
+ toODataQueryString(params) {
233
+ const queryParts = [];
234
+ // Filters
235
+ if (params.filters.length > 0) {
236
+ const filterGroups = {};
237
+ params.filters.forEach(filter => {
238
+ const logicalOp = filter.logicalOperator || 'and';
239
+ if (!filterGroups[logicalOp]) {
240
+ filterGroups[logicalOp] = [];
241
+ }
242
+ filterGroups[logicalOp].push(`${filter.field} ${filter.operator} ${this.formatODataValue(filter.value)}`);
243
+ });
244
+ let filterString = '';
245
+ Object.keys(filterGroups).forEach((op, index) => {
246
+ if (index > 0)
247
+ filterString += ' and ';
248
+ if (filterGroups[op].length > 1) {
249
+ filterString += `(${filterGroups[op].join(` ${op} `)})`;
250
+ }
251
+ else {
252
+ filterString += filterGroups[op][0];
253
+ }
254
+ });
255
+ queryParts.push(`$filter=${encodeURIComponent(filterString)}`);
256
+ }
257
+ // Order by
258
+ if (params.orderBy.length > 0) {
259
+ const orderByString = params.orderBy.map(order => `${order.field} ${order.direction}`).join(',');
260
+ queryParts.push(`$orderby=${encodeURIComponent(orderByString)}`);
261
+ }
262
+ // Expand
263
+ if (params.expand && params.expand.length > 0) {
264
+ queryParts.push(`$expand=${encodeURIComponent(params.expand.join(','))}`);
265
+ }
266
+ // Top
267
+ if (params.top) {
268
+ queryParts.push(`$top=${params.top}`);
269
+ }
270
+ // Skip
271
+ if (params.skip) {
272
+ queryParts.push(`$skip=${params.skip}`);
273
+ }
274
+ return queryParts.length > 0 ? '?' + queryParts.join('&') : '';
275
+ }
276
+ formatODataValue(value) {
277
+ if (typeof value === 'string') {
278
+ return `'${value.replace(/'/g, "''")}'`;
279
+ }
280
+ else if (value instanceof Date) {
281
+ return `'${value.toISOString()}'`;
282
+ }
283
+ else if (typeof value === 'boolean') {
284
+ return value.toString();
285
+ }
286
+ return value;
287
+ }
288
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
289
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderService, providedIn: 'root' });
290
+ }
291
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderService, decorators: [{
292
+ type: Injectable,
293
+ args: [{
294
+ providedIn: 'root'
295
+ }]
296
+ }] });
297
+
298
+ // query-builder.component.ts
299
+ class QueryBuilderComponent extends FieldType {
300
+ conditions = [];
301
+ ngOnInit() {
302
+ if (this.formControl.value && Array.isArray(this.formControl.value)) {
303
+ this.conditions = [...this.formControl.value];
304
+ }
305
+ }
306
+ addCondition() {
307
+ this.conditions.push({ field: '', operator: 'eq', value: '' });
308
+ this.updateValue();
309
+ }
310
+ removeCondition(index) {
311
+ this.conditions.splice(index, 1);
312
+ this.updateValue();
313
+ }
314
+ onFieldChange(condition) {
315
+ condition.operator = 'eq';
316
+ condition.value = '';
317
+ this.updateValue();
318
+ }
319
+ updateValue() {
320
+ this.formControl.setValue([...this.conditions]);
321
+ }
322
+ getOperatorsForField(fieldKey) {
323
+ if (!fieldKey)
324
+ return [];
325
+ const field = this.props['fields'].find((f) => f.key === fieldKey);
326
+ const operators = field?.operators || ['eq', 'ne'];
327
+ return operators.map((op) => ({
328
+ label: this.getOperatorLabel(op),
329
+ value: op
330
+ }));
331
+ }
332
+ getOperatorLabel(operator) {
333
+ const labels = {
334
+ 'eq': 'يساوي',
335
+ 'ne': 'لا يساوي',
336
+ 'contains': 'يحتوي على',
337
+ 'startswith': 'يبدأ بـ',
338
+ 'endswith': 'ينتهي بـ',
339
+ 'gt': 'أكبر من',
340
+ 'ge': 'أكبر أو يساوي',
341
+ 'lt': 'أصغر من',
342
+ 'le': 'أصغر أو يساوي'
343
+ };
344
+ return labels[operator] || operator;
345
+ }
346
+ getFieldType(fieldKey) {
347
+ const field = this.props['fields'].find((f) => f.key === fieldKey);
348
+ return field?.type || 'string';
349
+ }
350
+ getInputType(fieldKey) {
351
+ const type = this.getFieldType(fieldKey);
352
+ return type === 'number' ? 'number' : 'text';
353
+ }
354
+ getValuePlaceholder(fieldKey) {
355
+ return 'القيمة';
356
+ }
357
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
358
+ 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: `
359
+ <div class="query-builder p-3 border-round border-1 surface-border">
360
+ <h4 class="mt-0">{{ props.label }}</h4>
361
+
362
+ <div *ngFor="let condition of conditions; let i = index" class="condition-row mb-2 p-2 border-round surface-card">
363
+ <div class="grid grid-nogutter align-items-center gap-2">
364
+ <!-- Field Selector -->
365
+ <div class="col">
366
+ <p-select
367
+ [options]="props['fields']"
368
+ optionLabel="label"
369
+ optionValue="key"
370
+ [(ngModel)]="condition.field"
371
+ (onChange)="onFieldChange(condition)"
372
+ placeholder="اختر الحقل"
373
+ [showClear]="true"
374
+ styleClass="w-full">
375
+ </p-select>
376
+ </div>
377
+
378
+ <!-- Operator Selector -->
379
+ <div class="col">
380
+ <p-select
381
+ [options]="getOperatorsForField(condition.field)"
382
+ [(ngModel)]="condition.operator"
383
+ placeholder="المعامل"
384
+ styleClass="w-full">
385
+ </p-select>
386
+ </div>
387
+
388
+ <!-- Value Input -->
389
+ <div class="col" *ngIf="condition.field && getFieldType(condition.field) !== 'boolean'">
390
+ <input
391
+ *ngIf="getFieldType(condition.field) !== 'date'"
392
+ pInputText
393
+ [(ngModel)]="condition.value"
394
+ [type]="getInputType(condition.field)"
395
+ [placeholder]="getValuePlaceholder(condition.field)"
396
+ styleClass="w-full">
397
+
398
+ <p-calendar
399
+ *ngIf="getFieldType(condition.field) === 'date'"
400
+ [(ngModel)]="condition.value"
401
+ dateFormat="yy-mm-dd"
402
+ styleClass="w-full">
403
+ </p-calendar>
404
+ </div>
405
+
406
+ <!-- Boolean Value -->
407
+ <div class="col" *ngIf="condition.field && getFieldType(condition.field) === 'boolean'">
408
+ <p-checkbox
409
+ [(ngModel)]="condition.value"
410
+ [binary]="true">
411
+ </p-checkbox>
412
+ </div>
413
+
414
+ <!-- Remove Button -->
415
+ <div class="col-fixed">
416
+ <button
417
+ pButton
418
+ icon="pi pi-times"
419
+ class="p-button-danger p-button-text"
420
+ (click)="removeCondition(i)">
421
+ </button>
422
+ </div>
423
+ </div>
424
+ </div>
425
+
426
+ <button
427
+ pButton
428
+ icon="pi pi-plus"
429
+ label="إضافة شرط"
430
+ class="p-button-outlined w-full"
431
+ (click)="addCondition()">
432
+ </button>
433
+ </div>
434
+ `, 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.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.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: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i6.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"] }] });
435
+ }
436
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: QueryBuilderComponent, decorators: [{
437
+ type: Component,
438
+ args: [{
439
+ selector: 'formly-query-builder',
440
+ standalone: true,
441
+ imports: [
442
+ CommonModule,
443
+ FormsModule,
444
+ SelectModule,
445
+ InputTextModule,
446
+ ButtonModule,
447
+ DatePickerModule,
448
+ CheckboxModule
449
+ ],
450
+ template: `
451
+ <div class="query-builder p-3 border-round border-1 surface-border">
452
+ <h4 class="mt-0">{{ props.label }}</h4>
453
+
454
+ <div *ngFor="let condition of conditions; let i = index" class="condition-row mb-2 p-2 border-round surface-card">
455
+ <div class="grid grid-nogutter align-items-center gap-2">
456
+ <!-- Field Selector -->
457
+ <div class="col">
458
+ <p-select
459
+ [options]="props['fields']"
460
+ optionLabel="label"
461
+ optionValue="key"
462
+ [(ngModel)]="condition.field"
463
+ (onChange)="onFieldChange(condition)"
464
+ placeholder="اختر الحقل"
465
+ [showClear]="true"
466
+ styleClass="w-full">
467
+ </p-select>
468
+ </div>
469
+
470
+ <!-- Operator Selector -->
471
+ <div class="col">
472
+ <p-select
473
+ [options]="getOperatorsForField(condition.field)"
474
+ [(ngModel)]="condition.operator"
475
+ placeholder="المعامل"
476
+ styleClass="w-full">
477
+ </p-select>
478
+ </div>
479
+
480
+ <!-- Value Input -->
481
+ <div class="col" *ngIf="condition.field && getFieldType(condition.field) !== 'boolean'">
482
+ <input
483
+ *ngIf="getFieldType(condition.field) !== 'date'"
484
+ pInputText
485
+ [(ngModel)]="condition.value"
486
+ [type]="getInputType(condition.field)"
487
+ [placeholder]="getValuePlaceholder(condition.field)"
488
+ styleClass="w-full">
489
+
490
+ <p-calendar
491
+ *ngIf="getFieldType(condition.field) === 'date'"
492
+ [(ngModel)]="condition.value"
493
+ dateFormat="yy-mm-dd"
494
+ styleClass="w-full">
495
+ </p-calendar>
496
+ </div>
497
+
498
+ <!-- Boolean Value -->
499
+ <div class="col" *ngIf="condition.field && getFieldType(condition.field) === 'boolean'">
500
+ <p-checkbox
501
+ [(ngModel)]="condition.value"
502
+ [binary]="true">
503
+ </p-checkbox>
504
+ </div>
505
+
506
+ <!-- Remove Button -->
507
+ <div class="col-fixed">
508
+ <button
509
+ pButton
510
+ icon="pi pi-times"
511
+ class="p-button-danger p-button-text"
512
+ (click)="removeCondition(i)">
513
+ </button>
514
+ </div>
515
+ </div>
516
+ </div>
517
+
518
+ <button
519
+ pButton
520
+ icon="pi pi-plus"
521
+ label="إضافة شرط"
522
+ class="p-button-outlined w-full"
523
+ (click)="addCondition()">
524
+ </button>
525
+ </div>
526
+ `
527
+ }]
528
+ }] });
529
+
530
+ // sort-builder.component.ts
531
+ class SortBuilderComponent extends FieldType {
532
+ sorts = [];
533
+ directionOptions = [
534
+ { label: 'تصاعدي', value: 'asc' },
535
+ { label: 'تنازلي', value: 'desc' }
536
+ ];
537
+ ngOnInit() {
538
+ if (this.formControl.value && Array.isArray(this.formControl.value)) {
539
+ this.sorts = [...this.formControl.value];
540
+ }
541
+ }
542
+ addSort() {
543
+ this.sorts.push({ field: '', direction: 'asc' });
544
+ this.updateValue();
545
+ }
546
+ removeSort(index) {
547
+ this.sorts.splice(index, 1);
548
+ this.updateValue();
549
+ }
550
+ updateValue() {
551
+ this.formControl.setValue([...this.sorts]);
552
+ }
553
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SortBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
554
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: SortBuilderComponent, isStandalone: true, selector: "formly-sort-builder", usesInheritance: true, ngImport: i0, template: `
555
+ <div class="sort-builder p-3 border-round border-1 surface-border">
556
+ <h4 class="mt-0">{{ props.label }}</h4>
557
+
558
+ <div *ngFor="let sort of sorts; let i = index" class="sort-row mb-2 p-2 border-round surface-card">
559
+ <div class="grid grid-nogutter align-items-center gap-2">
560
+ <!-- Field Selector -->
561
+ <div class="col">
562
+ <p-select
563
+ [options]="props['fields']"
564
+ optionLabel="label"
565
+ optionValue="key"
566
+ [(ngModel)]="sort.field"
567
+ placeholder="اختر الحقل للترتيب"
568
+ [showClear]="true"
569
+ styleClass="w-full">
570
+ </p-select>
571
+ </div>
572
+
573
+ <!-- Direction Selector -->
574
+ <div class="col">
575
+ <p-select
576
+ [options]="directionOptions"
577
+ [(ngModel)]="sort.direction"
578
+ placeholder="الاتجاه"
579
+ styleClass="w-full">
580
+ </p-select>
581
+ </div>
582
+
583
+ <!-- Remove Button -->
584
+ <div class="col-fixed">
585
+ <button
586
+ pButton
587
+ icon="pi pi-times"
588
+ class="p-button-danger p-button-text"
589
+ (click)="removeSort(i)">
590
+ </button>
591
+ </div>
592
+ </div>
593
+ </div>
594
+
595
+ <button
596
+ pButton
597
+ icon="pi pi-sort-alt"
598
+ label="إضافة ترتيب"
599
+ class="p-button-outlined w-full"
600
+ (click)="addSort()">
601
+ </button>
602
+ </div>
603
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.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: i1$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }] });
604
+ }
605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SortBuilderComponent, decorators: [{
606
+ type: Component,
607
+ args: [{
608
+ selector: 'formly-sort-builder',
609
+ standalone: true,
610
+ imports: [CommonModule, FormsModule, SelectModule, ButtonModule],
611
+ template: `
612
+ <div class="sort-builder p-3 border-round border-1 surface-border">
613
+ <h4 class="mt-0">{{ props.label }}</h4>
614
+
615
+ <div *ngFor="let sort of sorts; let i = index" class="sort-row mb-2 p-2 border-round surface-card">
616
+ <div class="grid grid-nogutter align-items-center gap-2">
617
+ <!-- Field Selector -->
618
+ <div class="col">
619
+ <p-select
620
+ [options]="props['fields']"
621
+ optionLabel="label"
622
+ optionValue="key"
623
+ [(ngModel)]="sort.field"
624
+ placeholder="اختر الحقل للترتيب"
625
+ [showClear]="true"
626
+ styleClass="w-full">
627
+ </p-select>
628
+ </div>
629
+
630
+ <!-- Direction Selector -->
631
+ <div class="col">
632
+ <p-select
633
+ [options]="directionOptions"
634
+ [(ngModel)]="sort.direction"
635
+ placeholder="الاتجاه"
636
+ styleClass="w-full">
637
+ </p-select>
638
+ </div>
639
+
640
+ <!-- Remove Button -->
641
+ <div class="col-fixed">
642
+ <button
643
+ pButton
644
+ icon="pi pi-times"
645
+ class="p-button-danger p-button-text"
646
+ (click)="removeSort(i)">
647
+ </button>
648
+ </div>
649
+ </div>
650
+ </div>
651
+
652
+ <button
653
+ pButton
654
+ icon="pi pi-sort-alt"
655
+ label="إضافة ترتيب"
656
+ class="p-button-outlined w-full"
657
+ (click)="addSort()">
658
+ </button>
659
+ </div>
660
+ `
661
+ }]
662
+ }] });
663
+
664
+ // generic-search.component.ts
665
+ class GenericSearchAdvanced {
666
+ overlay;
667
+ translate = inject(TranslateService);
668
+ queryBuilder = inject(QueryBuilderService);
669
+ drawerVisible = false;
670
+ search = new EventEmitter();
671
+ odataSearch = new EventEmitter();
672
+ form = new FormGroup({});
673
+ model = {};
674
+ options = {};
675
+ fields = [];
676
+ fields_ = [];
677
+ enableQueryBuilder = false;
678
+ odataConfig = {};
679
+ ngOnInit() {
680
+ this.buildQueryUIFields();
681
+ }
682
+ buildQueryUIFields() {
683
+ if (this.enableQueryBuilder) {
684
+ this.fields_ = this.queryBuilder.buildQueryUIFields(this.fields);
685
+ }
686
+ else {
687
+ this.fields_ = this.fields;
688
+ }
689
+ }
690
+ onSubmit() {
691
+ this.search.emit(this.model);
692
+ if (this.enableQueryBuilder) {
693
+ const odataParams = this.queryBuilder.buildODataFromQueryModel(this.model, this.fields);
694
+ this.odataSearch.emit(odataParams);
695
+ // Generate query string
696
+ const queryString = this.queryBuilder.toODataQueryString(odataParams);
697
+ console.log('OData Query String:', queryString);
698
+ }
699
+ else {
700
+ // Use existing logic for non-query-builder mode
701
+ const odataParams = this.buildODataQueryParameters(this.model);
702
+ this.odataSearch.emit(odataParams);
703
+ }
704
+ }
705
+ onReset() {
706
+ this.model = {};
707
+ this.form.reset();
708
+ this.search.emit(this.model);
709
+ this.odataSearch.emit({
710
+ filters: [],
711
+ orderBy: [],
712
+ groupBy: [],
713
+ expand: []
714
+ });
715
+ }
716
+ // Existing OData building methods for backward compatibility
717
+ buildODataQueryParameters(model) {
718
+ const params = {
719
+ filters: [],
720
+ orderBy: [],
721
+ groupBy: [],
722
+ expand: this.odataConfig.expandFields || []
723
+ };
724
+ Object.keys(model).forEach(key => {
725
+ const value = model[key];
726
+ if (value === null || value === undefined || value === '' ||
727
+ (Array.isArray(value) && value.length === 0)) {
728
+ return;
729
+ }
730
+ if (typeof value === 'object' && value !== null) {
731
+ this.processComplexField(key, value, params);
732
+ }
733
+ else {
734
+ params.filters.push({
735
+ field: key,
736
+ operator: 'eq',
737
+ value: value,
738
+ logicalOperator: 'and'
739
+ });
740
+ }
741
+ });
742
+ return params;
743
+ }
744
+ processComplexField(key, value, params) {
745
+ if (value.from || value.to) {
746
+ if (value.from) {
747
+ params.filters.push({
748
+ field: key,
749
+ operator: 'ge',
750
+ value: value.from,
751
+ logicalOperator: 'and'
752
+ });
753
+ }
754
+ if (value.to) {
755
+ params.filters.push({
756
+ field: key,
757
+ operator: 'le',
758
+ value: value.to,
759
+ logicalOperator: 'and'
760
+ });
761
+ }
762
+ }
763
+ }
764
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericSearchAdvanced, deps: [], target: i0.ɵɵFactoryTarget.Component });
765
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.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", odataSearch: "odataSearch" }, viewQueries: [{ propertyName: "overlay", first: true, predicate: ["overlay"], descendants: true }], ngImport: i0, template: "<div class=\"flex flex-wrap items-center gap-3 w-full\">\r\n <!-- Search Input with Icon -->\r\n <p-iconfield iconPosition=\"left\">\r\n <input\r\n pInputText\r\n type=\"text\"\r\n (keyup.enter)=\"onSubmit()\"\r\n [(ngModel)]=\"model['filter']\"\r\n placeholder=\"{{ 'SEARCH' | translate }}\"\r\n />\r\n </p-iconfield>\r\n\r\n <!-- Filter Button -->\r\n <p-button\r\n icon=\"pi pi-filter\"\r\n outlined\r\n severity=\"secondary\"\r\n (onClick)=\"drawerVisible = true\"\r\n ></p-button>\r\n</div>\r\n\r\n<!-- Drawer for Advanced Multi-field Search -->\r\n<p-drawer\r\n [(visible)]=\"drawerVisible\"\r\n position=\"right\"\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 <!-- Action Buttons -->\r\n <div class=\"flex justify-end mt-2 space-x-2 flex-none\">\r\n <button type=\"submit\" pButton size=\"small\" label=\"{{ 'SEARCH' | translate }}\"></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 </form>\r\n</p-drawer>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: ToolbarModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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: "component", type: i3$1.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["iconPosition", "styleClass"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i5.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: FormlyModule }, { kind: "component", type: i6$1.LegacyFormlyForm, selector: "formly-form" }, { kind: "pipe", type: i7.TranslatePipe, name: "translate" }] });
766
+ }
767
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: GenericSearchAdvanced, decorators: [{
768
+ type: Component,
769
+ args: [{ selector: 'lib-generic-search-advanced', standalone: true, imports: [
770
+ CommonModule,
771
+ FormlyForm,
772
+ TranslateModule,
773
+ ToolbarModule,
774
+ ButtonModule,
775
+ ReactiveFormsModule,
776
+ PopoverModule,
777
+ FormsModule,
778
+ InputIconModule,
779
+ IconFieldModule,
780
+ InputTextModule,
781
+ DrawerModule,
782
+ FormlyModule,
783
+ QueryBuilderComponent,
784
+ SortBuilderComponent
785
+ ], template: "<div class=\"flex flex-wrap items-center gap-3 w-full\">\r\n <!-- Search Input with Icon -->\r\n <p-iconfield iconPosition=\"left\">\r\n <input\r\n pInputText\r\n type=\"text\"\r\n (keyup.enter)=\"onSubmit()\"\r\n [(ngModel)]=\"model['filter']\"\r\n placeholder=\"{{ 'SEARCH' | translate }}\"\r\n />\r\n </p-iconfield>\r\n\r\n <!-- Filter Button -->\r\n <p-button\r\n icon=\"pi pi-filter\"\r\n outlined\r\n severity=\"secondary\"\r\n (onClick)=\"drawerVisible = true\"\r\n ></p-button>\r\n</div>\r\n\r\n<!-- Drawer for Advanced Multi-field Search -->\r\n<p-drawer\r\n [(visible)]=\"drawerVisible\"\r\n position=\"right\"\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 <!-- Action Buttons -->\r\n <div class=\"flex justify-end mt-2 space-x-2 flex-none\">\r\n <button type=\"submit\" pButton size=\"small\" label=\"{{ 'SEARCH' | translate }}\"></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 </form>\r\n</p-drawer>\r\n" }]
786
+ }], propDecorators: { overlay: [{
787
+ type: ViewChild,
788
+ args: ['overlay']
789
+ }], search: [{
790
+ type: Output
791
+ }], odataSearch: [{
792
+ type: Output
793
+ }], model: [{
794
+ type: Input
795
+ }], fields: [{
796
+ type: Input
797
+ }], enableQueryBuilder: [{
798
+ type: Input
799
+ }], odataConfig: [{
800
+ type: Input
801
+ }] } });
802
+
803
+ /**
804
+ * Generated bundle index. Do not edit.
805
+ */
806
+
807
+ export { GenericSearchAdvanced, GenericSearchAdvancedModule, QueryBuilderComponent, QueryBuilderService, SortBuilderComponent };
808
+ //# sourceMappingURL=elite.framework-ng.ui.core-generic-search-advanced.mjs.map