@cqa-lib/cqa-ui 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/esm2020/lib/action-menu/action-menu.component.mjs +42 -0
  2. package/esm2020/lib/assets/images/image-assets.constants.mjs +28 -0
  3. package/esm2020/lib/badge/badge.component.mjs +141 -0
  4. package/esm2020/lib/button/button.component.mjs +232 -0
  5. package/esm2020/lib/column-visibility/column-visibility.component.mjs +69 -0
  6. package/esm2020/lib/dashboards/chart-card/chart-card.component.mjs +22 -0
  7. package/esm2020/lib/dashboards/coverage-module-card/coverage-module-card.component.mjs +104 -0
  8. package/esm2020/lib/dashboards/dashboard-header/dashboard-header.component.mjs +82 -0
  9. package/esm2020/lib/dashboards/failed-test-cases-card/failed-test-cases-card.component.mjs +60 -0
  10. package/esm2020/lib/dashboards/heat-error-map-cell/heat-error-map-cell.component.mjs +45 -0
  11. package/esm2020/lib/dashboards/insight-card/insight-card.component.mjs +201 -0
  12. package/esm2020/lib/dashboards/metrics-card/metrics-block.component.mjs +41 -0
  13. package/esm2020/lib/dashboards/metrics-card/metrics-card-item.interface.mjs +2 -0
  14. package/esm2020/lib/dashboards/metrics-card/metrics-card.component.mjs +62 -0
  15. package/esm2020/lib/dashboards/progress-text-card/progress-text-card.component.mjs +46 -0
  16. package/esm2020/lib/dashboards/test-distribution-card/test-distribution-card.component.mjs +35 -0
  17. package/esm2020/lib/dialog/dialog.component.mjs +127 -0
  18. package/esm2020/lib/dropdown-button/dropdown-button.component.mjs +189 -0
  19. package/esm2020/lib/dynamic-select/dynamic-select-field.component.mjs +160 -0
  20. package/esm2020/lib/empty-state/empty-state.component.mjs +37 -0
  21. package/esm2020/lib/filters/dynamic-filter/dynamic-filter.component.mjs +239 -0
  22. package/esm2020/lib/full-table-loader/full-table-loader.component.mjs +16 -0
  23. package/esm2020/lib/inline-sort/inline-sort.component.mjs +58 -0
  24. package/esm2020/lib/other-button/other-button.component.mjs +76 -0
  25. package/esm2020/lib/pagination/pagination.component.mjs +102 -0
  26. package/{dist/cqa-ui/esm2020 → esm2020}/lib/search-bar/search-bar.component.mjs +3 -3
  27. package/{dist/cqa-ui/esm2020 → esm2020}/lib/segment-control/segment-control.component.mjs +3 -3
  28. package/esm2020/lib/selected-filters/selected-filters.component.mjs +27 -0
  29. package/esm2020/lib/table/dynamic-table/dynamic-cell.directive.mjs +35 -0
  30. package/esm2020/lib/table/dynamic-table/dynamic-table.component.mjs +258 -0
  31. package/esm2020/lib/table-action-toolbar/table-action-toolbar.component.mjs +52 -0
  32. package/esm2020/lib/table-data-loader/table-data-loader.component.mjs +19 -0
  33. package/esm2020/lib/templates/table-template.component.mjs +365 -0
  34. package/esm2020/lib/ui-kit.module.mjs +248 -0
  35. package/esm2020/lib/utils/metadata-colors.util.mjs +100 -0
  36. package/esm2020/lib/utils/tw-overlay-container.mjs +22 -0
  37. package/esm2020/public-api.mjs +38 -0
  38. package/fesm2015/cqa-lib-cqa-ui.mjs +3661 -0
  39. package/fesm2015/cqa-lib-cqa-ui.mjs.map +1 -0
  40. package/fesm2020/cqa-lib-cqa-ui.mjs +3615 -0
  41. package/fesm2020/cqa-lib-cqa-ui.mjs.map +1 -0
  42. package/lib/action-menu/action-menu.component.d.ts +17 -0
  43. package/lib/assets/images/image-assets.constants.d.ts +20 -0
  44. package/lib/badge/badge.component.d.ts +25 -0
  45. package/{dist/cqa-ui/lib → lib}/button/button.component.d.ts +6 -5
  46. package/lib/column-visibility/column-visibility.component.d.ts +33 -0
  47. package/lib/dashboards/chart-card/chart-card.component.d.ts +8 -0
  48. package/lib/dashboards/coverage-module-card/coverage-module-card.component.d.ts +44 -0
  49. package/lib/dashboards/dashboard-header/dashboard-header.component.d.ts +30 -0
  50. package/lib/dashboards/failed-test-cases-card/failed-test-cases-card.component.d.ts +28 -0
  51. package/lib/dashboards/heat-error-map-cell/heat-error-map-cell.component.d.ts +14 -0
  52. package/lib/dashboards/insight-card/insight-card.component.d.ts +73 -0
  53. package/lib/dashboards/metrics-card/metrics-block.component.d.ts +12 -0
  54. package/lib/dashboards/metrics-card/metrics-card-item.interface.d.ts +12 -0
  55. package/lib/dashboards/metrics-card/metrics-card.component.d.ts +17 -0
  56. package/lib/dashboards/progress-text-card/progress-text-card.component.d.ts +13 -0
  57. package/lib/dashboards/test-distribution-card/test-distribution-card.component.d.ts +29 -0
  58. package/lib/dropdown-button/dropdown-button.component.d.ts +32 -0
  59. package/lib/dynamic-select/dynamic-select-field.component.d.ts +43 -0
  60. package/lib/empty-state/empty-state.component.d.ts +20 -0
  61. package/lib/filters/dynamic-filter/dynamic-filter.component.d.ts +56 -0
  62. package/lib/full-table-loader/full-table-loader.component.d.ts +6 -0
  63. package/lib/inline-sort/inline-sort.component.d.ts +12 -0
  64. package/lib/other-button/other-button.component.d.ts +37 -0
  65. package/lib/pagination/pagination.component.d.ts +37 -0
  66. package/lib/selected-filters/selected-filters.component.d.ts +17 -0
  67. package/lib/table/dynamic-table/dynamic-cell.directive.d.ts +16 -0
  68. package/lib/table/dynamic-table/dynamic-table.component.d.ts +72 -0
  69. package/lib/table-action-toolbar/table-action-toolbar.component.d.ts +34 -0
  70. package/lib/table-data-loader/table-data-loader.component.d.ts +7 -0
  71. package/lib/templates/table-template.component.d.ts +90 -0
  72. package/lib/ui-kit.module.d.ts +52 -0
  73. package/lib/utils/metadata-colors.util.d.ts +50 -0
  74. package/lib/utils/tw-overlay-container.d.ts +12 -0
  75. package/package.json +23 -49
  76. package/public-api.d.ts +37 -0
  77. package/src/lib/assets/images/.gitkeep +0 -0
  78. package/src/lib/assets/images/DashboardIcon.png +0 -0
  79. package/src/lib/assets/images/FilesIcon.png +0 -0
  80. package/src/lib/assets/images/README.md +66 -0
  81. package/src/lib/assets/images/ReportsIcon.png +0 -0
  82. package/src/lib/assets/images/SearchIcon.png +0 -0
  83. package/src/lib/assets/images/StepsIcon.png +0 -0
  84. package/src/lib/assets/images/TestCaseIcon.png +0 -0
  85. package/src/lib/assets/images/analytics-chart-icon.svg +11 -0
  86. package/src/lib/assets/images/checklist-add-icon.svg +10 -0
  87. package/src/lib/assets/images/document-gear-icon.svg +9 -0
  88. package/src/lib/assets/images/empty-state-default-icon.svg +8 -0
  89. package/src/lib/assets/images/image-assets.constants.ts +38 -0
  90. package/src/lib/assets/images/search-debug-icon.svg +8 -0
  91. package/src/lib/assets/images/test-case-icon.svg +9 -0
  92. package/src/lib/assets/images/upload-folder-icon.svg +7 -0
  93. package/src/lib/utils/metadata-colors.constants.js +33 -0
  94. package/storybook-static/assets/images/README.md +66 -0
  95. package/styles.css +1 -0
  96. package/dist/cqa-ui/README.md +0 -226
  97. package/dist/cqa-ui/esm2020/lib/button/button.component.mjs +0 -257
  98. package/dist/cqa-ui/esm2020/lib/dialog/dialog.component.mjs +0 -127
  99. package/dist/cqa-ui/esm2020/lib/ui-kit.module.mjs +0 -69
  100. package/dist/cqa-ui/esm2020/public-api.mjs +0 -10
  101. package/dist/cqa-ui/fesm2015/cqa-lib-cqa-ui.mjs +0 -895
  102. package/dist/cqa-ui/fesm2015/cqa-lib-cqa-ui.mjs.map +0 -1
  103. package/dist/cqa-ui/fesm2020/cqa-lib-cqa-ui.mjs +0 -881
  104. package/dist/cqa-ui/fesm2020/cqa-lib-cqa-ui.mjs.map +0 -1
  105. package/dist/cqa-ui/lib/ui-kit.module.d.ts +0 -15
  106. package/dist/cqa-ui/package.json +0 -56
  107. package/dist/cqa-ui/public-api.d.ts +0 -9
  108. package/dist/cqa-ui/styles.css +0 -1
  109. /package/{dist/cqa-ui/cqa-lib-cqa-ui.d.ts → cqa-lib-cqa-ui.d.ts} +0 -0
  110. /package/{dist/cqa-ui/esm2020 → esm2020}/cqa-lib-cqa-ui.mjs +0 -0
  111. /package/{dist/cqa-ui/esm2020 → esm2020}/lib/dialog/dialog-ref.mjs +0 -0
  112. /package/{dist/cqa-ui/esm2020 → esm2020}/lib/dialog/dialog.models.mjs +0 -0
  113. /package/{dist/cqa-ui/esm2020 → esm2020}/lib/dialog/dialog.service.mjs +0 -0
  114. /package/{dist/cqa-ui/esm2020 → esm2020}/lib/dialog/dialog.tokens.mjs +0 -0
  115. /package/{dist/cqa-ui/lib → lib}/dialog/dialog-ref.d.ts +0 -0
  116. /package/{dist/cqa-ui/lib → lib}/dialog/dialog.component.d.ts +0 -0
  117. /package/{dist/cqa-ui/lib → lib}/dialog/dialog.models.d.ts +0 -0
  118. /package/{dist/cqa-ui/lib → lib}/dialog/dialog.service.d.ts +0 -0
  119. /package/{dist/cqa-ui/lib → lib}/dialog/dialog.tokens.d.ts +0 -0
  120. /package/{dist/cqa-ui/lib → lib}/search-bar/search-bar.component.d.ts +0 -0
  121. /package/{dist/cqa-ui/lib → lib}/segment-control/segment-control.component.d.ts +0 -0
@@ -0,0 +1,3615 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, Component, Input, Output, HostListener, ViewChildren, ViewChild, ChangeDetectionStrategy, Directive, TemplateRef, ContentChildren, ContentChild, ElementRef, Injectable, NgModule, InjectionToken, Injector } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i1$1 from '@angular/forms';
6
+ import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
7
+ import * as i1 from '@angular/material/icon';
8
+ import { MatIconModule } from '@angular/material/icon';
9
+ import * as i1$2 from '@angular/material/tooltip';
10
+ import { MatTooltipModule } from '@angular/material/tooltip';
11
+ import * as i3$1 from '@angular/material/menu';
12
+ import { MatMenuModule } from '@angular/material/menu';
13
+ import * as i1$3 from '@angular/material/button';
14
+ import { MatButtonModule } from '@angular/material/button';
15
+ import * as i3$2 from '@angular/material/form-field';
16
+ import { MatFormFieldModule } from '@angular/material/form-field';
17
+ import * as i2$1 from '@angular/material/select';
18
+ import { MatSelectModule } from '@angular/material/select';
19
+ import * as i3$3 from '@angular/material/core';
20
+ import { MatOptionModule, MatNativeDateModule } from '@angular/material/core';
21
+ import * as i3$4 from '@angular/material/checkbox';
22
+ import { MatCheckboxModule } from '@angular/material/checkbox';
23
+ import * as i4$1 from '@angular/material/radio';
24
+ import { MatRadioModule } from '@angular/material/radio';
25
+ import * as i4 from '@angular/material/datepicker';
26
+ import { MatDatepickerModule } from '@angular/material/datepicker';
27
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
28
+ import * as i1$4 from '@angular/cdk/overlay';
29
+ import { OverlayContainer, OverlayModule, OverlayConfig } from '@angular/cdk/overlay';
30
+ import * as i3 from '@angular/cdk/portal';
31
+ import { TemplatePortal, CdkPortalOutlet, PortalModule, ComponentPortal } from '@angular/cdk/portal';
32
+ import { filter } from 'rxjs/operators';
33
+ import { Subject } from 'rxjs';
34
+
35
+ class ButtonComponent {
36
+ constructor() {
37
+ this.variant = 'filled';
38
+ this.disabled = false;
39
+ this.iconPosition = 'start';
40
+ this.fullWidth = false;
41
+ this.type = 'button';
42
+ this.clicked = new EventEmitter();
43
+ // Internal state tracking
44
+ this.isHovered = false;
45
+ this.isFocused = false;
46
+ this.isPressed = false;
47
+ }
48
+ get hasIcon() {
49
+ return !!this.icon;
50
+ }
51
+ get buttonClasses() {
52
+ const baseClasses = [
53
+ 'cqa-inline-flex',
54
+ 'cqa-items-center',
55
+ 'cqa-justify-center',
56
+ 'cqa-gap-2',
57
+ 'cqa-py-[10px]',
58
+ 'cqa-rounded-[8px]',
59
+ 'cqa-text-[12.3px]',
60
+ 'cqa-leading-[17.5px]',
61
+ 'cqa-font-medium',
62
+ 'cqa-border',
63
+ ];
64
+ if (this.disabled) {
65
+ baseClasses.push('cqa-cursor-not-allowed');
66
+ }
67
+ if (this.fullWidth) {
68
+ baseClasses.push('cqa-w-full');
69
+ }
70
+ // Add variant and state specific classes
71
+ const variantClasses = this.getVariantClasses();
72
+ return [...baseClasses, ...variantClasses, ...(this.customClass ? [this.customClass] : [])].join(' ');
73
+ }
74
+ getVariantClasses() {
75
+ const classes = [];
76
+ if (this.variant === 'filled') {
77
+ if (this.disabled) {
78
+ classes.push('cqa-bg-primary-muted');
79
+ }
80
+ else {
81
+ classes.push('cqa-bg-primary cqa-text-white');
82
+ if (this.isHovered) {
83
+ classes.push('cqa-bg-primary-hover');
84
+ }
85
+ }
86
+ }
87
+ else if (this.variant === 'grey-solid') {
88
+ // Neutral grey solid style
89
+ if (this.disabled) {
90
+ classes.push('cqa-bg-grey-400', 'cqa-border', 'cqa-border-primary-muted');
91
+ }
92
+ else {
93
+ classes.push('cqa-bg-grey-400', 'cqa-border', 'cqa-border-primary-muted');
94
+ if (this.isHovered) {
95
+ classes.push('cqa-bg-grey-200');
96
+ }
97
+ }
98
+ }
99
+ else if (this.variant === 'outlined') {
100
+ if (this.disabled) {
101
+ classes.push('cqa-bg-transparent', 'cqa-border', 'cqa-border-primary-muted');
102
+ }
103
+ else {
104
+ if (this.isFocused) {
105
+ classes.push('cqa-bg-primary-surface-alt', 'cqa-border', 'cqa-border-primary-hover', 'cqa-shadow-[0px_4px_4px_rgba(0,0,0,0.25)]');
106
+ }
107
+ else if (this.isHovered || this.isPressed) {
108
+ classes.push('cqa-bg-primary-surface', 'cqa-border', 'cqa-border-primary');
109
+ }
110
+ else {
111
+ classes.push('cqa-bg-transparent', 'cqa-border', 'cqa-border-slate');
112
+ }
113
+ }
114
+ }
115
+ else if (this.variant === 'text') {
116
+ if (this.disabled) {
117
+ classes.push('cqa-bg-transparent', 'cqa-border-none');
118
+ }
119
+ else {
120
+ classes.push('cqa-bg-transparent', 'cqa-border-none');
121
+ if (this.isHovered || this.isFocused || this.isPressed) {
122
+ classes.push('cqa-bg-primary-surface');
123
+ }
124
+ }
125
+ }
126
+ else if (this.variant === 'elevated') {
127
+ if (this.disabled) {
128
+ classes.push('cqa-bg-primary-muted', 'cqa-shadow-none');
129
+ }
130
+ else {
131
+ if (this.isFocused) {
132
+ classes.push('cqa-bg-primary-surface-alt', 'cqa-shadow-[0px_4px_4px_rgba(0,0,0,0.25)]');
133
+ }
134
+ else if (this.isPressed) {
135
+ classes.push('cqa-bg-primary-surface', 'cqa-shadow-[0px_1px_2px_rgba(0,0,0,0.3),0px_1px_3px_1px_rgba(0,0,0,0.15)]');
136
+ }
137
+ else if (this.isHovered) {
138
+ classes.push('cqa-bg-primary-surface-alt', 'cqa-shadow-[0px_1px_2px_rgba(0,0,0,0.3),0px_2px_6px_2px_rgba(0,0,0,0.15)]');
139
+ }
140
+ else {
141
+ classes.push('cqa-bg-primary-surface', 'cqa-shadow-[0px_1px_2px_rgba(0,0,0,0.3),0px_1px_3px_1px_rgba(0,0,0,0.15)]');
142
+ }
143
+ }
144
+ }
145
+ else if (this.variant === 'tonal') {
146
+ if (this.disabled) {
147
+ classes.push('cqa-bg-primary-muted');
148
+ }
149
+ else {
150
+ if (this.isHovered) {
151
+ classes.push('cqa-bg-tonal-hover', 'cqa-shadow-[0px_1px_2px_rgba(0,0,0,0.3),0px_1px_3px_1px_rgba(0,0,0,0.15)]');
152
+ }
153
+ else {
154
+ classes.push('cqa-bg-primary-surface-alt');
155
+ }
156
+ }
157
+ }
158
+ return classes;
159
+ }
160
+ get textClass() {
161
+ if (this.disabled) {
162
+ if (this.variant === 'outlined' || this.variant === 'text') {
163
+ return 'cqa-text-ink';
164
+ }
165
+ return 'cqa-text-ink-muted';
166
+ }
167
+ switch (this.variant) {
168
+ case 'filled':
169
+ return 'cqa-text-surface-default';
170
+ case 'grey-solid':
171
+ return 'cqa-text-black-100';
172
+ case 'outlined':
173
+ if (this.isFocused || this.isHovered || this.isPressed) {
174
+ return 'cqa-text-primary-hover';
175
+ }
176
+ return 'cqa-text-slate';
177
+ case 'text':
178
+ case 'elevated':
179
+ return 'cqa-text-primary-hover';
180
+ case 'tonal':
181
+ return 'cqa-text-ink';
182
+ default:
183
+ return '';
184
+ }
185
+ }
186
+ onMouseEnter() {
187
+ if (!this.disabled) {
188
+ this.isHovered = true;
189
+ }
190
+ }
191
+ onMouseLeave() {
192
+ this.isHovered = false;
193
+ this.isPressed = false;
194
+ }
195
+ onMouseDown() {
196
+ if (!this.disabled) {
197
+ this.isPressed = true;
198
+ }
199
+ }
200
+ onMouseUp() {
201
+ this.isPressed = false;
202
+ }
203
+ onFocus() {
204
+ if (!this.disabled) {
205
+ this.isFocused = true;
206
+ }
207
+ }
208
+ onBlur() {
209
+ this.isFocused = false;
210
+ this.isPressed = false;
211
+ }
212
+ onClick(event) {
213
+ if (!this.disabled) {
214
+ this.clicked.emit(event);
215
+ }
216
+ }
217
+ }
218
+ ButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
219
+ ButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ButtonComponent, selector: "cqa-button", inputs: { variant: "variant", disabled: "disabled", icon: "icon", iconPosition: "iconPosition", fullWidth: "fullWidth", iconColor: "iconColor", type: "type", text: "text", customClass: "customClass" }, outputs: { clicked: "clicked" }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "mousedown": "onMouseDown()", "mouseup": "onMouseUp()", "focus": "onFocus()", "blur": "onBlur()" } }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <button\n [ngClass]=\"\n text && icon && iconPosition === 'start' ? 'cqa-pr-[24px] cqa-pl-[16px]' :\n text && icon && iconPosition === 'end' ? 'cqa-pl-[24px] cqa-pr-[16px]' :\n text && !icon ? 'cqa-px-[24px]' : !text && icon ? 'cqa-px-[12px]' : 'cqa-px-[24px]'\"\n [type]=\"type\"\n [disabled]=\"disabled\"\n [attr.aria-disabled]=\"disabled\"\n [class]=\"buttonClasses\"\n (click)=\"onClick($event)\"\n >\n\n <mat-icon *ngIf=\"icon && iconPosition === 'start'\" class=\"!cqa-w-[18px] !cqa-h-[18px] !cqa-text-[18px]\" [style.color]=\"iconColor\">\n {{ icon }}\n </mat-icon>\n\n <!-- Dynamic text support -->\n <span *ngIf=\"text\">{{text}}</span>\n\n <ng-content *ngIf=\"!text\" ></ng-content>\n\n <mat-icon *ngIf=\"icon && iconPosition === 'end'\" class=\"!cqa-w-[18px] !cqa-h-[18px] !cqa-text-[18px]\" [style.color]=\"iconColor\">\n {{ icon }}\n </mat-icon>\n\n </button>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
220
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ButtonComponent, decorators: [{
221
+ type: Component,
222
+ args: [{ selector: 'cqa-button', template: "<div id=\"cqa-ui-root\">\n <button\n [ngClass]=\"\n text && icon && iconPosition === 'start' ? 'cqa-pr-[24px] cqa-pl-[16px]' :\n text && icon && iconPosition === 'end' ? 'cqa-pl-[24px] cqa-pr-[16px]' :\n text && !icon ? 'cqa-px-[24px]' : !text && icon ? 'cqa-px-[12px]' : 'cqa-px-[24px]'\"\n [type]=\"type\"\n [disabled]=\"disabled\"\n [attr.aria-disabled]=\"disabled\"\n [class]=\"buttonClasses\"\n (click)=\"onClick($event)\"\n >\n\n <mat-icon *ngIf=\"icon && iconPosition === 'start'\" class=\"!cqa-w-[18px] !cqa-h-[18px] !cqa-text-[18px]\" [style.color]=\"iconColor\">\n {{ icon }}\n </mat-icon>\n\n <!-- Dynamic text support -->\n <span *ngIf=\"text\">{{text}}</span>\n\n <ng-content *ngIf=\"!text\" ></ng-content>\n\n <mat-icon *ngIf=\"icon && iconPosition === 'end'\" class=\"!cqa-w-[18px] !cqa-h-[18px] !cqa-text-[18px]\" [style.color]=\"iconColor\">\n {{ icon }}\n </mat-icon>\n\n </button>\n</div>", styles: [] }]
223
+ }], propDecorators: { variant: [{
224
+ type: Input
225
+ }], disabled: [{
226
+ type: Input
227
+ }], icon: [{
228
+ type: Input
229
+ }], iconPosition: [{
230
+ type: Input
231
+ }], fullWidth: [{
232
+ type: Input
233
+ }], iconColor: [{
234
+ type: Input
235
+ }], type: [{
236
+ type: Input
237
+ }], text: [{
238
+ type: Input
239
+ }], customClass: [{
240
+ type: Input
241
+ }], clicked: [{
242
+ type: Output
243
+ }], onMouseEnter: [{
244
+ type: HostListener,
245
+ args: ['mouseenter']
246
+ }], onMouseLeave: [{
247
+ type: HostListener,
248
+ args: ['mouseleave']
249
+ }], onMouseDown: [{
250
+ type: HostListener,
251
+ args: ['mousedown']
252
+ }], onMouseUp: [{
253
+ type: HostListener,
254
+ args: ['mouseup']
255
+ }], onFocus: [{
256
+ type: HostListener,
257
+ args: ['focus']
258
+ }], onBlur: [{
259
+ type: HostListener,
260
+ args: ['blur']
261
+ }] } });
262
+
263
+ class SearchBarComponent {
264
+ constructor() {
265
+ /** Placeholder text for the input */
266
+ this.placeholder = 'Search';
267
+ /** Initial value or externally controlled value */
268
+ this.value = '';
269
+ /** Disable interactions */
270
+ this.disabled = false;
271
+ /** Whether the clear button should be visible when there is text */
272
+ this.showClear = true;
273
+ /** Accessible label for the input */
274
+ this.ariaLabel = 'Search';
275
+ /** Automatically focus the input when rendered */
276
+ this.autoFocus = false;
277
+ /** Search bar size */
278
+ this.size = 'md';
279
+ /** Stretch to fill container width */
280
+ this.fullWidth = false;
281
+ /** Emit on value changes (e.g. for two-way binding) */
282
+ this.valueChange = new EventEmitter();
283
+ /** Emit when user submits search (Enter key or form submit) */
284
+ this.search = new EventEmitter();
285
+ /** Emit when the value is cleared via the clear button */
286
+ this.cleared = new EventEmitter();
287
+ this.inputValue = '';
288
+ this.widthClasses = {
289
+ sm: 'cqa-w-[295px]',
290
+ md: 'cqa-w-[395px]',
291
+ lg: 'cqa-w-[495px]',
292
+ };
293
+ }
294
+ ngOnChanges(changes) {
295
+ if (changes['value'] && changes['value'].currentValue !== undefined) {
296
+ const newValue = changes['value'].currentValue ?? '';
297
+ if (newValue !== this.inputValue) {
298
+ this.inputValue = newValue;
299
+ }
300
+ }
301
+ }
302
+ onInput(event) {
303
+ const target = event.target;
304
+ const nextValue = target?.value ?? '';
305
+ this.inputValue = nextValue;
306
+ this.valueChange.emit(this.inputValue);
307
+ }
308
+ onSubmit(event) {
309
+ event.preventDefault();
310
+ if (this.disabled) {
311
+ return;
312
+ }
313
+ this.search.emit(this.inputValue.trim());
314
+ }
315
+ clear() {
316
+ if (this.disabled || this.inputValue === '') {
317
+ return;
318
+ }
319
+ this.inputValue = '';
320
+ this.valueChange.emit('');
321
+ this.cleared.emit();
322
+ }
323
+ }
324
+ SearchBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: SearchBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
325
+ SearchBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: SearchBarComponent, selector: "cqa-search-bar", inputs: { placeholder: "placeholder", value: "value", disabled: "disabled", showClear: "showClear", ariaLabel: "ariaLabel", autoFocus: "autoFocus", size: "size", fullWidth: "fullWidth" }, outputs: { valueChange: "valueChange", search: "search", cleared: "cleared" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"cqa-ui-root\" [style.display]=\"fullWidth ? 'block' : 'inline-block'\"\n [style.width]=\"fullWidth ? '100%' : 'auto'\">\n <form\n class=\"cqa-inline-flex cqa-items-center cqa-gap-2 cqa-px-6 cqa-py-3 cqa-text-[14px] cqa-border cqa-border-gray-200 cqa-rounded-md cqa-bg-white cqa-shadow-sm cqa-transition-colors\"\n [ngClass]=\"fullWidth ? 'cqa-w-full' : widthClasses[size]\" (submit)=\"onSubmit($event)\">\n <span class=\"cqa-flex-none cqa-flex cqa-items-center cqa-justify-center cqa-text-gray-400 cqa-w-4 cqa-h-4\"\n [ngClass]=\"{ 'cqa-opacity-[0.38]': disabled }\">\n <mat-icon class=\"cqa-flex cqa-items-center cqa-justify-center cqa-leading-none cqa-p-0\" [style.width.px]=\"16\"\n [style.height.px]=\"16\" [style.fontSize.px]=\"16\">\n search\n </mat-icon>\n </span>\n\n <input\n type=\"text\"\n class=\"cqa-flex-1 cqa-min-w-[180px] cqa-border-none cqa-outline-none cqa-bg-transparent placeholder:cqa-text-gray-400 disabled:cqa-text-gray-400 disabled:cqa-cursor-not-allowed cqa-font-['SF_Pro_Text'] cqa-font-normal cqa-text-[12.3px] cqa-leading-none cqa-tracking-normal cqa-align-middle cqa-text-muted\"\n [placeholder]=\"placeholder\"\n [value]=\"inputValue\"\n (input)=\"onInput($event)\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"ariaLabel\"\n autocomplete=\"off\"\n autocapitalize=\"none\"\n spellcheck=\"false\"\n [attr.autofocus]=\"autoFocus ? '' : null\"\n />\n\n <button *ngIf=\"showClear && inputValue\" type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-p-0 cqa-w-4 cqa-h-4 cqa-border-0 cqa-bg-transparent cqa-cursor-pointer cqa-text-gray-500 cqa-hover:cqa-text-gray-700 disabled:cqa-text-gray-300 cqa-transition-colors cqa-leading-none\"\n (click)=\"clear()\" [disabled]=\"disabled\" aria-label=\"Clear search\">\n <mat-icon class=\"cqa-flex cqa-items-center cqa-justify-center cqa-leading-none cqa-p-0\" [style.width.px]=\"16\"\n [style.height.px]=\"16\" [style.fontSize.px]=\"16\" [ngClass]=\"{ 'cqa-opacity-[0.38]': disabled }\">\n close\n </mat-icon>\n </button>\n </form>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
326
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: SearchBarComponent, decorators: [{
327
+ type: Component,
328
+ args: [{ selector: 'cqa-search-bar', template: "<div id=\"cqa-ui-root\" [style.display]=\"fullWidth ? 'block' : 'inline-block'\"\n [style.width]=\"fullWidth ? '100%' : 'auto'\">\n <form\n class=\"cqa-inline-flex cqa-items-center cqa-gap-2 cqa-px-6 cqa-py-3 cqa-text-[14px] cqa-border cqa-border-gray-200 cqa-rounded-md cqa-bg-white cqa-shadow-sm cqa-transition-colors\"\n [ngClass]=\"fullWidth ? 'cqa-w-full' : widthClasses[size]\" (submit)=\"onSubmit($event)\">\n <span class=\"cqa-flex-none cqa-flex cqa-items-center cqa-justify-center cqa-text-gray-400 cqa-w-4 cqa-h-4\"\n [ngClass]=\"{ 'cqa-opacity-[0.38]': disabled }\">\n <mat-icon class=\"cqa-flex cqa-items-center cqa-justify-center cqa-leading-none cqa-p-0\" [style.width.px]=\"16\"\n [style.height.px]=\"16\" [style.fontSize.px]=\"16\">\n search\n </mat-icon>\n </span>\n\n <input\n type=\"text\"\n class=\"cqa-flex-1 cqa-min-w-[180px] cqa-border-none cqa-outline-none cqa-bg-transparent placeholder:cqa-text-gray-400 disabled:cqa-text-gray-400 disabled:cqa-cursor-not-allowed cqa-font-['SF_Pro_Text'] cqa-font-normal cqa-text-[12.3px] cqa-leading-none cqa-tracking-normal cqa-align-middle cqa-text-muted\"\n [placeholder]=\"placeholder\"\n [value]=\"inputValue\"\n (input)=\"onInput($event)\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"ariaLabel\"\n autocomplete=\"off\"\n autocapitalize=\"none\"\n spellcheck=\"false\"\n [attr.autofocus]=\"autoFocus ? '' : null\"\n />\n\n <button *ngIf=\"showClear && inputValue\" type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-p-0 cqa-w-4 cqa-h-4 cqa-border-0 cqa-bg-transparent cqa-cursor-pointer cqa-text-gray-500 cqa-hover:cqa-text-gray-700 disabled:cqa-text-gray-300 cqa-transition-colors cqa-leading-none\"\n (click)=\"clear()\" [disabled]=\"disabled\" aria-label=\"Clear search\">\n <mat-icon class=\"cqa-flex cqa-items-center cqa-justify-center cqa-leading-none cqa-p-0\" [style.width.px]=\"16\"\n [style.height.px]=\"16\" [style.fontSize.px]=\"16\" [ngClass]=\"{ 'cqa-opacity-[0.38]': disabled }\">\n close\n </mat-icon>\n </button>\n </form>\n</div>", styles: [] }]
329
+ }], propDecorators: { placeholder: [{
330
+ type: Input
331
+ }], value: [{
332
+ type: Input
333
+ }], disabled: [{
334
+ type: Input
335
+ }], showClear: [{
336
+ type: Input
337
+ }], ariaLabel: [{
338
+ type: Input
339
+ }], autoFocus: [{
340
+ type: Input
341
+ }], size: [{
342
+ type: Input
343
+ }], fullWidth: [{
344
+ type: Input
345
+ }], valueChange: [{
346
+ type: Output
347
+ }], search: [{
348
+ type: Output
349
+ }], cleared: [{
350
+ type: Output
351
+ }] } });
352
+
353
+ class SegmentControlComponent {
354
+ constructor() {
355
+ this.segments = [
356
+ { label: 'Tab Group', value: 'tab-group-1' },
357
+ { label: 'Tab Group', value: 'tab-group-2' },
358
+ ];
359
+ this.disabled = false;
360
+ this.valueChange = new EventEmitter();
361
+ this.indicatorStyle = {};
362
+ this.indicatorVisible = false;
363
+ }
364
+ ngOnChanges(changes) {
365
+ if (changes['segments'] || changes['value']) {
366
+ this.ensureSelectedValue();
367
+ }
368
+ }
369
+ ngAfterViewInit() {
370
+ this.buttonChangesSub = this.segmentButtons.changes.subscribe(() => this.updateIndicator());
371
+ this.ensureSelectedValue();
372
+ this.updateIndicator();
373
+ }
374
+ ngOnDestroy() {
375
+ this.buttonChangesSub?.unsubscribe?.();
376
+ }
377
+ trackByValue(_index, option) {
378
+ return option.value;
379
+ }
380
+ isSelected(option) {
381
+ return option.value === this.value;
382
+ }
383
+ select(option, index) {
384
+ if (this.disabled || option.disabled) {
385
+ return;
386
+ }
387
+ const nextValue = option.value;
388
+ if (nextValue !== this.value) {
389
+ this.value = nextValue;
390
+ this.valueChange.emit(nextValue);
391
+ }
392
+ this.focusButton(index);
393
+ this.updateIndicator();
394
+ }
395
+ onKeyDown(event, currentIndex) {
396
+ if (this.disabled) {
397
+ return;
398
+ }
399
+ switch (event.key) {
400
+ case 'ArrowRight':
401
+ case 'ArrowDown':
402
+ event.preventDefault();
403
+ this.moveSelection(1, currentIndex);
404
+ break;
405
+ case 'ArrowLeft':
406
+ case 'ArrowUp':
407
+ event.preventDefault();
408
+ this.moveSelection(-1, currentIndex);
409
+ break;
410
+ case 'Home':
411
+ event.preventDefault();
412
+ this.selectFirstEnabled();
413
+ break;
414
+ case 'End':
415
+ event.preventDefault();
416
+ this.selectLastEnabled();
417
+ break;
418
+ case ' ':
419
+ case 'Enter':
420
+ event.preventDefault();
421
+ this.select(this.segments[currentIndex], currentIndex);
422
+ break;
423
+ default:
424
+ break;
425
+ }
426
+ }
427
+ moveSelection(step, startIndex) {
428
+ const enabledIndexes = this.getEnabledIndexes();
429
+ if (enabledIndexes.length === 0) {
430
+ return;
431
+ }
432
+ const currentEnabledIndex = enabledIndexes.indexOf(startIndex);
433
+ const fallbackIndex = this.getSelectedIndex(enabledIndexes);
434
+ const baseIndex = currentEnabledIndex >= 0 ? currentEnabledIndex : fallbackIndex;
435
+ const nextPosition = (baseIndex + step + enabledIndexes.length) % enabledIndexes.length;
436
+ const targetIndex = enabledIndexes[nextPosition];
437
+ this.select(this.segments[targetIndex], targetIndex);
438
+ }
439
+ selectFirstEnabled() {
440
+ const enabledIndexes = this.getEnabledIndexes();
441
+ if (enabledIndexes.length > 0) {
442
+ const index = enabledIndexes[0];
443
+ this.select(this.segments[index], index);
444
+ this.updateIndicator();
445
+ }
446
+ }
447
+ selectLastEnabled() {
448
+ const enabledIndexes = this.getEnabledIndexes();
449
+ if (enabledIndexes.length > 0) {
450
+ const index = enabledIndexes[enabledIndexes.length - 1];
451
+ this.select(this.segments[index], index);
452
+ this.updateIndicator();
453
+ }
454
+ }
455
+ getEnabledIndexes() {
456
+ return this.segments
457
+ .map((option, index) => ({ option, index }))
458
+ .filter(({ option }) => !option.disabled)
459
+ .map(({ index }) => index);
460
+ }
461
+ getSelectedIndex(enabledIndexes) {
462
+ const current = this.segments.findIndex((option) => option.value === this.value && !option.disabled);
463
+ if (current >= 0) {
464
+ return enabledIndexes.indexOf(current);
465
+ }
466
+ return 0;
467
+ }
468
+ ensureSelectedValue() {
469
+ const enabled = this.segments.filter((option) => !option.disabled);
470
+ if (enabled.length === 0) {
471
+ this.value = undefined;
472
+ return;
473
+ }
474
+ if (!this.value || !this.segments.some((option) => option.value === this.value)) {
475
+ this.value = enabled[0].value;
476
+ this.valueChange.emit(this.value);
477
+ const index = this.segments.indexOf(enabled[0]);
478
+ this.focusButton(index);
479
+ this.updateIndicator(index);
480
+ return;
481
+ }
482
+ const selected = this.segments.find((option) => option.value === this.value);
483
+ if (selected?.disabled) {
484
+ this.value = enabled[0].value;
485
+ this.valueChange.emit(this.value);
486
+ const index = this.segments.indexOf(enabled[0]);
487
+ this.focusButton(index);
488
+ this.updateIndicator(index);
489
+ }
490
+ this.updateIndicator();
491
+ }
492
+ focusButton(index) {
493
+ queueMicrotask(() => {
494
+ const button = this.segmentButtons?.get(index)?.nativeElement;
495
+ button?.focus();
496
+ });
497
+ }
498
+ updateIndicator(preferredIndex) {
499
+ queueMicrotask(() => {
500
+ const container = this.segmentContainer?.nativeElement;
501
+ const buttons = this.segmentButtons?.toArray() ?? [];
502
+ if (!container || buttons.length === 0) {
503
+ this.indicatorVisible = false;
504
+ this.indicatorStyle = {};
505
+ return;
506
+ }
507
+ const index = preferredIndex ?? buttons.findIndex((button, idx) => this.segments[idx]?.value === this.value);
508
+ if (index === -1) {
509
+ this.indicatorVisible = false;
510
+ this.indicatorStyle = {};
511
+ return;
512
+ }
513
+ const buttonEl = buttons[index]?.nativeElement;
514
+ if (!buttonEl) {
515
+ this.indicatorVisible = false;
516
+ this.indicatorStyle = {};
517
+ return;
518
+ }
519
+ const containerRect = container.getBoundingClientRect();
520
+ const buttonRect = buttonEl.getBoundingClientRect();
521
+ const offsetLeft = buttonEl.offsetLeft;
522
+ const offsetTop = buttonEl.offsetTop;
523
+ const width = buttonEl.offsetWidth;
524
+ const height = buttonEl.offsetHeight;
525
+ const isDisabled = this.disabled || this.segments[index]?.disabled;
526
+ this.indicatorStyle = {
527
+ width: `${width}px`,
528
+ height: `${height}px`,
529
+ left: `${offsetLeft}px`,
530
+ top: `${offsetTop}px`,
531
+ backgroundColor: isDisabled ? '#9BA0F4' : '#3F43EE',
532
+ };
533
+ this.indicatorVisible = true;
534
+ });
535
+ }
536
+ get isIndicatorVisible() {
537
+ return this.indicatorVisible;
538
+ }
539
+ }
540
+ SegmentControlComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: SegmentControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
541
+ SegmentControlComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: SegmentControlComponent, selector: "cqa-segment-control", inputs: { segments: "segments", value: "value", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "segmentContainer", first: true, predicate: ["segmentContainer"], descendants: true }, { propertyName: "segmentButtons", predicate: ["segmentButton"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div id=\"cqa-ui-root\" style=\"display: inline-block;\">\n <div\n #segmentContainer\n class=\"cqa-relative cqa-inline-flex cqa-flex-row cqa-items-start cqa-p-[3.5px] cqa-h-[31.5px] cqa-bg-surface-light cqa-rounded-[8px]\"\n role=\"tablist\"\n [attr.aria-disabled]=\"disabled || null\"\n >\n <div\n class=\"cqa-absolute cqa-rounded-[8px] cqa-transition-all cqa-duration-200 cqa-ease-in-out cqa-pointer-events-none\"\n [class.cqa-opacity-0]=\"!isIndicatorVisible\" [ngStyle]=\"indicatorStyle\" aria-hidden=\"true\"></div>\n\n <button *ngFor=\"let segment of segments; index as index; trackBy: trackByValue\" #segmentButton type=\"button\"\n role=\"tab\"\n class=\"cqa-relative cqa-z-10 cqa-flex cqa-flex-col cqa-justify-center cqa-items-center cqa-px-[14px] cqa-py-[3.5px] cqa-h-[25px] cqa-rounded-[8px] cqa-transition-all cqa-duration-200 cqa-ease-in-out cqa-whitespace-nowrap cqa-text-center focus:cqa-outline-none focus-visible:cqa-outline-none focus-visible:cqa-ring-0 focus-visible:cqa-ring-offset-0 cqa-flex-none\"\n [ngClass]=\"{\n 'cqa-text-white cqa-font-medium': isSelected(segment),\n 'cqa-text-muted': !isSelected(segment) && !(disabled || segment.disabled),\n 'cqa-cursor-not-allowed': disabled || segment.disabled,\n 'cqa-text-disabled': (disabled || segment.disabled) && !isSelected(segment),\n 'cqa-hover:cqa-text-black': !isSelected(segment) && !disabled && !segment.disabled\n }\" [disabled]=\"disabled || segment.disabled\" [attr.aria-selected]=\"isSelected(segment)\"\n [attr.tabindex]=\"!disabled && !segment.disabled ? (isSelected(segment) ? 0 : -1) : -1\"\n (click)=\"select(segment, index)\" (keydown)=\"onKeyDown($event, index)\">\n <span\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-h-[18px] cqa-font-['Inter'] cqa-font-normal cqa-text-[12px] cqa-leading-[12px] cqa-text-center cqa-align-middle\">\n {{ segment.label }}\n </span>\n </button>\n </div>\n</div>", directives: [{ type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
542
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: SegmentControlComponent, decorators: [{
543
+ type: Component,
544
+ args: [{ selector: 'cqa-segment-control', template: "<div id=\"cqa-ui-root\" style=\"display: inline-block;\">\n <div\n #segmentContainer\n class=\"cqa-relative cqa-inline-flex cqa-flex-row cqa-items-start cqa-p-[3.5px] cqa-h-[31.5px] cqa-bg-surface-light cqa-rounded-[8px]\"\n role=\"tablist\"\n [attr.aria-disabled]=\"disabled || null\"\n >\n <div\n class=\"cqa-absolute cqa-rounded-[8px] cqa-transition-all cqa-duration-200 cqa-ease-in-out cqa-pointer-events-none\"\n [class.cqa-opacity-0]=\"!isIndicatorVisible\" [ngStyle]=\"indicatorStyle\" aria-hidden=\"true\"></div>\n\n <button *ngFor=\"let segment of segments; index as index; trackBy: trackByValue\" #segmentButton type=\"button\"\n role=\"tab\"\n class=\"cqa-relative cqa-z-10 cqa-flex cqa-flex-col cqa-justify-center cqa-items-center cqa-px-[14px] cqa-py-[3.5px] cqa-h-[25px] cqa-rounded-[8px] cqa-transition-all cqa-duration-200 cqa-ease-in-out cqa-whitespace-nowrap cqa-text-center focus:cqa-outline-none focus-visible:cqa-outline-none focus-visible:cqa-ring-0 focus-visible:cqa-ring-offset-0 cqa-flex-none\"\n [ngClass]=\"{\n 'cqa-text-white cqa-font-medium': isSelected(segment),\n 'cqa-text-muted': !isSelected(segment) && !(disabled || segment.disabled),\n 'cqa-cursor-not-allowed': disabled || segment.disabled,\n 'cqa-text-disabled': (disabled || segment.disabled) && !isSelected(segment),\n 'cqa-hover:cqa-text-black': !isSelected(segment) && !disabled && !segment.disabled\n }\" [disabled]=\"disabled || segment.disabled\" [attr.aria-selected]=\"isSelected(segment)\"\n [attr.tabindex]=\"!disabled && !segment.disabled ? (isSelected(segment) ? 0 : -1) : -1\"\n (click)=\"select(segment, index)\" (keydown)=\"onKeyDown($event, index)\">\n <span\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-h-[18px] cqa-font-['Inter'] cqa-font-normal cqa-text-[12px] cqa-leading-[12px] cqa-text-center cqa-align-middle\">\n {{ segment.label }}\n </span>\n </button>\n </div>\n</div>", styles: [] }]
545
+ }], propDecorators: { segments: [{
546
+ type: Input
547
+ }], value: [{
548
+ type: Input
549
+ }], disabled: [{
550
+ type: Input
551
+ }], valueChange: [{
552
+ type: Output
553
+ }], segmentButtons: [{
554
+ type: ViewChildren,
555
+ args: ['segmentButton']
556
+ }], segmentContainer: [{
557
+ type: ViewChild,
558
+ args: ['segmentContainer']
559
+ }] } });
560
+
561
+ class DialogComponent {
562
+ constructor(viewContainerRef, cdr) {
563
+ this.viewContainerRef = viewContainerRef;
564
+ this.cdr = cdr;
565
+ this.contentAttached = false;
566
+ }
567
+ attachTemplate(template, context) {
568
+ if (!this.portalOutlet) {
569
+ return;
570
+ }
571
+ const templateContext = context ??
572
+ {
573
+ $implicit: this.config?.data,
574
+ data: this.config?.data,
575
+ };
576
+ const portal = new TemplatePortal(template, this.viewContainerRef, templateContext);
577
+ this.portalOutlet.attachTemplatePortal(portal);
578
+ this.markContentAttached();
579
+ }
580
+ attachComponent(component) {
581
+ if (!this.portalOutlet) {
582
+ return undefined;
583
+ }
584
+ const componentRef = this.portalOutlet.attachComponentPortal(component);
585
+ this.markContentAttached();
586
+ return componentRef;
587
+ }
588
+ async onButtonClick(button) {
589
+ const closeOnClick = button.closeOnClick ?? true;
590
+ let handlerResult = undefined;
591
+ if (button.handler) {
592
+ handlerResult = button.handler(this.dialogRef);
593
+ }
594
+ const resolved = handlerResult instanceof Promise ? await handlerResult : handlerResult;
595
+ if (!closeOnClick || resolved === false) {
596
+ return;
597
+ }
598
+ this.dialogRef.close(resolved);
599
+ }
600
+ get buttonAlignmentClass() {
601
+ const alignment = this.config?.buttonAlignment ?? 'right';
602
+ return this.mapAlignmentToClass(alignment);
603
+ }
604
+ get panelClassList() {
605
+ const baseClasses = [
606
+ 'cqa-relative',
607
+ 'cqa-w-full',
608
+ 'cqa-bg-white',
609
+ 'cqa-rounded-2xl',
610
+ 'cqa-shadow-md',
611
+ 'cqa-border',
612
+ 'cqa-border-border-default',
613
+ 'cqa-p-6',
614
+ 'cqa-text-left',
615
+ ];
616
+ const custom = this.config?.panelClass;
617
+ if (!custom) {
618
+ return baseClasses;
619
+ }
620
+ return Array.isArray(custom) ? [...baseClasses, ...custom] : [...baseClasses, custom];
621
+ }
622
+ get panelStyles() {
623
+ return {
624
+ width: this.config?.width,
625
+ maxWidth: this.config?.maxWidth ?? '480px',
626
+ };
627
+ }
628
+ buttonVariant(button) {
629
+ const role = this.normalizeRole(button.role);
630
+ switch (role) {
631
+ case 'secondary':
632
+ return 'outlined';
633
+ case 'text':
634
+ return 'text';
635
+ case 'tonal':
636
+ return 'tonal';
637
+ case 'elevated':
638
+ return 'elevated';
639
+ case 'filled':
640
+ case 'primary':
641
+ case 'warn':
642
+ default:
643
+ return 'filled';
644
+ }
645
+ }
646
+ buttonHostClasses(button) {
647
+ const role = this.normalizeRole(button.role);
648
+ if (role === 'warn') {
649
+ return ['cqa-dialog-btn-warn'];
650
+ }
651
+ return [];
652
+ }
653
+ mapAlignmentToClass(alignment) {
654
+ switch (alignment) {
655
+ case 'left':
656
+ return 'cqa-justify-start';
657
+ case 'center':
658
+ return 'cqa-justify-center';
659
+ case 'right':
660
+ default:
661
+ return 'cqa-justify-end';
662
+ }
663
+ }
664
+ markContentAttached() {
665
+ this.contentAttached = true;
666
+ this.cdr.markForCheck();
667
+ }
668
+ normalizeRole(role) {
669
+ return (role ?? 'secondary').trim().split(/\s+/)[0];
670
+ }
671
+ }
672
+ DialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DialogComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
673
+ DialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DialogComponent, selector: "cqa-dialog", viewQueries: [{ propertyName: "portalOutlet", first: true, predicate: CdkPortalOutlet, descendants: true, static: true }], ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-flex cqa-w-full cqa-justify-center cqa-px-4 sm:cqa-px-6\">\n <div [ngClass]=\"panelClassList\" [ngStyle]=\"panelStyles\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-5\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-3\">\n <h2 class=\"cqa-text-lg cqa-font-semibold cqa-text-dialog\">\n {{ config.title }}\n </h2>\n\n <p *ngIf=\"config.description\" class=\"cqa-text-sm cqa-leading-6 cqa-text-dialog-secondary\">\n {{ config.description }}\n </p>\n\n <div *ngIf=\"config.warning\"\n class=\"cqa-rounded-xl cqa-border cqa-border-red-200 cqa-bg-red-50 cqa-px-4 cqa-py-3 cqa-text-sm cqa-leading-5 cqa-text-red-700\">\n {{ config.warning }}\n </div>\n </div>\n\n <div class=\"cqa-text-sm cqa-text-dialog\" [class.hidden]=\"!contentAttached\">\n <ng-template cdkPortalOutlet></ng-template>\n </div>\n\n <div class=\"cqa-mt-4 cqa-flex cqa-flex-wrap cqa-gap-3\" [ngClass]=\"buttonAlignmentClass\">\n <cqa-button *ngFor=\"let button of config.buttons\" type=\"button\" [variant]=\"buttonVariant(button)\"\n [ngClass]=\"buttonHostClasses(button)\" (clicked)=\"onButtonClick(button)\">\n {{ button.label }}\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n</div>", components: [{ type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "disabled", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass"], outputs: ["clicked"] }], directives: [{ type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
674
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DialogComponent, decorators: [{
675
+ type: Component,
676
+ args: [{ selector: 'cqa-dialog', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-flex cqa-w-full cqa-justify-center cqa-px-4 sm:cqa-px-6\">\n <div [ngClass]=\"panelClassList\" [ngStyle]=\"panelStyles\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-5\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-3\">\n <h2 class=\"cqa-text-lg cqa-font-semibold cqa-text-dialog\">\n {{ config.title }}\n </h2>\n\n <p *ngIf=\"config.description\" class=\"cqa-text-sm cqa-leading-6 cqa-text-dialog-secondary\">\n {{ config.description }}\n </p>\n\n <div *ngIf=\"config.warning\"\n class=\"cqa-rounded-xl cqa-border cqa-border-red-200 cqa-bg-red-50 cqa-px-4 cqa-py-3 cqa-text-sm cqa-leading-5 cqa-text-red-700\">\n {{ config.warning }}\n </div>\n </div>\n\n <div class=\"cqa-text-sm cqa-text-dialog\" [class.hidden]=\"!contentAttached\">\n <ng-template cdkPortalOutlet></ng-template>\n </div>\n\n <div class=\"cqa-mt-4 cqa-flex cqa-flex-wrap cqa-gap-3\" [ngClass]=\"buttonAlignmentClass\">\n <cqa-button *ngFor=\"let button of config.buttons\" type=\"button\" [variant]=\"buttonVariant(button)\"\n [ngClass]=\"buttonHostClasses(button)\" (clicked)=\"onButtonClick(button)\">\n {{ button.label }}\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: [] }]
677
+ }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { portalOutlet: [{
678
+ type: ViewChild,
679
+ args: [CdkPortalOutlet, { static: true }]
680
+ }] } });
681
+
682
+ class DynamicCellTemplateDirective {
683
+ constructor(template) {
684
+ this.template = template;
685
+ }
686
+ }
687
+ DynamicCellTemplateDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicCellTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
688
+ DynamicCellTemplateDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.4.0", type: DynamicCellTemplateDirective, selector: "ng-template[dynamicCell]", inputs: { name: ["dynamicCell", "name"] }, ngImport: i0 });
689
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicCellTemplateDirective, decorators: [{
690
+ type: Directive,
691
+ args: [{
692
+ selector: "ng-template[dynamicCell]"
693
+ }]
694
+ }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; }, propDecorators: { name: [{
695
+ type: Input,
696
+ args: ["dynamicCell"]
697
+ }] } });
698
+ class DynamicHeaderTemplateDirective {
699
+ constructor(template) {
700
+ this.template = template;
701
+ }
702
+ }
703
+ DynamicHeaderTemplateDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicHeaderTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
704
+ DynamicHeaderTemplateDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.4.0", type: DynamicHeaderTemplateDirective, selector: "ng-template[dynamicHeader]", inputs: { name: ["dynamicHeader", "name"] }, ngImport: i0 });
705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicHeaderTemplateDirective, decorators: [{
706
+ type: Directive,
707
+ args: [{
708
+ selector: "ng-template[dynamicHeader]"
709
+ }]
710
+ }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; }, propDecorators: { name: [{
711
+ type: Input,
712
+ args: ["dynamicHeader"]
713
+ }] } });
714
+
715
+ class FullTableLoaderComponent {
716
+ constructor() {
717
+ this.label = 'Loading...';
718
+ }
719
+ }
720
+ FullTableLoaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: FullTableLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
721
+ FullTableLoaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: FullTableLoaderComponent, selector: "cqa-full-table-loader", inputs: { label: "label" }, ngImport: i0, template: "<div\n class=\"table-loading-overlay cqa-absolute cqa-top-0 cqa-bottom-0 cqa-left-0 cqa-right-0 cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-z-[1000]\">\n <div\n class=\"blur-backdrop cqa-absolute cqa-top-0 cqa-bottom-0 cqa-left-0 cqa-right-0 cqa-bg-white/70 cqa-backdrop-blur-[4px] cqa-z-[1]\">\n </div>\n <div\n class=\"loading-spinner cqa-flex cqa-flex-row cqa-items-center cqa-justify-center cqa-gap-3 cqa-text-center cqa-relative cqa-z-20 cqa-bg-white cqa-py-4 cqa-px-6 cqa-rounded-xl cqa-shadow-lg\">\n <svg class=\"cqa-animate-spin cqa-text-primary\" width=\"32\" height=\"32\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\"\n viewBox=\"0 0 24 24\">\n <circle class=\"cqa-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"cqa-opacity-75\" fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\n </path>\n </svg>\n <span class=\"cqa-text-primary\">{{ label }}</span>\n </div>\n</div>\n\n\n" });
722
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: FullTableLoaderComponent, decorators: [{
723
+ type: Component,
724
+ args: [{ selector: 'cqa-full-table-loader', template: "<div\n class=\"table-loading-overlay cqa-absolute cqa-top-0 cqa-bottom-0 cqa-left-0 cqa-right-0 cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-z-[1000]\">\n <div\n class=\"blur-backdrop cqa-absolute cqa-top-0 cqa-bottom-0 cqa-left-0 cqa-right-0 cqa-bg-white/70 cqa-backdrop-blur-[4px] cqa-z-[1]\">\n </div>\n <div\n class=\"loading-spinner cqa-flex cqa-flex-row cqa-items-center cqa-justify-center cqa-gap-3 cqa-text-center cqa-relative cqa-z-20 cqa-bg-white cqa-py-4 cqa-px-6 cqa-rounded-xl cqa-shadow-lg\">\n <svg class=\"cqa-animate-spin cqa-text-primary\" width=\"32\" height=\"32\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\"\n viewBox=\"0 0 24 24\">\n <circle class=\"cqa-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"cqa-opacity-75\" fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\n </path>\n </svg>\n <span class=\"cqa-text-primary\">{{ label }}</span>\n </div>\n</div>\n\n\n", styles: [] }]
725
+ }], propDecorators: { label: [{
726
+ type: Input
727
+ }] } });
728
+
729
+ class TableDataLoaderComponent {
730
+ constructor() {
731
+ this.label = 'Loading...';
732
+ this.size = 24;
733
+ }
734
+ }
735
+ TableDataLoaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TableDataLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
736
+ TableDataLoaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TableDataLoaderComponent, selector: "cqa-table-data-loader", inputs: { label: "label", size: "size" }, ngImport: i0, template: "<div class=\"loading-spinner-simple cqa-py-2 cqa-flex cqa-flex-row cqa-items-center cqa-justify-center cqa-gap-3 cqa-text-center\">\n <svg class=\"cqa-animate-spin cqa-text-primary\" [attr.width]=\"size\" [attr.height]=\"size\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"cqa-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"cqa-opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n <span class=\"cqa-text-primary\">{{ label }}</span>\n </div>\n\n\n" });
737
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TableDataLoaderComponent, decorators: [{
738
+ type: Component,
739
+ args: [{ selector: 'cqa-table-data-loader', template: "<div class=\"loading-spinner-simple cqa-py-2 cqa-flex cqa-flex-row cqa-items-center cqa-justify-center cqa-gap-3 cqa-text-center\">\n <svg class=\"cqa-animate-spin cqa-text-primary\" [attr.width]=\"size\" [attr.height]=\"size\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"cqa-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"cqa-opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n <span class=\"cqa-text-primary\">{{ label }}</span>\n </div>\n\n\n", styles: [] }]
740
+ }], propDecorators: { label: [{
741
+ type: Input
742
+ }], size: [{
743
+ type: Input
744
+ }] } });
745
+
746
+ class DynamicTableComponent {
747
+ constructor() {
748
+ this.data = [];
749
+ this.columns = [];
750
+ // Enable built-in select-all behavior for a 'checkbox' column
751
+ this.enableSelectAll = true;
752
+ // Enable simple client-side sorting when clicking sortable headers (no custom header)
753
+ this.enableLocalSort = true;
754
+ // Emit sort changes so parent can perform server-side sort if desired
755
+ this.sortChange = new EventEmitter();
756
+ this._sortDirection = null;
757
+ }
758
+ get isXs() {
759
+ const w = this.screenWidth || window.innerWidth;
760
+ return w <= 480;
761
+ }
762
+ get isSm() {
763
+ const w = this.screenWidth || window.innerWidth;
764
+ return w <= 768;
765
+ }
766
+ get isMd() {
767
+ const w = this.screenWidth || window.innerWidth;
768
+ return w <= 992;
769
+ }
770
+ get visibleColumns() {
771
+ const responsiveFilter = (c) => {
772
+ const r = c?.responsive || {};
773
+ if (r.xs && this.isXs)
774
+ return false;
775
+ if (r.sm && this.isSm)
776
+ return false;
777
+ if (r.md && this.isMd)
778
+ return false;
779
+ return true;
780
+ };
781
+ return (this.columns || []).filter(c => c.isShow !== false).filter(responsiveFilter);
782
+ }
783
+ getHeaderTemplate(colId) {
784
+ const tpl = this.headerTemplates?.find(t => t.name === colId);
785
+ return tpl ? tpl.template : null;
786
+ }
787
+ getCellTemplate(colId) {
788
+ const tpl = this.cellTemplates?.find(t => t.name === colId);
789
+ return tpl ? tpl.template : null;
790
+ }
791
+ getCellValue(row, path) {
792
+ if (!row || !path)
793
+ return "";
794
+ const parts = path.split(".");
795
+ let current = row;
796
+ for (const part of parts) {
797
+ if (current == null)
798
+ return "";
799
+ current = current[part];
800
+ }
801
+ return current ?? "";
802
+ }
803
+ trackByIndex(index) {
804
+ return index;
805
+ }
806
+ // Compute grid-template-columns string from column config
807
+ get computedGridTemplate() {
808
+ if (this.gridTemplateColumns) {
809
+ return this.gridTemplateColumns;
810
+ }
811
+ const cols = this.visibleColumns;
812
+ if (!cols?.length)
813
+ return '';
814
+ const fixedPx = cols.reduce((sum, c) => sum + (c.fixedPx || 0), 0);
815
+ const dynamicCols = cols.filter(c => !c.fixedPx);
816
+ const totalWeight = dynamicCols.reduce((sum, c) => sum + (c.weight || 1), 0) || 1;
817
+ const parts = cols.map(c => {
818
+ if (c.fixedPx && c.fixedPx > 0) {
819
+ return `${c.fixedPx}px`;
820
+ }
821
+ const share = (c.weight || 1) / totalWeight;
822
+ return `calc((100% - ${fixedPx}px) * ${share.toFixed(4)})`;
823
+ });
824
+ return parts.join(' ');
825
+ }
826
+ // Compute per-column widths for use with <colgroup>
827
+ get computedColumnWidths() {
828
+ const cols = this.visibleColumns;
829
+ if (!cols?.length)
830
+ return [];
831
+ const fixedPx = cols.reduce((sum, c) => sum + (c.fixedPx || 0), 0);
832
+ const dynamicCols = cols.filter(c => !c.fixedPx);
833
+ const totalWeight = dynamicCols.reduce((sum, c) => sum + (c.weight || 1), 0) || 1;
834
+ return cols.map(c => {
835
+ if (c.fixedPx && c.fixedPx > 0) {
836
+ return `${c.fixedPx}px`;
837
+ }
838
+ const share = (c.weight || 1) / totalWeight;
839
+ return `calc((100% - ${fixedPx}px) * ${share.toFixed(4)})`;
840
+ });
841
+ }
842
+ // Selection helpers
843
+ get allSelected() {
844
+ const rows = this.data || [];
845
+ if (!rows.length)
846
+ return false;
847
+ return rows.every(r => !!r?.isSelected);
848
+ }
849
+ get someSelected() {
850
+ const rows = this.data || [];
851
+ if (!rows.length)
852
+ return false;
853
+ const anySelected = rows.some(r => !!r?.isSelected);
854
+ return anySelected && !this.allSelected;
855
+ }
856
+ onSelectAllChange(event) {
857
+ const target = event.target;
858
+ this.toggleSelectAll(target.checked);
859
+ }
860
+ onRowSelectChange(event, row) {
861
+ const target = event.target;
862
+ row.isSelected = target.checked;
863
+ }
864
+ toggleSelectAll(checked) {
865
+ const rows = this.data || [];
866
+ for (const row of rows) {
867
+ if (row) {
868
+ row.isSelected = checked;
869
+ }
870
+ }
871
+ }
872
+ get computedData() {
873
+ const source = this.data || [];
874
+ if (!this.enableLocalSort || !this._sortActive || !this._sortDirection) {
875
+ return source;
876
+ }
877
+ const col = this.visibleColumns.find(c => c.fieldId === this._sortActive);
878
+ if (!col || !col.fieldValue) {
879
+ return source;
880
+ }
881
+ const dir = this._sortDirection === 'asc' ? 1 : -1;
882
+ const fieldPath = col.fieldValue;
883
+ const out = [...source];
884
+ out.sort((a, b) => dir * this.compareValues(this.getCellValue(a, fieldPath), this.getCellValue(b, fieldPath)));
885
+ return out;
886
+ }
887
+ // Computed loading flags to support backward compatibility
888
+ get showTableLoading() {
889
+ return this.isTableLoading ?? false;
890
+ }
891
+ get showTableDataLoading() {
892
+ return this.isTableDataLoading ?? false;
893
+ }
894
+ // True when table has no data and is not currently loading — used to show an empty state
895
+ get isEmpty() {
896
+ const anyLoading = this.showTableLoading || this.showTableDataLoading;
897
+ return !anyLoading && (!(this.data && this.data.length) || this.data.length === 0);
898
+ }
899
+ isSortedAsc(colId) {
900
+ return this._sortActive === colId && this._sortDirection === 'asc';
901
+ }
902
+ isSortedDesc(colId) {
903
+ return this._sortActive === colId && this._sortDirection === 'desc';
904
+ }
905
+ toggleSort(col) {
906
+ if (!col?.sortable)
907
+ return;
908
+ const colId = col.fieldId;
909
+ if (this._sortActive !== colId) {
910
+ this._sortActive = colId;
911
+ this._sortDirection = 'asc';
912
+ }
913
+ else {
914
+ // cycle asc -> desc -> null -> asc
915
+ if (this._sortDirection === 'asc')
916
+ this._sortDirection = 'desc';
917
+ else if (this._sortDirection === 'desc')
918
+ this._sortDirection = null;
919
+ else
920
+ this._sortDirection = 'asc';
921
+ }
922
+ this.sortChange.emit({ fieldId: this._sortActive, fieldValue: col.fieldValue, direction: this._sortDirection });
923
+ }
924
+ compareValues(a, b) {
925
+ if (a == null && b == null)
926
+ return 0;
927
+ if (a == null)
928
+ return 1; // nulls last in asc (handled by dir multiplier)
929
+ if (b == null)
930
+ return -1;
931
+ const numA = typeof a === 'number' ? a : Number(a);
932
+ const numB = typeof b === 'number' ? b : Number(b);
933
+ const aIsNum = !isNaN(numA) && a !== '' && a !== null && a !== false;
934
+ const bIsNum = !isNaN(numB) && b !== '' && b !== null && b !== false;
935
+ if (aIsNum && bIsNum) {
936
+ if (numA < numB)
937
+ return -1;
938
+ if (numA > numB)
939
+ return 1;
940
+ return 0;
941
+ }
942
+ // Date detection: attempt parse when both values are strings and parseable
943
+ if (typeof a === 'string' && typeof b === 'string') {
944
+ const tsA = Date.parse(a);
945
+ const tsB = Date.parse(b);
946
+ if (!isNaN(tsA) && !isNaN(tsB)) {
947
+ if (tsA < tsB)
948
+ return -1;
949
+ if (tsA > tsB)
950
+ return 1;
951
+ return 0;
952
+ }
953
+ // localeCompare, case-insensitive
954
+ return a.localeCompare(b, undefined, { sensitivity: 'base', numeric: true });
955
+ }
956
+ // Fallback to string comparison
957
+ const sa = String(a);
958
+ const sb = String(b);
959
+ return sa.localeCompare(sb, undefined, { sensitivity: 'base', numeric: true });
960
+ }
961
+ }
962
+ DynamicTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
963
+ DynamicTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DynamicTableComponent, selector: "app-dynamic-table", inputs: { data: "data", columns: "columns", emptyState: "emptyState", gridTemplateColumns: "gridTemplateColumns", screenWidth: "screenWidth", enableSelectAll: "enableSelectAll", enableLocalSort: "enableLocalSort", isTableLoading: "isTableLoading", isTableDataLoading: "isTableDataLoading" }, outputs: { sortChange: "sortChange" }, queries: [{ propertyName: "emptyTableTpl", first: true, predicate: ["emptyTableTpl"], descendants: true, read: TemplateRef }, { propertyName: "cellTemplates", predicate: DynamicCellTemplateDirective }, { propertyName: "headerTemplates", predicate: DynamicHeaderTemplateDirective }], ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-relative\">\n <cqa-full-table-loader *ngIf=\"showTableLoading\"></cqa-full-table-loader>\n <cqa-table-data-loader *ngIf=\"showTableDataLoading\"></cqa-table-data-loader>\n <table class=\"table-inner cqa-w-full\" [class.is-loading]=\"true\">\n <colgroup>\n <ng-container *ngFor=\"let width of computedColumnWidths; trackBy: trackByIndex\">\n <col [style.width]=\"width\" />\n </ng-container>\n </colgroup>\n\n <thead *ngIf=\"data?.length\">\n <tr class=\"table-header cqa-items-center\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <th\n class=\"header-cell cqa-py-[13.25px] cqa-px-[10.5px] cqa-text-xs cqa-font-semibold cqa-text-left cqa-text-[#374151] cqa-bg-[#eff0f7]\"\n [ngClass]=\"col.fieldId + '-cell'\">\n <!-- Built-in select-all for checkbox column when enabled and no custom header template -->\n <ng-container\n *ngIf=\"col.fieldId === 'checkbox' && enableSelectAll && !getHeaderTemplate(col.fieldId); else headerTplOrDefault\">\n <div class=\"custom-checkbox\">\n <input type=\"checkbox\" id=\"checkbox\" aria-label=\"Select all rows\" [checked]=\"allSelected\"\n [indeterminate]=\"someSelected\" (change)=\"onSelectAllChange($event)\" class=\"hidden-checkbox\" />\n <label for=\"checkbox\" class=\"custom-checkbox-label\"></label>\n </div>\n </ng-container>\n <ng-template #headerTplOrDefault>\n <ng-container *ngIf=\"getHeaderTemplate(col.fieldId) as headerTpl; else defaultHeader\">\n <ng-container *ngTemplateOutlet=\"headerTpl\"></ng-container>\n </ng-container>\n <ng-template #defaultHeader>\n <ng-container *ngIf=\"col.sortable; else plainHeader\">\n <button type=\"button\" class=\"header-text cqa-inline-flex cqa-items-center cqa-gap-1\"\n (click)=\"toggleSort(col)\" [attr.aria-label]=\"'Sort by ' + (col.fieldName || col.fieldId)\">\n <span class=\"cqa-text-[12.3px] cqa-leading-[17.5px] cqa-text-[#0A0A0A] cqa-font-medium\">{{\n col.fieldName }}</span>\n <span *ngIf=\"isSortedAsc(col.fieldId)\">\u25B2</span>\n <span *ngIf=\"isSortedDesc(col.fieldId)\">\u25BC</span>\n </button>\n </ng-container>\n <ng-template #plainHeader>\n <span class=\"header-text\">{{ col.fieldName }}</span>\n </ng-template>\n </ng-template>\n </ng-template>\n </th>\n </ng-container>\n </tr>\n </thead>\n\n <tbody class=\"table-body cqa-w-full\">\n <tr class=\"table-row cqa-bg-surface-default hover:cqa-bg-surface-hover\" *ngFor=\"let row of computedData; let rowIndex = index; trackBy: trackByIndex\"\n [class.selected]=\"row?.isSelected\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <td class=\"cell cqa-px-[10.5px] cqa-py-[11px]\" [ngClass]=\"col.fieldId + '-cell'\">\n <!-- Built-in checkbox cell when no custom template is provided -->\n <ng-container *ngIf=\"col.fieldId === 'checkbox' && !getCellTemplate(col.fieldId); else regularCell\">\n <div class=\"custom-checkbox\">\n <input\n type=\"checkbox\"\n class=\"hidden-checkbox\"\n [attr.id]=\"'row-checkbox-' + rowIndex\"\n aria-label=\"Select row\"\n [checked]=\"row?.isSelected\"\n (change)=\"onRowSelectChange($event, row)\" />\n <label\n class=\"custom-checkbox-label\"\n [attr.for]=\"'row-checkbox-' + rowIndex\">\n </label>\n </div>\n </ng-container>\n <ng-template #regularCell>\n <ng-container *ngIf=\"getCellTemplate(col.fieldId) as cellTpl; else defaultCell\">\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: {$implicit: row, row: row, value: getCellValue(row, col.fieldValue)}\"></ng-container>\n </ng-container>\n <ng-template #defaultCell>\n <span class=\"cqa-text-xs cqa-text-[#374151]\">{{ getCellValue(row, col.fieldValue) }}</span>\n </ng-template>\n </ng-template>\n </td>\n </ng-container>\n </tr>\n </tbody>\n </table>\n </div>\n</div>", components: [{ type: FullTableLoaderComponent, selector: "cqa-full-table-loader", inputs: ["label"] }, { type: TableDataLoaderComponent, selector: "cqa-table-data-loader", inputs: ["label", "size"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }] });
964
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicTableComponent, decorators: [{
965
+ type: Component,
966
+ args: [{ selector: "app-dynamic-table", template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-relative\">\n <cqa-full-table-loader *ngIf=\"showTableLoading\"></cqa-full-table-loader>\n <cqa-table-data-loader *ngIf=\"showTableDataLoading\"></cqa-table-data-loader>\n <table class=\"table-inner cqa-w-full\" [class.is-loading]=\"true\">\n <colgroup>\n <ng-container *ngFor=\"let width of computedColumnWidths; trackBy: trackByIndex\">\n <col [style.width]=\"width\" />\n </ng-container>\n </colgroup>\n\n <thead *ngIf=\"data?.length\">\n <tr class=\"table-header cqa-items-center\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <th\n class=\"header-cell cqa-py-[13.25px] cqa-px-[10.5px] cqa-text-xs cqa-font-semibold cqa-text-left cqa-text-[#374151] cqa-bg-[#eff0f7]\"\n [ngClass]=\"col.fieldId + '-cell'\">\n <!-- Built-in select-all for checkbox column when enabled and no custom header template -->\n <ng-container\n *ngIf=\"col.fieldId === 'checkbox' && enableSelectAll && !getHeaderTemplate(col.fieldId); else headerTplOrDefault\">\n <div class=\"custom-checkbox\">\n <input type=\"checkbox\" id=\"checkbox\" aria-label=\"Select all rows\" [checked]=\"allSelected\"\n [indeterminate]=\"someSelected\" (change)=\"onSelectAllChange($event)\" class=\"hidden-checkbox\" />\n <label for=\"checkbox\" class=\"custom-checkbox-label\"></label>\n </div>\n </ng-container>\n <ng-template #headerTplOrDefault>\n <ng-container *ngIf=\"getHeaderTemplate(col.fieldId) as headerTpl; else defaultHeader\">\n <ng-container *ngTemplateOutlet=\"headerTpl\"></ng-container>\n </ng-container>\n <ng-template #defaultHeader>\n <ng-container *ngIf=\"col.sortable; else plainHeader\">\n <button type=\"button\" class=\"header-text cqa-inline-flex cqa-items-center cqa-gap-1\"\n (click)=\"toggleSort(col)\" [attr.aria-label]=\"'Sort by ' + (col.fieldName || col.fieldId)\">\n <span class=\"cqa-text-[12.3px] cqa-leading-[17.5px] cqa-text-[#0A0A0A] cqa-font-medium\">{{\n col.fieldName }}</span>\n <span *ngIf=\"isSortedAsc(col.fieldId)\">\u25B2</span>\n <span *ngIf=\"isSortedDesc(col.fieldId)\">\u25BC</span>\n </button>\n </ng-container>\n <ng-template #plainHeader>\n <span class=\"header-text\">{{ col.fieldName }}</span>\n </ng-template>\n </ng-template>\n </ng-template>\n </th>\n </ng-container>\n </tr>\n </thead>\n\n <tbody class=\"table-body cqa-w-full\">\n <tr class=\"table-row cqa-bg-surface-default hover:cqa-bg-surface-hover\" *ngFor=\"let row of computedData; let rowIndex = index; trackBy: trackByIndex\"\n [class.selected]=\"row?.isSelected\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <td class=\"cell cqa-px-[10.5px] cqa-py-[11px]\" [ngClass]=\"col.fieldId + '-cell'\">\n <!-- Built-in checkbox cell when no custom template is provided -->\n <ng-container *ngIf=\"col.fieldId === 'checkbox' && !getCellTemplate(col.fieldId); else regularCell\">\n <div class=\"custom-checkbox\">\n <input\n type=\"checkbox\"\n class=\"hidden-checkbox\"\n [attr.id]=\"'row-checkbox-' + rowIndex\"\n aria-label=\"Select row\"\n [checked]=\"row?.isSelected\"\n (change)=\"onRowSelectChange($event, row)\" />\n <label\n class=\"custom-checkbox-label\"\n [attr.for]=\"'row-checkbox-' + rowIndex\">\n </label>\n </div>\n </ng-container>\n <ng-template #regularCell>\n <ng-container *ngIf=\"getCellTemplate(col.fieldId) as cellTpl; else defaultCell\">\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: {$implicit: row, row: row, value: getCellValue(row, col.fieldValue)}\"></ng-container>\n </ng-container>\n <ng-template #defaultCell>\n <span class=\"cqa-text-xs cqa-text-[#374151]\">{{ getCellValue(row, col.fieldValue) }}</span>\n </ng-template>\n </ng-template>\n </td>\n </ng-container>\n </tr>\n </tbody>\n </table>\n </div>\n</div>", styles: [] }]
967
+ }], propDecorators: { data: [{
968
+ type: Input
969
+ }], columns: [{
970
+ type: Input
971
+ }], emptyState: [{
972
+ type: Input
973
+ }], gridTemplateColumns: [{
974
+ type: Input
975
+ }], screenWidth: [{
976
+ type: Input
977
+ }], enableSelectAll: [{
978
+ type: Input
979
+ }], enableLocalSort: [{
980
+ type: Input
981
+ }], isTableLoading: [{
982
+ type: Input
983
+ }], isTableDataLoading: [{
984
+ type: Input
985
+ }], sortChange: [{
986
+ type: Output
987
+ }], cellTemplates: [{
988
+ type: ContentChildren,
989
+ args: [DynamicCellTemplateDirective]
990
+ }], headerTemplates: [{
991
+ type: ContentChildren,
992
+ args: [DynamicHeaderTemplateDirective]
993
+ }], emptyTableTpl: [{
994
+ type: ContentChild,
995
+ args: ['emptyTableTpl', { read: TemplateRef }]
996
+ }] } });
997
+
998
+ class InlineSortComponent {
999
+ get getToolTip() {
1000
+ if (this.ascending == true)
1001
+ return this.heading?.includes('created_at') ? 'message.common.sort_by.old' : 'message.common.sort_by.ascending';
1002
+ else if (this.ascending == false)
1003
+ return this.heading?.includes('created_at') ? 'message.common.sort_by.new' : 'message.common.sort_by.descending';
1004
+ else
1005
+ return 'message.common.sort';
1006
+ }
1007
+ ngOnChanges() {
1008
+ if (this.ascending != undefined) {
1009
+ this.tooltipDiv?.hide();
1010
+ this.tooltipDiv?.show(200);
1011
+ }
1012
+ }
1013
+ }
1014
+ InlineSortComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: InlineSortComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1015
+ InlineSortComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: InlineSortComponent, selector: "app-inline-sort, cqa-inline-sort", inputs: { ascending: "ascending", heading: "heading" }, viewQueries: [{ propertyName: "tooltipDiv", first: true, predicate: ["tooltipDiv"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
1016
+ <div #tooltipDiv="matTooltip"
1017
+ class="w-fit-content sort-header"
1018
+ [matTooltip]="getToolTip"
1019
+ [matTooltipPosition]="'after'">
1020
+ <span>{{ heading }}</span>
1021
+ <span *ngIf="ascending" class='fa-down-sort'></span>
1022
+ <span *ngIf="ascending==false" class='fa-up-sort'></span>
1023
+ <span *ngIf="ascending==undefined" class='fa-down-sort opaque-50'></span>
1024
+ </div>
1025
+ `, isInline: true, directives: [{ type: i1$2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
1026
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: InlineSortComponent, decorators: [{
1027
+ type: Component,
1028
+ args: [{
1029
+ selector: 'app-inline-sort, cqa-inline-sort',
1030
+ template: `
1031
+ <div #tooltipDiv="matTooltip"
1032
+ class="w-fit-content sort-header"
1033
+ [matTooltip]="getToolTip"
1034
+ [matTooltipPosition]="'after'">
1035
+ <span>{{ heading }}</span>
1036
+ <span *ngIf="ascending" class='fa-down-sort'></span>
1037
+ <span *ngIf="ascending==false" class='fa-up-sort'></span>
1038
+ <span *ngIf="ascending==undefined" class='fa-down-sort opaque-50'></span>
1039
+ </div>
1040
+ `,
1041
+ styles: []
1042
+ }]
1043
+ }], propDecorators: { ascending: [{
1044
+ type: Input
1045
+ }], heading: [{
1046
+ type: Input
1047
+ }], tooltipDiv: [{
1048
+ type: ViewChild,
1049
+ args: ['tooltipDiv']
1050
+ }] } });
1051
+
1052
+ class PaginationComponent {
1053
+ constructor() {
1054
+ this.totalElements = 0;
1055
+ this.pageIndex = 0; // 0-based
1056
+ this.pageSize = 10;
1057
+ this.pageItemCount = 0; // number of items currently rendered on this page
1058
+ this.pageSizeOptions = [10, 20, 40, 60, 80];
1059
+ this.pageIndexChange = new EventEmitter();
1060
+ this.pageSizeChange = new EventEmitter();
1061
+ this.paginate = new EventEmitter();
1062
+ // Local UI state for custom page-size dropdown
1063
+ this.pageSizeOpen = false;
1064
+ this.pagesOption = {
1065
+ placeholder: 'Choose page',
1066
+ disabled: false,
1067
+ multiple: false,
1068
+ searchable: false,
1069
+ options: [
1070
+ { id: 1, name: '10' },
1071
+ { id: 2, name: '20' },
1072
+ { id: 3, name: '30' },
1073
+ { id: 4, name: '40' },
1074
+ ],
1075
+ };
1076
+ }
1077
+ get computedTotalPages() {
1078
+ if (this.totalPages != null && this.totalPages > 0) {
1079
+ return this.totalPages;
1080
+ }
1081
+ if (this.pageSize > 0 && this.totalElements >= 0) {
1082
+ return Math.max(1, Math.ceil(this.totalElements / this.pageSize));
1083
+ }
1084
+ return 0;
1085
+ }
1086
+ getStartItem() {
1087
+ if (!this.totalElements) {
1088
+ return 0;
1089
+ }
1090
+ return this.pageIndex * this.pageSize + 1;
1091
+ }
1092
+ getEndItem() {
1093
+ const end = this.getStartItem() + this.pageItemCount - 1;
1094
+ if (end < 0) {
1095
+ return 0;
1096
+ }
1097
+ return Math.min(end, this.totalElements);
1098
+ }
1099
+ togglePageSizeMenu() {
1100
+ this.pageSizeOpen = !this.pageSizeOpen;
1101
+ }
1102
+ selectPageSize(size) {
1103
+ if (this.pageSize !== size) {
1104
+ this.pageSize = size;
1105
+ this.onPageSizeChange();
1106
+ }
1107
+ this.pageSizeOpen = false;
1108
+ }
1109
+ onPageSizeChange() {
1110
+ this.pageIndex = 0;
1111
+ this.pageSizeChange.emit(this.pageSize);
1112
+ this.pageIndexChange.emit(this.pageIndex);
1113
+ this.paginate.emit({ pageIndex: this.pageIndex, pageSize: this.pageSize });
1114
+ }
1115
+ goToPage(index) {
1116
+ const lastIndex = Math.max(0, this.computedTotalPages - 1);
1117
+ const next = Math.max(0, Math.min(index, lastIndex));
1118
+ if (next === this.pageIndex) {
1119
+ return;
1120
+ }
1121
+ this.pageIndex = next;
1122
+ this.pageIndexChange.emit(this.pageIndex);
1123
+ this.paginate.emit({ pageIndex: this.pageIndex, pageSize: this.pageSize });
1124
+ }
1125
+ }
1126
+ PaginationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: PaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1127
+ PaginationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: PaginationComponent, selector: "cqa-pagination", inputs: { totalElements: "totalElements", totalPages: "totalPages", pageIndex: "pageIndex", pageSize: "pageSize", pageItemCount: "pageItemCount", pageSizeOptions: "pageSizeOptions" }, outputs: { pageIndexChange: "pageIndexChange", pageSizeChange: "pageSizeChange", paginate: "paginate" }, ngImport: i0, template: "<!-- Bottom Pagination -->\n<div id=\"cqa-ui-root\" >\n <div class=\"table-footer-pagination cqa-text-grey-300 cqa-text-[12.3px] cqa-leading-[17.5px] cqa-flex cqa-items-center cqa-justify-between cqa-gap-2 cqa-flex-wrap cqa-bg-surface-default cqa-px-[21px] cqa-py-[15px]\" *ngIf=\"pageItemCount && totalElements\">\n <div class=\"pagination-info cqa-flex cqa-items-center cqa-gap-[7px] cqa-relative\">\n <span class=\"rows-label\">Rows per page</span>\n <div class=\"cqa-relative\">\n <!-- Custom Select Trigger -->\n <button\n type=\"button\"\n class=\"cqa-inline-flex cqa-items-center cqa-gap-[19px] cqa-bg-white cqa-text-black-100 cqa-rounded-[5px] cqa-px-[11.5px] cqa-py-[6.75px]\"\n (click)=\"togglePageSizeMenu()\"\n [attr.aria-expanded]=\"pageSizeOpen\"\n aria-haspopup=\"listbox\"\n >\n {{ pageSize }}\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g opacity=\"0.5\"><path d=\"M3.5 5.25L7 8.75L10.5 5.25\" stroke=\"#717182\" stroke-width=\"1.16667\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></g></svg>\n </button>\n <!-- Dropdown Menu -->\n <div\n *ngIf=\"pageSizeOpen\"\n class=\"cqa-absolute cqa-z-[100] cqa-bottom-[calc(100%+8px)] cqa-left-0 cqa-w-[75px] cqa-max-h-[170px] cqa-overflow-auto cqa-rounded-lg cqa-border cqa-border-[#E5E7EB] cqa-bg-white cqa-shadow-[0px_4px_6px_-1px_rgba(0,0,0,0.1)] cqa-p-[5px]\"\n role=\"listbox\"\n [attr.aria-activedescendant]=\"'pagesize-' + pageSize\"\n >\n <button\n *ngFor=\"let size of pageSizeOptions\"\n type=\"button\"\n class=\"cqa-w-full cqa-px-2 cqa-py-[6px] hover:cqa-bg-[#F7F8FA] cqa-text-left cqa-rounded-md cqa-text-black-100\"\n [attr.id]=\"'pagesize-' + size\"\n role=\"option\"\n [attr.aria-selected]=\"pageSize === size\"\n (click)=\"selectPageSize(size)\"\n >\n {{ size }}\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"pagination-right cqa-flex cqa-items-center cqa-gap-[21px]\">\n <div class=\"pagination-range\">\n {{ getStartItem() }}&ndash;{{ getEndItem() }} of {{ totalElements }}\n </div>\n <div class=\"pagination-controls cqa-flex cqa-items-stretch cqa-gap-[3.5px]\">\n <button class=\"pagination-btn cqa-w-[28px] cqa-h-[28px] cqa-min-w-[28px] cqa-rounded-[5px] cqa-border cqa-border-[#E5E7EB] cqa-bg-[#F7F8FA] cqa-flex cqa-items-center cqa-justify-center\" [disabled]=\"pageIndex === 0\" (click)=\"goToPage(pageIndex - 1)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8.75 10.5L5.25 7L8.75 3.5\" stroke=\"#838384\" stroke-width=\"1.16667\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n </button>\n <button class=\"pagination-btn cqa-w-[28px] cqa-h-[28px] cqa-min-w-[28px] cqa-rounded-[5px] cqa-border cqa-border-[#E5E7EB] cqa-bg-[#F7F8FA] cqa-flex cqa-items-center cqa-justify-center\" [disabled]=\"pageIndex >= computedTotalPages - 1\" (click)=\"goToPage(pageIndex + 1)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M5.25 10.5L8.75 7L5.25 3.5\" stroke=\"#838384\" stroke-width=\"1.16667\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n </button>\n </div>\n </div>\n </div>\n</div>", directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1128
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: PaginationComponent, decorators: [{
1129
+ type: Component,
1130
+ args: [{ selector: 'cqa-pagination', template: "<!-- Bottom Pagination -->\n<div id=\"cqa-ui-root\" >\n <div class=\"table-footer-pagination cqa-text-grey-300 cqa-text-[12.3px] cqa-leading-[17.5px] cqa-flex cqa-items-center cqa-justify-between cqa-gap-2 cqa-flex-wrap cqa-bg-surface-default cqa-px-[21px] cqa-py-[15px]\" *ngIf=\"pageItemCount && totalElements\">\n <div class=\"pagination-info cqa-flex cqa-items-center cqa-gap-[7px] cqa-relative\">\n <span class=\"rows-label\">Rows per page</span>\n <div class=\"cqa-relative\">\n <!-- Custom Select Trigger -->\n <button\n type=\"button\"\n class=\"cqa-inline-flex cqa-items-center cqa-gap-[19px] cqa-bg-white cqa-text-black-100 cqa-rounded-[5px] cqa-px-[11.5px] cqa-py-[6.75px]\"\n (click)=\"togglePageSizeMenu()\"\n [attr.aria-expanded]=\"pageSizeOpen\"\n aria-haspopup=\"listbox\"\n >\n {{ pageSize }}\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g opacity=\"0.5\"><path d=\"M3.5 5.25L7 8.75L10.5 5.25\" stroke=\"#717182\" stroke-width=\"1.16667\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></g></svg>\n </button>\n <!-- Dropdown Menu -->\n <div\n *ngIf=\"pageSizeOpen\"\n class=\"cqa-absolute cqa-z-[100] cqa-bottom-[calc(100%+8px)] cqa-left-0 cqa-w-[75px] cqa-max-h-[170px] cqa-overflow-auto cqa-rounded-lg cqa-border cqa-border-[#E5E7EB] cqa-bg-white cqa-shadow-[0px_4px_6px_-1px_rgba(0,0,0,0.1)] cqa-p-[5px]\"\n role=\"listbox\"\n [attr.aria-activedescendant]=\"'pagesize-' + pageSize\"\n >\n <button\n *ngFor=\"let size of pageSizeOptions\"\n type=\"button\"\n class=\"cqa-w-full cqa-px-2 cqa-py-[6px] hover:cqa-bg-[#F7F8FA] cqa-text-left cqa-rounded-md cqa-text-black-100\"\n [attr.id]=\"'pagesize-' + size\"\n role=\"option\"\n [attr.aria-selected]=\"pageSize === size\"\n (click)=\"selectPageSize(size)\"\n >\n {{ size }}\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"pagination-right cqa-flex cqa-items-center cqa-gap-[21px]\">\n <div class=\"pagination-range\">\n {{ getStartItem() }}&ndash;{{ getEndItem() }} of {{ totalElements }}\n </div>\n <div class=\"pagination-controls cqa-flex cqa-items-stretch cqa-gap-[3.5px]\">\n <button class=\"pagination-btn cqa-w-[28px] cqa-h-[28px] cqa-min-w-[28px] cqa-rounded-[5px] cqa-border cqa-border-[#E5E7EB] cqa-bg-[#F7F8FA] cqa-flex cqa-items-center cqa-justify-center\" [disabled]=\"pageIndex === 0\" (click)=\"goToPage(pageIndex - 1)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8.75 10.5L5.25 7L8.75 3.5\" stroke=\"#838384\" stroke-width=\"1.16667\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n </button>\n <button class=\"pagination-btn cqa-w-[28px] cqa-h-[28px] cqa-min-w-[28px] cqa-rounded-[5px] cqa-border cqa-border-[#E5E7EB] cqa-bg-[#F7F8FA] cqa-flex cqa-items-center cqa-justify-center\" [disabled]=\"pageIndex >= computedTotalPages - 1\" (click)=\"goToPage(pageIndex + 1)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M5.25 10.5L8.75 7L5.25 3.5\" stroke=\"#838384\" stroke-width=\"1.16667\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n </button>\n </div>\n </div>\n </div>\n</div>", styles: [] }]
1131
+ }], propDecorators: { totalElements: [{
1132
+ type: Input
1133
+ }], totalPages: [{
1134
+ type: Input
1135
+ }], pageIndex: [{
1136
+ type: Input
1137
+ }], pageSize: [{
1138
+ type: Input
1139
+ }], pageItemCount: [{
1140
+ type: Input
1141
+ }], pageSizeOptions: [{
1142
+ type: Input
1143
+ }], pageIndexChange: [{
1144
+ type: Output
1145
+ }], pageSizeChange: [{
1146
+ type: Output
1147
+ }], paginate: [{
1148
+ type: Output
1149
+ }] } });
1150
+
1151
+ class ActionMenuButtonComponent {
1152
+ constructor() {
1153
+ this.view = new EventEmitter();
1154
+ this.edit = new EventEmitter();
1155
+ this.delete = new EventEmitter();
1156
+ }
1157
+ navigateToTestCase(id) {
1158
+ if (id === undefined || id === null)
1159
+ return;
1160
+ this.view.emit(id);
1161
+ }
1162
+ editTestCase(row) {
1163
+ if (!row)
1164
+ return;
1165
+ this.edit.emit(row);
1166
+ }
1167
+ deleteTestCase(row) {
1168
+ if (!row)
1169
+ return;
1170
+ this.delete.emit(row);
1171
+ }
1172
+ }
1173
+ ActionMenuButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ActionMenuButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1174
+ ActionMenuButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ActionMenuButtonComponent, selector: "cqa-action-menu-button", inputs: { row: "row" }, outputs: { view: "view", edit: "edit", delete: "delete" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <button class=\"action-menu-btn\" [matMenuTriggerFor]=\"actionMenu\" (click)=\"$event.stopPropagation()\" mat-icon-button\n type=\"button\" aria-label=\"More actions\">\n <mat-icon>more_horiz</mat-icon>\n </button>\n\n <mat-menu #actionMenu=\"matMenu\" class=\"action-menu\" xPosition=\"after\" yPosition=\"below\" hasBackdrop=\"true\">\n <button mat-menu-item (click)=\"navigateToTestCase(row?.id)\">\n <mat-icon>visibility</mat-icon>\n <span>View</span>\n </button>\n <button mat-menu-item (click)=\"editTestCase(row)\">\n <mat-icon>edit_square</mat-icon>\n <span>Edit</span>\n </button>\n <button mat-menu-item (click)=\"deleteTestCase(row)\" class=\"delete-menu-item\">\n <mat-icon>delete</mat-icon>\n <span>Delete</span>\n </button>\n </mat-menu>\n</div>", components: [{ type: i1$3.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: i3$1.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { type: i3$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }], directives: [{ type: i3$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }] });
1175
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ActionMenuButtonComponent, decorators: [{
1176
+ type: Component,
1177
+ args: [{ selector: 'cqa-action-menu-button', template: "<div id=\"cqa-ui-root\">\n <button class=\"action-menu-btn\" [matMenuTriggerFor]=\"actionMenu\" (click)=\"$event.stopPropagation()\" mat-icon-button\n type=\"button\" aria-label=\"More actions\">\n <mat-icon>more_horiz</mat-icon>\n </button>\n\n <mat-menu #actionMenu=\"matMenu\" class=\"action-menu\" xPosition=\"after\" yPosition=\"below\" hasBackdrop=\"true\">\n <button mat-menu-item (click)=\"navigateToTestCase(row?.id)\">\n <mat-icon>visibility</mat-icon>\n <span>View</span>\n </button>\n <button mat-menu-item (click)=\"editTestCase(row)\">\n <mat-icon>edit_square</mat-icon>\n <span>Edit</span>\n </button>\n <button mat-menu-item (click)=\"deleteTestCase(row)\" class=\"delete-menu-item\">\n <mat-icon>delete</mat-icon>\n <span>Delete</span>\n </button>\n </mat-menu>\n</div>", styles: [] }]
1178
+ }], propDecorators: { row: [{
1179
+ type: Input
1180
+ }], view: [{
1181
+ type: Output
1182
+ }], edit: [{
1183
+ type: Output
1184
+ }], delete: [{
1185
+ type: Output
1186
+ }] } });
1187
+
1188
+ class OtherButtonComponent {
1189
+ constructor() {
1190
+ // Single button API (backwards compatible)
1191
+ this.icon = '';
1192
+ this.label = '';
1193
+ this.classes = '';
1194
+ this.colorClass = '';
1195
+ this.buttonClass = '';
1196
+ this.disabled = false;
1197
+ this.type = 'button';
1198
+ // Group buttons API (new)
1199
+ this.buttons = null;
1200
+ /** Extra classes for the button group container */
1201
+ this.groupClass = '';
1202
+ /** Gap utility class; defaults to Tailwind spacing applied in template */
1203
+ this.gapClass = 'cqa-gap-2';
1204
+ /** When true, allows wrapping to next line on smaller screens */
1205
+ this.wrap = true;
1206
+ this.clicked = new EventEmitter();
1207
+ /** Emits the config of the clicked button in a group, along with the event */
1208
+ this.buttonClick = new EventEmitter();
1209
+ }
1210
+ onClick(event) {
1211
+ if (this.disabled) {
1212
+ event.preventDefault();
1213
+ event.stopPropagation();
1214
+ return;
1215
+ }
1216
+ this.clicked.emit(event);
1217
+ }
1218
+ onItemClick(event, item) {
1219
+ if (item?.disabled) {
1220
+ event.preventDefault();
1221
+ event.stopPropagation();
1222
+ return;
1223
+ }
1224
+ this.buttonClick.emit({ event, item });
1225
+ }
1226
+ }
1227
+ OtherButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: OtherButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1228
+ OtherButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: OtherButtonComponent, selector: "cqa-other-button", inputs: { icon: "icon", label: "label", classes: "classes", colorClass: "colorClass", buttonClass: "buttonClass", disabled: "disabled", type: "type", buttons: "buttons", groupClass: "groupClass", gapClass: "gapClass", wrap: "wrap" }, outputs: { clicked: "clicked", buttonClick: "buttonClick" }, ngImport: i0, template: "<div id=\"cqa-ui-root\" >\n <!-- Group rendering if buttons are provided -->\n <ng-container *ngIf=\"buttons?.length; else singleButton\">\n <div class=\"cqa-inline-flex cqa-items-center\" [ngClass]=\"[wrap ? 'cqa-flex-wrap' : '', gapClass, groupClass]\">\n <button *ngFor=\"let b of buttons\"\n [type]=\"b.type || 'button'\"\n class=\"cqa-inline-flex cqa-items-center cqa-gap-1 cqa-py-[5.75px] cqa-px-[11.5px] cqa-rounded-[5px] cqa-text-[12.3px] cqa-leading-[17.5px] cqa-font-medium cqa-border\"\n [ngClass]=\"[b.classes || classes, buttonClass, b.colorClass || colorClass]\"\n [disabled]=\"b.disabled\"\n (click)=\"onItemClick($event, b)\">\n <mat-icon *ngIf=\"b.icon || icon\" class=\"!cqa-w-[16px] !cqa-h-[16px] !cqa-text-[16px]\">{{ b.icon || icon }}</mat-icon>\n <span>{{ b.label }}</span>\n </button>\n </div>\n </ng-container>\n\n <!-- Single button (backwards compatible) -->\n <ng-template #singleButton>\n <button [type]=\"type\"\n class=\"cqa-inline-flex cqa-items-center cqa-gap-1 cqa-py-[5.75px] cqa-px-[11.5px] cqa-rounded-[5px] cqa-text-[12.3px] cqa-leading-[17.5px] cqa-font-medium cqa-border\"\n [ngClass]=\"[classes, buttonClass, colorClass]\" [disabled]=\"disabled\" (click)=\"onClick($event)\">\n <mat-icon *ngIf=\"icon\" class=\"!cqa-w-[16px] !cqa-h-[16px] !cqa-text-[16px]\">{{ icon }}</mat-icon>\n <span>{{ label }}</span>\n </button>\n </ng-template>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1229
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: OtherButtonComponent, decorators: [{
1230
+ type: Component,
1231
+ args: [{ selector: 'cqa-other-button', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\" >\n <!-- Group rendering if buttons are provided -->\n <ng-container *ngIf=\"buttons?.length; else singleButton\">\n <div class=\"cqa-inline-flex cqa-items-center\" [ngClass]=\"[wrap ? 'cqa-flex-wrap' : '', gapClass, groupClass]\">\n <button *ngFor=\"let b of buttons\"\n [type]=\"b.type || 'button'\"\n class=\"cqa-inline-flex cqa-items-center cqa-gap-1 cqa-py-[5.75px] cqa-px-[11.5px] cqa-rounded-[5px] cqa-text-[12.3px] cqa-leading-[17.5px] cqa-font-medium cqa-border\"\n [ngClass]=\"[b.classes || classes, buttonClass, b.colorClass || colorClass]\"\n [disabled]=\"b.disabled\"\n (click)=\"onItemClick($event, b)\">\n <mat-icon *ngIf=\"b.icon || icon\" class=\"!cqa-w-[16px] !cqa-h-[16px] !cqa-text-[16px]\">{{ b.icon || icon }}</mat-icon>\n <span>{{ b.label }}</span>\n </button>\n </div>\n </ng-container>\n\n <!-- Single button (backwards compatible) -->\n <ng-template #singleButton>\n <button [type]=\"type\"\n class=\"cqa-inline-flex cqa-items-center cqa-gap-1 cqa-py-[5.75px] cqa-px-[11.5px] cqa-rounded-[5px] cqa-text-[12.3px] cqa-leading-[17.5px] cqa-font-medium cqa-border\"\n [ngClass]=\"[classes, buttonClass, colorClass]\" [disabled]=\"disabled\" (click)=\"onClick($event)\">\n <mat-icon *ngIf=\"icon\" class=\"!cqa-w-[16px] !cqa-h-[16px] !cqa-text-[16px]\">{{ icon }}</mat-icon>\n <span>{{ label }}</span>\n </button>\n </ng-template>\n</div>", styles: [] }]
1232
+ }], propDecorators: { icon: [{
1233
+ type: Input
1234
+ }], label: [{
1235
+ type: Input
1236
+ }], classes: [{
1237
+ type: Input
1238
+ }], colorClass: [{
1239
+ type: Input
1240
+ }], buttonClass: [{
1241
+ type: Input
1242
+ }], disabled: [{
1243
+ type: Input
1244
+ }], type: [{
1245
+ type: Input
1246
+ }], buttons: [{
1247
+ type: Input
1248
+ }], groupClass: [{
1249
+ type: Input
1250
+ }], gapClass: [{
1251
+ type: Input
1252
+ }], wrap: [{
1253
+ type: Input
1254
+ }], clicked: [{
1255
+ type: Output
1256
+ }], buttonClick: [{
1257
+ type: Output
1258
+ }] } });
1259
+
1260
+ class DynamicSelectFieldComponent {
1261
+ constructor() {
1262
+ // Must be public for template access in Angular's strict template checking mode
1263
+ this.searchTextByKey = {};
1264
+ }
1265
+ ngOnInit() {
1266
+ if (!this.config || !this.config.key) {
1267
+ throw new Error('cqa-dynamic-select: input "config.key" is required.');
1268
+ }
1269
+ }
1270
+ ngOnChanges(changes) {
1271
+ if ('config' in changes) {
1272
+ // When config changes (including toggling multiple), ensure control value shape matches
1273
+ this.syncControlValueForMultipleMode();
1274
+ }
1275
+ }
1276
+ get panelClass() {
1277
+ return `ctc-select-panel ${this.isMultiple ? 'multiple' : ''}`.trim();
1278
+ }
1279
+ get isMultiple() {
1280
+ return this.toBoolean(this.config?.multiple);
1281
+ }
1282
+ get isDisabled() {
1283
+ return this.toBoolean(this.config?.disabled);
1284
+ }
1285
+ toBoolean(value) {
1286
+ if (typeof value === 'string') {
1287
+ const v = value.trim().toLowerCase();
1288
+ if (v === 'true' || v === '1')
1289
+ return true;
1290
+ if (v === 'false' || v === '0' || v === '')
1291
+ return false;
1292
+ return true; // any other non-empty string treated as truthy
1293
+ }
1294
+ if (typeof value === 'number') {
1295
+ return value !== 0;
1296
+ }
1297
+ return !!value;
1298
+ }
1299
+ syncControlValueForMultipleMode() {
1300
+ const key = this.config?.key;
1301
+ if (!key || !this.form)
1302
+ return;
1303
+ const control = this.form.get(key);
1304
+ if (!control)
1305
+ return;
1306
+ const currentValue = control.value;
1307
+ if (this.isMultiple) {
1308
+ if (currentValue == null)
1309
+ return;
1310
+ if (Array.isArray(currentValue))
1311
+ return;
1312
+ control.setValue([currentValue], { emitEvent: false });
1313
+ }
1314
+ else {
1315
+ if (!Array.isArray(currentValue))
1316
+ return;
1317
+ control.setValue(currentValue.length ? currentValue[0] : null, { emitEvent: false });
1318
+ }
1319
+ }
1320
+ onSelectOpenedChange(opened, _select) {
1321
+ if (!opened) {
1322
+ // Reset search text on close so the next open shows full list
1323
+ if (this.config?.key) {
1324
+ this.searchTextByKey[this.config.key] = '';
1325
+ }
1326
+ // Ensure any previous custom listeners are cleared (legacy compatibility)
1327
+ if (this.outsideCleanup) {
1328
+ this.outsideCleanup();
1329
+ this.outsideCleanup = undefined;
1330
+ }
1331
+ return;
1332
+ }
1333
+ // Focus the search box if enabled
1334
+ if (this.config?.searchable) {
1335
+ setTimeout(() => {
1336
+ const input = document.querySelector('.ts-select-search-input');
1337
+ input?.focus();
1338
+ }, 0);
1339
+ }
1340
+ }
1341
+ onSearch(key, value) {
1342
+ this.searchTextByKey[key] = value ?? '';
1343
+ }
1344
+ filteredOptions(c) {
1345
+ const t = (this.searchTextByKey[c.key] || '').toLowerCase().trim();
1346
+ if (!t)
1347
+ return c.options || [];
1348
+ return (c.options || []).filter((opt) => {
1349
+ const text = String(opt.name ?? opt.label ?? opt.value ?? '').toLowerCase();
1350
+ return text.includes(t);
1351
+ });
1352
+ }
1353
+ // Close when an option is selected if requested. Always close for single-select.
1354
+ onOptionSelected(select) {
1355
+ // Let Angular Material auto-close for single-select.
1356
+ // For multi-select, close only if explicitly requested.
1357
+ const shouldClose = this.isMultiple ? !!this.config?.closeOnSelect : false;
1358
+ if (shouldClose) {
1359
+ try {
1360
+ select.close();
1361
+ }
1362
+ catch { }
1363
+ }
1364
+ // If searchable, clear the search after selection so reopening works predictably
1365
+ if (this.config?.searchable && this.config?.key) {
1366
+ this.searchTextByKey[this.config.key] = '';
1367
+ }
1368
+ }
1369
+ handleDocumentClick(event) {
1370
+ // Close when clicking outside of the trigger and outside of the open panel
1371
+ if (!this.selectRef?.panelOpen) {
1372
+ return;
1373
+ }
1374
+ const target = event.target;
1375
+ if (!target)
1376
+ return;
1377
+ // If click is inside the component host, ignore
1378
+ if (this.hostEl?.nativeElement && this.hostEl.nativeElement.contains(target)) {
1379
+ return;
1380
+ }
1381
+ // If click is inside any open mat-select panel, ignore
1382
+ const panelEls = Array.from(document.querySelectorAll('.mat-select-panel'));
1383
+ const clickInsidePanel = panelEls.some((el) => el.contains(target));
1384
+ if (clickInsidePanel) {
1385
+ return;
1386
+ }
1387
+ try {
1388
+ this.selectRef.close();
1389
+ }
1390
+ catch { }
1391
+ }
1392
+ }
1393
+ DynamicSelectFieldComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1394
+ DynamicSelectFieldComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: { form: "form", config: "config" }, host: { listeners: { "document:click": "handleDocumentClick($event)" } }, viewQueries: [{ propertyName: "selectRef", first: true, predicate: ["selectRef"], descendants: true }, { propertyName: "hostEl", first: true, predicate: ["host"], descendants: true, read: ElementRef }], usesOnChanges: true, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <ng-container [formGroup]=\"form\">\n <label *ngIf=\"config.label\"\n class=\"form-label cqa-text-[#374151] cqa-text-[14px] cqa-font-medium cqa-block cqa-leading-[1.4] cqa-mb-2\">{{\n config.label }}</label>\n <mat-form-field #host class=\"mat-select-custom cqa-w-full\" appearance=\"fill\">\n <mat-select #selectRef=\"matSelect\" [placeholder]=\"config.placeholder\" [disabled]=\"isDisabled\" [multiple]=\"isMultiple\"\n disableOptionCentering [panelClass]=\"panelClass\" [formControlName]=\"config.key\"\n (openedChange)=\"onSelectOpenedChange($event, selectRef)\" (selectionChange)=\"onOptionSelected(selectRef)\">\n\n <mat-option *ngIf=\"config.searchable\" class=\"ts-select-search\" disabled>\n <input class=\"ts-select-search-input\" type=\"text\" [value]=\"searchTextByKey[config.key] || ''\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\"\n (keydown)=\"$event.stopPropagation()\" (input)=\"onSearch(config.key, $any($event.target).value)\"\n placeholder=\"Search...\" />\n </mat-option>\n\n <mat-option *ngFor=\"let opt of filteredOptions(config)\" [value]=\"opt.id ?? opt.value\"\n [textContent]=\"opt.name ?? opt.label ?? opt.value\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </mat-option>\n </mat-select>\n\n <div>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6L8 10L12 6\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n </mat-form-field>\n </ng-container>\n</div>", components: [{ type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { type: i2$1.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { type: i3$3.MatOption, selector: "mat-option", exportAs: ["matOption"] }], directives: [{ type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1395
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicSelectFieldComponent, decorators: [{
1396
+ type: Component,
1397
+ args: [{ selector: 'cqa-dynamic-select', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <ng-container [formGroup]=\"form\">\n <label *ngIf=\"config.label\"\n class=\"form-label cqa-text-[#374151] cqa-text-[14px] cqa-font-medium cqa-block cqa-leading-[1.4] cqa-mb-2\">{{\n config.label }}</label>\n <mat-form-field #host class=\"mat-select-custom cqa-w-full\" appearance=\"fill\">\n <mat-select #selectRef=\"matSelect\" [placeholder]=\"config.placeholder\" [disabled]=\"isDisabled\" [multiple]=\"isMultiple\"\n disableOptionCentering [panelClass]=\"panelClass\" [formControlName]=\"config.key\"\n (openedChange)=\"onSelectOpenedChange($event, selectRef)\" (selectionChange)=\"onOptionSelected(selectRef)\">\n\n <mat-option *ngIf=\"config.searchable\" class=\"ts-select-search\" disabled>\n <input class=\"ts-select-search-input\" type=\"text\" [value]=\"searchTextByKey[config.key] || ''\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\"\n (keydown)=\"$event.stopPropagation()\" (input)=\"onSearch(config.key, $any($event.target).value)\"\n placeholder=\"Search...\" />\n </mat-option>\n\n <mat-option *ngFor=\"let opt of filteredOptions(config)\" [value]=\"opt.id ?? opt.value\"\n [textContent]=\"opt.name ?? opt.label ?? opt.value\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </mat-option>\n </mat-select>\n\n <div>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6L8 10L12 6\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n </mat-form-field>\n </ng-container>\n</div>", styles: [] }]
1398
+ }], propDecorators: { form: [{
1399
+ type: Input
1400
+ }], config: [{
1401
+ type: Input
1402
+ }], selectRef: [{
1403
+ type: ViewChild,
1404
+ args: ['selectRef', { static: false }]
1405
+ }], hostEl: [{
1406
+ type: ViewChild,
1407
+ args: ['host', { static: false, read: ElementRef }]
1408
+ }], handleDocumentClick: [{
1409
+ type: HostListener,
1410
+ args: ['document:click', ['$event']]
1411
+ }] } });
1412
+
1413
+ class DynamicFilterComponent {
1414
+ constructor(fb) {
1415
+ this.fb = fb;
1416
+ this.config = [];
1417
+ this.model = {};
1418
+ this.showFilterPanel = true;
1419
+ this.filtersApplied = new EventEmitter();
1420
+ this.filtersChanged = new EventEmitter();
1421
+ this.resetAction = new EventEmitter();
1422
+ this.form = this.fb.group({});
1423
+ this.maxDate = new Date();
1424
+ this.searchTextByKey = {};
1425
+ this.selectOutsideCleanup = new Map();
1426
+ }
1427
+ ngOnChanges(changes) {
1428
+ if (changes['config'] || changes['model']) {
1429
+ this.buildForm();
1430
+ }
1431
+ }
1432
+ onDateChange(event, key) {
1433
+ const formGroup = this.getDateGroup(key);
1434
+ const start = formGroup.get('start')?.value;
1435
+ const end = formGroup.get('end')?.value;
1436
+ // When both dates are selected, auto-apply
1437
+ // if (start && end) {
1438
+ // this.applyDateRange(key, { start, end });
1439
+ // }
1440
+ }
1441
+ buildForm() {
1442
+ const ctrls = {};
1443
+ (this.config || []).forEach(c => {
1444
+ if (c.hidden)
1445
+ return;
1446
+ if (c.type === 'date-range') {
1447
+ ctrls[c.key] = this.fb.group({ start: new FormControl(), end: new FormControl() });
1448
+ }
1449
+ else {
1450
+ const initial = this.model?.[c.key];
1451
+ if (c.multiple) {
1452
+ const value = Array.isArray(initial) ? initial : (initial != null ? [initial] : []);
1453
+ ctrls[c.key] = new FormControl(value);
1454
+ }
1455
+ else {
1456
+ ctrls[c.key] = new FormControl(Array.isArray(initial) ? (initial.length ? initial[0] : undefined) : initial);
1457
+ }
1458
+ }
1459
+ });
1460
+ this.form = this.fb.group(ctrls);
1461
+ this.form.valueChanges.subscribe(() => this.filtersChanged.emit(this.serialize()));
1462
+ }
1463
+ onSelectOpenedChange(opened, select) {
1464
+ if (opened) {
1465
+ setTimeout(() => {
1466
+ const onDocDown = (e) => {
1467
+ const panel = document.querySelector('.cdk-overlay-pane .mat-select-panel');
1468
+ const target = e.target;
1469
+ const originEl = select?._elementRef?.nativeElement || null;
1470
+ const insidePanel = !!(panel && target && panel.contains(target));
1471
+ const insideOrigin = !!(originEl && target && originEl.contains(target));
1472
+ if (!insidePanel && !insideOrigin) {
1473
+ select.close();
1474
+ }
1475
+ };
1476
+ document.addEventListener('mousedown', onDocDown, true);
1477
+ this.selectOutsideCleanup.set(select, () => document.removeEventListener('mousedown', onDocDown, true));
1478
+ }, 0);
1479
+ }
1480
+ else {
1481
+ const cleanup = this.selectOutsideCleanup.get(select);
1482
+ if (cleanup)
1483
+ cleanup();
1484
+ this.selectOutsideCleanup.delete(select);
1485
+ }
1486
+ }
1487
+ onSearch(key, text) {
1488
+ this.searchTextByKey[key] = (text || '').toLowerCase();
1489
+ }
1490
+ filteredOptions(item) {
1491
+ const options = item?.options || [];
1492
+ const q = (this.searchTextByKey[item.key] || '').trim();
1493
+ if (!q)
1494
+ return options;
1495
+ return options.filter(opt => {
1496
+ const name = (opt.name ?? opt.label ?? String(opt.value ?? '')).toLowerCase();
1497
+ return name.includes(q);
1498
+ });
1499
+ }
1500
+ getDateGroup(key) {
1501
+ return this.form.get(key);
1502
+ }
1503
+ getSelectConfig(item) {
1504
+ return {
1505
+ key: item.key,
1506
+ label: item.label,
1507
+ placeholder: item.placeholder,
1508
+ disabled: item.disabled,
1509
+ multiple: item.multiple,
1510
+ searchable: item.searchable,
1511
+ options: item.options || []
1512
+ };
1513
+ }
1514
+ apply() {
1515
+ this.filtersApplied.emit(this.serialize());
1516
+ }
1517
+ reset() {
1518
+ Object.keys(this.form.controls).forEach(key => {
1519
+ const ctrl = this.form.get(key);
1520
+ if (ctrl instanceof FormGroup) {
1521
+ ctrl.get('start')?.setValue(undefined);
1522
+ ctrl.get('end')?.setValue(undefined);
1523
+ }
1524
+ else {
1525
+ ctrl?.setValue(undefined);
1526
+ }
1527
+ });
1528
+ this.resetAction.emit();
1529
+ this.filtersChanged.emit(this.serialize());
1530
+ }
1531
+ serialize() {
1532
+ const result = {};
1533
+ (this.config || []).forEach(c => {
1534
+ const ctrl = this.form.get(c.key);
1535
+ if (!ctrl)
1536
+ return;
1537
+ let val = ctrl instanceof FormGroup ? ctrl.getRawValue() : ctrl.value;
1538
+ if (c.type === 'date-range') {
1539
+ const start = val?.start;
1540
+ const end = val?.end;
1541
+ if (start || end)
1542
+ result[c.key] = { start, end };
1543
+ }
1544
+ else {
1545
+ if (val !== undefined && val !== null && (Array.isArray(val) ? val.length > 0 : val !== '')) {
1546
+ result[c.key] = val;
1547
+ }
1548
+ }
1549
+ });
1550
+ return result;
1551
+ }
1552
+ // Mat date range picker overlay preset helpers
1553
+ applyPresetToGroup(key, presetKey) {
1554
+ const dateGroup = this.getDateGroup(key);
1555
+ if (!dateGroup)
1556
+ return;
1557
+ const { start, end } = this.getPresetDates(presetKey);
1558
+ dateGroup.patchValue({ start, end });
1559
+ this.filtersChanged.emit(this.serialize());
1560
+ }
1561
+ getPresetDates(presetKey) {
1562
+ const today = new Date();
1563
+ const clampToEndOfToday = (d) => { const nd = new Date(d); nd.setHours(23, 59, 59, 999); return nd; };
1564
+ switch (presetKey) {
1565
+ case 'today':
1566
+ return { start: today, end: today };
1567
+ case 'last7days': {
1568
+ const start = new Date();
1569
+ start.setDate(start.getDate() - 6);
1570
+ return { start, end: today };
1571
+ }
1572
+ case 'last30days': {
1573
+ const start = new Date();
1574
+ start.setDate(start.getDate() - 29);
1575
+ return { start, end: today };
1576
+ }
1577
+ case 'last90days': {
1578
+ const start = new Date();
1579
+ start.setDate(start.getDate() - 89);
1580
+ return { start, end: today };
1581
+ }
1582
+ case 'thismonth': {
1583
+ const start = new Date(today.getFullYear(), today.getMonth(), 1);
1584
+ return { start, end: today };
1585
+ }
1586
+ case 'lastmonth': {
1587
+ const start = new Date(today.getFullYear(), today.getMonth() - 1, 1);
1588
+ const end = new Date(today.getFullYear(), today.getMonth(), 0);
1589
+ return { start, end: clampToEndOfToday(end) };
1590
+ }
1591
+ }
1592
+ }
1593
+ getDateValidationError(key) {
1594
+ const dateGroup = this.getDateGroup(key);
1595
+ if (!dateGroup)
1596
+ return null;
1597
+ const startCtrl = dateGroup.controls['start'];
1598
+ const endCtrl = dateGroup.controls['end'];
1599
+ const startVal = startCtrl?.value;
1600
+ const endVal = endCtrl?.value;
1601
+ const startParseErr = startCtrl?.errors?.['matDatepickerParse']?.text;
1602
+ const endParseErr = endCtrl?.errors?.['matDatepickerParse']?.text;
1603
+ if (!startVal && !endVal && !startParseErr && !endParseErr)
1604
+ return null;
1605
+ if (startParseErr)
1606
+ return `Invalid start date format`;
1607
+ if (endParseErr)
1608
+ return `Invalid end date format`;
1609
+ if (startVal && !endVal)
1610
+ return `Please select an end date`;
1611
+ if (!startVal && endVal)
1612
+ return `Please select a start date`;
1613
+ if (dateGroup.hasError('matStartDateInvalid'))
1614
+ return `Start date must be before end date`;
1615
+ if (dateGroup.hasError('matEndDateInvalid'))
1616
+ return `End date must be after start date`;
1617
+ if (dateGroup.invalid) {
1618
+ const config = this.config?.find(c => c.key === key);
1619
+ return config ? `${config.label} is invalid` : 'Date range is invalid';
1620
+ }
1621
+ return null;
1622
+ }
1623
+ }
1624
+ DynamicFilterComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicFilterComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component });
1625
+ DynamicFilterComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DynamicFilterComponent, selector: "cqa-dynamic-filter", inputs: { config: "config", model: "model", showFilterPanel: "showFilterPanel" }, outputs: { filtersApplied: "filtersApplied", filtersChanged: "filtersChanged", resetAction: "resetAction" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-filter cqa-mb-[28px]\" *ngIf=\"showFilterPanel\">\n <form class=\"ts-form cqa-grid lg:cqa-grid-cols-4 md:cqa-grid-cols-2 cqa-gap-4\" [formGroup]=\"form\"\n (keydown.enter)=\"(false)\" novalidate=\"novalidate\">\n <ng-container *ngFor=\"let c of config\">\n <ng-container *ngIf=\"!c.hidden\">\n <div class=\"form-group cqa-flex cqa-flex-col cqa-gap-2\">\n <!-- Select -->\n <ng-container *ngIf=\"c.type === 'select'\">\n <cqa-dynamic-select [form]=\"form\" [config]=\"getSelectConfig(c)\"></cqa-dynamic-select>\n </ng-container>\n\n <!-- Date Range --> \n <ng-container *ngIf=\"c.type === 'date-range'\">\n <label\n class=\"form-label cqa-text-[#374151] cqa-text-[14px] cqa-font-medium cqa-block cqa-leading-[1.4]\">{{\n c.label }}</label>\n <mat-form-field class=\"mat-date-custom\" appearance=\"fill\">\n <mat-date-range-input [rangePicker]=\"picker\" [formGroup]=\"getDateGroup(c.key)\" [max]=\"maxDate\">\n <input matStartDate formControlName=\"start\" placeholder=\"Start date\" [max]=\"maxDate\" required\n [readonly]=\"true\" (focus)=\"picker.open()\" (click)=\"picker.open()\" />\n <input matEndDate formControlName=\"end\" placeholder=\"End date\" [max]=\"maxDate\" required\n [readonly]=\"true\" (focus)=\"picker.open()\" (click)=\"picker.open()\" />\n </mat-date-range-input>\n <mat-date-range-picker #picker (dateChange)=\"onDateChange($event, c.key)\"\n [panelClass]=\"'ctc-date-range-panel'\">\n <mat-datepicker-actions>\n <div class=\"ctc-date-presets\">\n <div class=\"cqa-mb-2 cqa-font-medium\">Quick Presets</div>\n <div class=\"btn-group\">\n <button type=\"button\" class=\"preset-btn today\"\n (click)=\"applyPresetToGroup(c.key, 'today'); picker.close()\">Today</button>\n <button type=\"button\" class=\"preset-btn last7days\"\n (click)=\"applyPresetToGroup(c.key, 'last7days'); picker.close()\">Last 7 days</button>\n <button type=\"button\" class=\"preset-btn last30days\"\n (click)=\"applyPresetToGroup(c.key, 'last30days'); picker.close()\">Last 30 days</button>\n <button type=\"button\" class=\"preset-btn last90days\"\n (click)=\"applyPresetToGroup(c.key, 'last90days'); picker.close()\">Last 90 days</button>\n <button type=\"button\" class=\"preset-btn thismonth\"\n (click)=\"applyPresetToGroup(c.key, 'thismonth'); picker.close()\">This month</button>\n <button type=\"button\" class=\"preset-btn lastmonth\"\n (click)=\"applyPresetToGroup(c.key, 'lastmonth'); picker.close()\">Last month</button>\n </div>\n </div>\n <div class=\"cqa-font-medium cqa-mb-2 cqa-mt-[10px] cqa-w-full\">Custom Range</div>\n <button\n class=\"cqa-font-medium !cqa-mb-2 !cqa-mt-[10px] cqa-w-[calc(100%-32px)] !cqa-absolute cqa-bottom-[2px]\"\n mat-flat-button color=\"primary\" matDatepickerApply>Apply</button>\n </mat-datepicker-actions>\n </mat-date-range-picker>\n <div (click)=\"picker.open()\" class=\"cqa-cursor-pointer\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5.33398 1.33203V3.9987\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M10.666 1.33203V3.9987\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path\n d=\"M12.6667 2.66797H3.33333C2.59695 2.66797 2 3.26492 2 4.0013V13.3346C2 14.071 2.59695 14.668 3.33333 14.668H12.6667C13.403 14.668 14 14.071 14 13.3346V4.0013C14 3.26492 13.403 2.66797 12.6667 2.66797Z\"\n stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <path d=\"M2 6.66797H14\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n </mat-form-field>\n <!-- Specific validation messages -->\n <mat-error *ngIf=\"getDateValidationError(c.key) && !picker.opened\">\n {{ getDateValidationError(c.key) }}\n </mat-error>\n\n </ng-container>\n </div>\n </ng-container>\n </ng-container>\n </form>\n\n <div class=\"cqa-flex cqa-justify-end cqa-items-stretch cqa-gap-2 cqa-mt-4\">\n <button type=\"button\"\n class=\"cqa-text-[13.33px] cqa-leading-[1] cqa-flex cqa-flex-row cqa-justify-center cqa-items-center cqa-px-[11px] cqa-py-[1px] cqa-gap-[14px] cqa-h-8 cqa-border cqa-border-[#0B0B0C] cqa-shadow-[0_1px_2px_rgba(0,0,0,0.05)] cqa-rounded-lg cqa-text-[#0B0B0C]\"\n (click)=\"reset()\">Reset</button>\n <button type=\"button\"\n class=\"cqa-text-[13.33px] cqa-leading-[1] cqa-flex cqa-flex-row cqa-justify-center cqa-items-center cqa-px-[11px] cqa-py-[1px] cqa-gap-[14px] cqa-h-8 cqa-bg-[#0B0B0C] cqa-border cqa-border-[#E5E5E5] cqa-shadow-[0_1px_2px_rgba(0,0,0,0.05)] cqa-rounded-lg cqa-text-white\"\n (click)=\"apply()\">Apply Filter</button>\n </div>\n </div>\n</div>", components: [{ type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: ["form", "config"] }, { type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { type: i4.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { type: i4.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { type: i4.MatDatepickerActions, selector: "mat-datepicker-actions, mat-date-range-picker-actions" }, { type: i1$3.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4.MatStartDate, selector: "input[matStartDate]", inputs: ["errorStateMatcher"], outputs: ["dateChange", "dateInput"] }, { type: i1$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]" }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { type: i4.MatEndDate, selector: "input[matEndDate]", inputs: ["errorStateMatcher"], outputs: ["dateChange", "dateInput"] }, { type: i4.MatDatepickerApply, selector: "[matDatepickerApply], [matDateRangePickerApply]" }, { type: i3$2.MatError, selector: "mat-error", inputs: ["id"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicFilterComponent, decorators: [{
1627
+ type: Component,
1628
+ args: [{ selector: 'cqa-dynamic-filter', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-filter cqa-mb-[28px]\" *ngIf=\"showFilterPanel\">\n <form class=\"ts-form cqa-grid lg:cqa-grid-cols-4 md:cqa-grid-cols-2 cqa-gap-4\" [formGroup]=\"form\"\n (keydown.enter)=\"(false)\" novalidate=\"novalidate\">\n <ng-container *ngFor=\"let c of config\">\n <ng-container *ngIf=\"!c.hidden\">\n <div class=\"form-group cqa-flex cqa-flex-col cqa-gap-2\">\n <!-- Select -->\n <ng-container *ngIf=\"c.type === 'select'\">\n <cqa-dynamic-select [form]=\"form\" [config]=\"getSelectConfig(c)\"></cqa-dynamic-select>\n </ng-container>\n\n <!-- Date Range --> \n <ng-container *ngIf=\"c.type === 'date-range'\">\n <label\n class=\"form-label cqa-text-[#374151] cqa-text-[14px] cqa-font-medium cqa-block cqa-leading-[1.4]\">{{\n c.label }}</label>\n <mat-form-field class=\"mat-date-custom\" appearance=\"fill\">\n <mat-date-range-input [rangePicker]=\"picker\" [formGroup]=\"getDateGroup(c.key)\" [max]=\"maxDate\">\n <input matStartDate formControlName=\"start\" placeholder=\"Start date\" [max]=\"maxDate\" required\n [readonly]=\"true\" (focus)=\"picker.open()\" (click)=\"picker.open()\" />\n <input matEndDate formControlName=\"end\" placeholder=\"End date\" [max]=\"maxDate\" required\n [readonly]=\"true\" (focus)=\"picker.open()\" (click)=\"picker.open()\" />\n </mat-date-range-input>\n <mat-date-range-picker #picker (dateChange)=\"onDateChange($event, c.key)\"\n [panelClass]=\"'ctc-date-range-panel'\">\n <mat-datepicker-actions>\n <div class=\"ctc-date-presets\">\n <div class=\"cqa-mb-2 cqa-font-medium\">Quick Presets</div>\n <div class=\"btn-group\">\n <button type=\"button\" class=\"preset-btn today\"\n (click)=\"applyPresetToGroup(c.key, 'today'); picker.close()\">Today</button>\n <button type=\"button\" class=\"preset-btn last7days\"\n (click)=\"applyPresetToGroup(c.key, 'last7days'); picker.close()\">Last 7 days</button>\n <button type=\"button\" class=\"preset-btn last30days\"\n (click)=\"applyPresetToGroup(c.key, 'last30days'); picker.close()\">Last 30 days</button>\n <button type=\"button\" class=\"preset-btn last90days\"\n (click)=\"applyPresetToGroup(c.key, 'last90days'); picker.close()\">Last 90 days</button>\n <button type=\"button\" class=\"preset-btn thismonth\"\n (click)=\"applyPresetToGroup(c.key, 'thismonth'); picker.close()\">This month</button>\n <button type=\"button\" class=\"preset-btn lastmonth\"\n (click)=\"applyPresetToGroup(c.key, 'lastmonth'); picker.close()\">Last month</button>\n </div>\n </div>\n <div class=\"cqa-font-medium cqa-mb-2 cqa-mt-[10px] cqa-w-full\">Custom Range</div>\n <button\n class=\"cqa-font-medium !cqa-mb-2 !cqa-mt-[10px] cqa-w-[calc(100%-32px)] !cqa-absolute cqa-bottom-[2px]\"\n mat-flat-button color=\"primary\" matDatepickerApply>Apply</button>\n </mat-datepicker-actions>\n </mat-date-range-picker>\n <div (click)=\"picker.open()\" class=\"cqa-cursor-pointer\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5.33398 1.33203V3.9987\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M10.666 1.33203V3.9987\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path\n d=\"M12.6667 2.66797H3.33333C2.59695 2.66797 2 3.26492 2 4.0013V13.3346C2 14.071 2.59695 14.668 3.33333 14.668H12.6667C13.403 14.668 14 14.071 14 13.3346V4.0013C14 3.26492 13.403 2.66797 12.6667 2.66797Z\"\n stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <path d=\"M2 6.66797H14\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n </mat-form-field>\n <!-- Specific validation messages -->\n <mat-error *ngIf=\"getDateValidationError(c.key) && !picker.opened\">\n {{ getDateValidationError(c.key) }}\n </mat-error>\n\n </ng-container>\n </div>\n </ng-container>\n </ng-container>\n </form>\n\n <div class=\"cqa-flex cqa-justify-end cqa-items-stretch cqa-gap-2 cqa-mt-4\">\n <button type=\"button\"\n class=\"cqa-text-[13.33px] cqa-leading-[1] cqa-flex cqa-flex-row cqa-justify-center cqa-items-center cqa-px-[11px] cqa-py-[1px] cqa-gap-[14px] cqa-h-8 cqa-border cqa-border-[#0B0B0C] cqa-shadow-[0_1px_2px_rgba(0,0,0,0.05)] cqa-rounded-lg cqa-text-[#0B0B0C]\"\n (click)=\"reset()\">Reset</button>\n <button type=\"button\"\n class=\"cqa-text-[13.33px] cqa-leading-[1] cqa-flex cqa-flex-row cqa-justify-center cqa-items-center cqa-px-[11px] cqa-py-[1px] cqa-gap-[14px] cqa-h-8 cqa-bg-[#0B0B0C] cqa-border cqa-border-[#E5E5E5] cqa-shadow-[0_1px_2px_rgba(0,0,0,0.05)] cqa-rounded-lg cqa-text-white\"\n (click)=\"apply()\">Apply Filter</button>\n </div>\n </div>\n</div>", styles: [] }]
1629
+ }], ctorParameters: function () { return [{ type: i1$1.FormBuilder }]; }, propDecorators: { config: [{
1630
+ type: Input
1631
+ }], model: [{
1632
+ type: Input
1633
+ }], showFilterPanel: [{
1634
+ type: Input
1635
+ }], filtersApplied: [{
1636
+ type: Output
1637
+ }], filtersChanged: [{
1638
+ type: Output
1639
+ }], resetAction: [{
1640
+ type: Output
1641
+ }] } });
1642
+
1643
+ class ColumnVisibilityComponent {
1644
+ constructor() {
1645
+ this.isStepGroup = false;
1646
+ // Dynamic columns (preferred). Each item defines the id used as key and the label to render.
1647
+ this.columns = [];
1648
+ // Start with an empty visibility map; keys will be added from 'columns'
1649
+ this.columnVisibility = {};
1650
+ this.selectedAutoRefreshInterval = 0; // 0 = Off
1651
+ this.columnVisibilityChange = new EventEmitter();
1652
+ this.autoRefreshChange = new EventEmitter();
1653
+ }
1654
+ ngOnChanges(changes) {
1655
+ // When dynamic columns change, ensure we have keys in the visibility map
1656
+ if (changes['columns'] && Array.isArray(this.columns) && this.columns.length) {
1657
+ for (const col of this.columns) {
1658
+ if (this.columnVisibility[col.id] === undefined) {
1659
+ this.columnVisibility[col.id] = true;
1660
+ }
1661
+ }
1662
+ }
1663
+ }
1664
+ get areAllColumnsSelected() {
1665
+ const keys = this.getTogglableKeys();
1666
+ return keys.every(k => !!this.columnVisibility[k]);
1667
+ }
1668
+ toggleAllColumns(checked) {
1669
+ const keys = this.getTogglableKeys();
1670
+ for (const k of keys) {
1671
+ this.columnVisibility[k] = checked;
1672
+ }
1673
+ this.saveColumnPreferences();
1674
+ }
1675
+ saveColumnPreferences() {
1676
+ this.columnVisibilityChange.emit({ ...this.columnVisibility });
1677
+ }
1678
+ onAutoRefreshChange() {
1679
+ this.autoRefreshChange.emit(this.selectedAutoRefreshInterval);
1680
+ }
1681
+ getTogglableKeys() {
1682
+ return Array.isArray(this.columns) ? this.columns.map(c => c.id) : [];
1683
+ }
1684
+ }
1685
+ ColumnVisibilityComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ColumnVisibilityComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1686
+ ColumnVisibilityComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ColumnVisibilityComponent, selector: "cqa-column-visibility", inputs: { isStepGroup: "isStepGroup", columns: "columns", columnVisibility: "columnVisibility", selectedAutoRefreshInterval: "selectedAutoRefreshInterval" }, outputs: { columnVisibilityChange: "columnVisibilityChange", autoRefreshChange: "autoRefreshChange" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <cqa-button variant=\"grey-solid\" icon=\"settings\" [matMenuTriggerFor]=\"settingsMenu\" aria-label=\"Settings\">\n </cqa-button>\n\n <mat-menu #settingsMenu=\"matMenu\" class=\"table-settings-menu\">\n <div class=\"settings-menu-content cqa-p-[17px]\" (click)=\"$event.stopPropagation()\">\n <div class=\"settings-section cqa-mb-3\">\n <h4 class=\"settings-title cqa-font-bold cqa-text-[14px] cqa-leading-[20px] cqa-mb-2\">Show Columns</h4>\n <div class=\"settings-options cqa-flex cqa-flex-col cqa-gap-2 cqa-text-[14px] cqa-leading-[20px]\">\n <mat-checkbox [checked]=\"areAllColumnsSelected\" (change)=\"toggleAllColumns($event.checked)\"\n class=\"select-all-checkbox\">\n {{ areAllColumnsSelected ? 'Unselect All' : 'Select All' }}\n </mat-checkbox>\n <!-- Dynamic column list -->\n <ng-container *ngIf=\"columns?.length\">\n <mat-checkbox *ngFor=\"let col of columns\" [(ngModel)]=\"columnVisibility[col.id]\"\n (change)=\"saveColumnPreferences()\">\n {{ col.label }}\n </mat-checkbox>\n </ng-container>\n </div>\n </div>\n\n <div class=\"settings-section\">\n <h4 class=\"settings-title cqa-font-bold cqa-text-[14px] cqa-leading-[20px] cqa-mb-2\">Auto refresh every</h4>\n <div class=\"refresh-options\">\n <mat-radio-group [(ngModel)]=\"selectedAutoRefreshInterval\" (change)=\"onAutoRefreshChange()\"\n class=\"cqa-flex cqa-flex-col cqa-gap-2 cqa-text-[14px] cqa-leading-[20px]\">\n <label><mat-radio-button [value]=\"10000\">10 Seconds</mat-radio-button></label>\n <label><mat-radio-button [value]=\"20000\">20 Seconds</mat-radio-button></label>\n <label><mat-radio-button [value]=\"30000\">30 Seconds</mat-radio-button></label>\n <label><mat-radio-button [value]=\"0\">Off</mat-radio-button></label>\n </mat-radio-group>\n </div>\n </div>\n </div>\n </mat-menu>", components: [{ type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "disabled", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass"], outputs: ["clicked"] }, { type: i3$1.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { type: i3$4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex", "aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { type: i4$1.MatRadioButton, selector: "mat-radio-button", inputs: ["disableRipple", "tabIndex"], exportAs: ["matRadioButton"] }], directives: [{ type: i3$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { type: i4$1.MatRadioGroup, selector: "mat-radio-group", exportAs: ["matRadioGroup"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1687
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ColumnVisibilityComponent, decorators: [{
1688
+ type: Component,
1689
+ args: [{ selector: 'cqa-column-visibility', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <cqa-button variant=\"grey-solid\" icon=\"settings\" [matMenuTriggerFor]=\"settingsMenu\" aria-label=\"Settings\">\n </cqa-button>\n\n <mat-menu #settingsMenu=\"matMenu\" class=\"table-settings-menu\">\n <div class=\"settings-menu-content cqa-p-[17px]\" (click)=\"$event.stopPropagation()\">\n <div class=\"settings-section cqa-mb-3\">\n <h4 class=\"settings-title cqa-font-bold cqa-text-[14px] cqa-leading-[20px] cqa-mb-2\">Show Columns</h4>\n <div class=\"settings-options cqa-flex cqa-flex-col cqa-gap-2 cqa-text-[14px] cqa-leading-[20px]\">\n <mat-checkbox [checked]=\"areAllColumnsSelected\" (change)=\"toggleAllColumns($event.checked)\"\n class=\"select-all-checkbox\">\n {{ areAllColumnsSelected ? 'Unselect All' : 'Select All' }}\n </mat-checkbox>\n <!-- Dynamic column list -->\n <ng-container *ngIf=\"columns?.length\">\n <mat-checkbox *ngFor=\"let col of columns\" [(ngModel)]=\"columnVisibility[col.id]\"\n (change)=\"saveColumnPreferences()\">\n {{ col.label }}\n </mat-checkbox>\n </ng-container>\n </div>\n </div>\n\n <div class=\"settings-section\">\n <h4 class=\"settings-title cqa-font-bold cqa-text-[14px] cqa-leading-[20px] cqa-mb-2\">Auto refresh every</h4>\n <div class=\"refresh-options\">\n <mat-radio-group [(ngModel)]=\"selectedAutoRefreshInterval\" (change)=\"onAutoRefreshChange()\"\n class=\"cqa-flex cqa-flex-col cqa-gap-2 cqa-text-[14px] cqa-leading-[20px]\">\n <label><mat-radio-button [value]=\"10000\">10 Seconds</mat-radio-button></label>\n <label><mat-radio-button [value]=\"20000\">20 Seconds</mat-radio-button></label>\n <label><mat-radio-button [value]=\"30000\">30 Seconds</mat-radio-button></label>\n <label><mat-radio-button [value]=\"0\">Off</mat-radio-button></label>\n </mat-radio-group>\n </div>\n </div>\n </div>\n </mat-menu>", styles: [] }]
1690
+ }], propDecorators: { isStepGroup: [{
1691
+ type: Input
1692
+ }], columns: [{
1693
+ type: Input
1694
+ }], columnVisibility: [{
1695
+ type: Input
1696
+ }], selectedAutoRefreshInterval: [{
1697
+ type: Input
1698
+ }], columnVisibilityChange: [{
1699
+ type: Output
1700
+ }], autoRefreshChange: [{
1701
+ type: Output
1702
+ }] } });
1703
+
1704
+ class TableActionToolbarComponent {
1705
+ constructor() {
1706
+ this.selectedItems = [];
1707
+ this.actions = [];
1708
+ this.actionClick = new EventEmitter();
1709
+ }
1710
+ get hasSelection() {
1711
+ return (this.selectedItems?.length || 0) > 0;
1712
+ }
1713
+ get isSingleSelection() {
1714
+ return this.selectedItems?.length === 1;
1715
+ }
1716
+ get selectionLabel() {
1717
+ const n = this.selectedItems?.length || 0;
1718
+ return n === 1 ? '1 selected' : `${n} selected`;
1719
+ }
1720
+ visibleActions() {
1721
+ const ctx = { selected: this.selectedItems || [] };
1722
+ return (this.actions || []).filter(a => (a.show ? a.show(ctx) : true));
1723
+ }
1724
+ isDisabled(action) {
1725
+ const ctx = { selected: this.selectedItems || [] };
1726
+ return action.disabled ? !!action.disabled(ctx) : false;
1727
+ }
1728
+ onAction(action) {
1729
+ if (this.isDisabled(action)) {
1730
+ return;
1731
+ }
1732
+ const context = { id: action.id, selected: this.selectedItems || [] };
1733
+ if (action.onClick) {
1734
+ action.onClick(context);
1735
+ }
1736
+ this.actionClick.emit(context);
1737
+ }
1738
+ }
1739
+ TableActionToolbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TableActionToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1740
+ TableActionToolbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TableActionToolbarComponent, selector: "cqa-table-action-toolbar", inputs: { selectedItems: "selectedItems", actions: "actions" }, outputs: { actionClick: "actionClick" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div\n class=\"action-toolbar cqa-flex cqa-items-center cqa-justify-between cqa-gap-2 cqa-flex-wrap cqa-py-[15.5px] sm:cqa-px-[34px] cqa-px-[20px] cqa-bg-primary cqa-text-white cqa-rounded-[7px]\"\n *ngIf=\"hasSelection\">\n <div class=\"action-toolbar-left cqa-text-[14px] cqa-leading-[21px] cqa-font-medium\">\n {{ selectionLabel }}\n </div>\n <div class=\"action-toolbar-right cqa-flex cqa-items-center cqa-gap-[7px]\">\n <ng-container *ngFor=\"let action of visibleActions()\">\n <div [attr.aria-disabled]=\"isDisabled(action)\" [class.cqa-opacity-50]=\"isDisabled(action)\" [class.cqa-cursor-not-allowed]=\"isDisabled(action)\" (click)=\"!isDisabled(action) && onAction(action)\"\n class=\"cqa-flex cqa-items-center cqa-gap-[8.75px] cqa-py-[5px] cqa-px-[8.75px] cqa-cursor-pointer cqa-text-[12.3px] cqa-leading-[17.5px] cqa-font-medium\">\n <mat-icon class=\"!cqa-w-[16px] !cqa-h-[16px] !cqa-text-[16px]\">{{ action.icon }}</mat-icon>\n <span class=\"md:cqa-flex cqa-hidden\">{{ action.label }}</span>\n </div>\n </ng-container>\n </div>\n <!-- Forwards clicks inside toolbar without affecting outer selections -->\n </div>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1741
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TableActionToolbarComponent, decorators: [{
1742
+ type: Component,
1743
+ args: [{ selector: 'cqa-table-action-toolbar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div\n class=\"action-toolbar cqa-flex cqa-items-center cqa-justify-between cqa-gap-2 cqa-flex-wrap cqa-py-[15.5px] sm:cqa-px-[34px] cqa-px-[20px] cqa-bg-primary cqa-text-white cqa-rounded-[7px]\"\n *ngIf=\"hasSelection\">\n <div class=\"action-toolbar-left cqa-text-[14px] cqa-leading-[21px] cqa-font-medium\">\n {{ selectionLabel }}\n </div>\n <div class=\"action-toolbar-right cqa-flex cqa-items-center cqa-gap-[7px]\">\n <ng-container *ngFor=\"let action of visibleActions()\">\n <div [attr.aria-disabled]=\"isDisabled(action)\" [class.cqa-opacity-50]=\"isDisabled(action)\" [class.cqa-cursor-not-allowed]=\"isDisabled(action)\" (click)=\"!isDisabled(action) && onAction(action)\"\n class=\"cqa-flex cqa-items-center cqa-gap-[8.75px] cqa-py-[5px] cqa-px-[8.75px] cqa-cursor-pointer cqa-text-[12.3px] cqa-leading-[17.5px] cqa-font-medium\">\n <mat-icon class=\"!cqa-w-[16px] !cqa-h-[16px] !cqa-text-[16px]\">{{ action.icon }}</mat-icon>\n <span class=\"md:cqa-flex cqa-hidden\">{{ action.label }}</span>\n </div>\n </ng-container>\n </div>\n <!-- Forwards clicks inside toolbar without affecting outer selections -->\n </div>\n</div>", styles: [] }]
1744
+ }], propDecorators: { selectedItems: [{
1745
+ type: Input
1746
+ }], actions: [{
1747
+ type: Input
1748
+ }], actionClick: [{
1749
+ type: Output
1750
+ }] } });
1751
+
1752
+ class MetricsBlockComponent {
1753
+ constructor() {
1754
+ this.progress = '0%';
1755
+ this.layout = '1';
1756
+ this.itemslength = '0';
1757
+ }
1758
+ formatPercent(value) {
1759
+ if (value === undefined || value === null || Number.isNaN(value))
1760
+ return '';
1761
+ const sign = value > 0 ? '+' : value < 0 ? '' : '';
1762
+ return `${sign}${value}%`;
1763
+ }
1764
+ percentClass(value) {
1765
+ if (value === undefined || value === null || Number.isNaN(value))
1766
+ return 'cqa-text-[#6B7280]';
1767
+ if (value > 0)
1768
+ return 'cqa-text-[#10B981]';
1769
+ if (value < 0)
1770
+ return 'cqa-text-[#EF4444]';
1771
+ return 'cqa-text-[#6B7280]';
1772
+ }
1773
+ }
1774
+ MetricsBlockComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: MetricsBlockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1775
+ MetricsBlockComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: MetricsBlockComponent, selector: "cqa-metrics-block", inputs: { item: "item", progress: "progress", layout: "layout", itemslength: "itemslength" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div\n [ngClass]=\"[layout === '1'\n ? 'cqa-flex cqa-justify-center cqa-items-center cqa-py-3 md:cqa-pt-1 md:cqa-pb-0.5 cqa-px-5 cqa-gap-3 cqa-w-full md:cqa-h-[62px] cqa-border-b-0 cqa-border-[#E5E7EB]'\n : 'cqa-flex cqa-flex-col cqa-justify-start cqa-items-start cqa-py-3 md:cqa-py-1 lg:cqa-px-5 cqa-px-2 cqa-w-full md:cqa-h-[53px]', itemslength == item.id ? 'cqa-border-l md:cqa-border-l-0 md:cqa-border-l' : '' , item.id == '1' && layout === '1' ? 'cqa-border-l md:cqa-border-r' : '']\">\n <ng-container *ngIf=\"layout === '1'; else layout2\">\n <!-- [ngClass]=\"{ 'border-l md:border-l-0': itemslength == item.id }\" -->\n <div class=\"cqa-flex cqa-items-center cqa-justify-center cqa-w-10 cqa-h-10 cqa-min-w-[40px] cqa-rounded-[10px]\"\n [ngClass]=\"item.accentBgClass || 'cqa-bg-[#EEF2FF]'\">\n <mat-icon class=\"!cqa-w-5 !cqa-h-5 !cqa-text-[20px]\" [ngClass]=\"item.iconColorClass || 'cqa-text-[#3F43EE]'\"\n *ngIf=\"item.icon\">{{ item.icon }}</mat-icon>\n </div>\n <div class=\"cqa-flex cqa-flex-col cqa-items-start cqa-gap-0.5 cqa-w-full cqa-h-auto cqa-flex-1 cqa-truncate\">\n <div class=\"cqa-w-full cqa-h-4 cqa-text-[12px] cqa-leading-4 cqa-font-medium cqa-text-[#6A7282]\">{{ item.label\n }}</div>\n <div class=\"cqa-w-full cqa-h-7 cqa-gap-2 cqa-flex\"\n [ngClass]=\"item.subtext ? 'cqa-justify-between cqa-items-center' : 'cqa-justify-start cqa-items-center'\">\n <div\n class=\"cqa-text-[20px] cqa-leading-[28px] cqa-font-semibold cqa-tracking-[-0.449219px] cqa-text-[#101828]\">\n {{ item.value | number }}</div>\n <div *ngIf=\"item.subtext\"\n class=\"cqa-text-[12px] cqa-leading-4 cqa-text-[#6A7282] cqa-truncate cqa-hidden sm:cqa-block\">{{\n item.subtext }}</div>\n </div>\n <div class=\"cqa-w-full cqa-h-1 cqa-rounded-full cqa-bg-[#F3F4F6] cqa-overflow-hidden\">\n <div class=\"cqa-h-full cqa-rounded-full\" [ngClass]=\"item.colorClass || 'cqa-bg-[#3F43EE]'\"\n [style.width]=\"progress\"></div>\n </div>\n </div>\n </ng-container>\n <ng-template #layout2>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1\">\n <mat-icon class=\"!cqa-w-5 !cqa-h-5 !cqa-text-[16px]\" [ngClass]=\"item.iconColorClass || 'cqa-text-[#3F43EE]'\"\n *ngIf=\"item.icon\">{{ item.icon }}</mat-icon>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-[#6B7280]\">{{ item.label }}</span>\n </div>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1.5 cqa-w-full cqa-justify-between\">\n <h6 class=\"cqa-text-[16px] cqa-leading-7 cqa-tracking-[-0.45px] cqa-text-[#111827]\">{{ item.value | number }}\n </h6>\n <span *ngIf=\"item.percent !== undefined\" class=\"cqa-text-[10px] cqa-leading-4\"\n [ngClass]=\"percentClass(item.percent)\">{{ formatPercent(item.percent) }}</span>\n </div>\n </ng-template>\n </div>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], pipes: { "number": i2.DecimalPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1776
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: MetricsBlockComponent, decorators: [{
1777
+ type: Component,
1778
+ args: [{ selector: 'cqa-metrics-block', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div\n [ngClass]=\"[layout === '1'\n ? 'cqa-flex cqa-justify-center cqa-items-center cqa-py-3 md:cqa-pt-1 md:cqa-pb-0.5 cqa-px-5 cqa-gap-3 cqa-w-full md:cqa-h-[62px] cqa-border-b-0 cqa-border-[#E5E7EB]'\n : 'cqa-flex cqa-flex-col cqa-justify-start cqa-items-start cqa-py-3 md:cqa-py-1 lg:cqa-px-5 cqa-px-2 cqa-w-full md:cqa-h-[53px]', itemslength == item.id ? 'cqa-border-l md:cqa-border-l-0 md:cqa-border-l' : '' , item.id == '1' && layout === '1' ? 'cqa-border-l md:cqa-border-r' : '']\">\n <ng-container *ngIf=\"layout === '1'; else layout2\">\n <!-- [ngClass]=\"{ 'border-l md:border-l-0': itemslength == item.id }\" -->\n <div class=\"cqa-flex cqa-items-center cqa-justify-center cqa-w-10 cqa-h-10 cqa-min-w-[40px] cqa-rounded-[10px]\"\n [ngClass]=\"item.accentBgClass || 'cqa-bg-[#EEF2FF]'\">\n <mat-icon class=\"!cqa-w-5 !cqa-h-5 !cqa-text-[20px]\" [ngClass]=\"item.iconColorClass || 'cqa-text-[#3F43EE]'\"\n *ngIf=\"item.icon\">{{ item.icon }}</mat-icon>\n </div>\n <div class=\"cqa-flex cqa-flex-col cqa-items-start cqa-gap-0.5 cqa-w-full cqa-h-auto cqa-flex-1 cqa-truncate\">\n <div class=\"cqa-w-full cqa-h-4 cqa-text-[12px] cqa-leading-4 cqa-font-medium cqa-text-[#6A7282]\">{{ item.label\n }}</div>\n <div class=\"cqa-w-full cqa-h-7 cqa-gap-2 cqa-flex\"\n [ngClass]=\"item.subtext ? 'cqa-justify-between cqa-items-center' : 'cqa-justify-start cqa-items-center'\">\n <div\n class=\"cqa-text-[20px] cqa-leading-[28px] cqa-font-semibold cqa-tracking-[-0.449219px] cqa-text-[#101828]\">\n {{ item.value | number }}</div>\n <div *ngIf=\"item.subtext\"\n class=\"cqa-text-[12px] cqa-leading-4 cqa-text-[#6A7282] cqa-truncate cqa-hidden sm:cqa-block\">{{\n item.subtext }}</div>\n </div>\n <div class=\"cqa-w-full cqa-h-1 cqa-rounded-full cqa-bg-[#F3F4F6] cqa-overflow-hidden\">\n <div class=\"cqa-h-full cqa-rounded-full\" [ngClass]=\"item.colorClass || 'cqa-bg-[#3F43EE]'\"\n [style.width]=\"progress\"></div>\n </div>\n </div>\n </ng-container>\n <ng-template #layout2>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1\">\n <mat-icon class=\"!cqa-w-5 !cqa-h-5 !cqa-text-[16px]\" [ngClass]=\"item.iconColorClass || 'cqa-text-[#3F43EE]'\"\n *ngIf=\"item.icon\">{{ item.icon }}</mat-icon>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-[#6B7280]\">{{ item.label }}</span>\n </div>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1.5 cqa-w-full cqa-justify-between\">\n <h6 class=\"cqa-text-[16px] cqa-leading-7 cqa-tracking-[-0.45px] cqa-text-[#111827]\">{{ item.value | number }}\n </h6>\n <span *ngIf=\"item.percent !== undefined\" class=\"cqa-text-[10px] cqa-leading-4\"\n [ngClass]=\"percentClass(item.percent)\">{{ formatPercent(item.percent) }}</span>\n </div>\n </ng-template>\n </div>\n</div>", styles: [] }]
1779
+ }], propDecorators: { item: [{
1780
+ type: Input
1781
+ }], progress: [{
1782
+ type: Input
1783
+ }], layout: [{
1784
+ type: Input
1785
+ }], itemslength: [{
1786
+ type: Input
1787
+ }] } });
1788
+
1789
+ class MetricsCardComponent {
1790
+ constructor() {
1791
+ this.icon = 'text_snippet';
1792
+ this.title = 'Total Test Cases';
1793
+ this.total = 0;
1794
+ this.items = [];
1795
+ this.cardClass = '';
1796
+ this.layout = '1';
1797
+ }
1798
+ progressWidth(item) {
1799
+ const max = item.max ?? this.inferMax();
1800
+ if (!max || max <= 0)
1801
+ return '0%';
1802
+ const pct = Math.max(0, Math.min(100, (item.value / max) * 100));
1803
+ return pct.toFixed(1) + '%';
1804
+ }
1805
+ inferMax() {
1806
+ const maxFromItems = this.items.reduce((m, it) => Math.max(m, it.value), 0);
1807
+ return maxFromItems || 1;
1808
+ }
1809
+ formatPercent(value) {
1810
+ if (value === undefined || value === null || Number.isNaN(value))
1811
+ return '';
1812
+ const sign = value > 0 ? '+' : value < 0 ? '' : '';
1813
+ return `${sign}${value}%`;
1814
+ }
1815
+ percentClass(value) {
1816
+ if (value === undefined || value === null || Number.isNaN(value))
1817
+ return 'cqa-text-[#6B7280]';
1818
+ if (value > 0)
1819
+ return 'cqa-text-[#10B981]'; // green
1820
+ if (value < 0)
1821
+ return 'cqa-text-[#EF4444]'; // red
1822
+ return 'cqa-text-[#6B7280]'; // neutral
1823
+ }
1824
+ }
1825
+ MetricsCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: MetricsCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1826
+ MetricsCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: MetricsCardComponent, selector: "cqa-metrics-card", inputs: { icon: "icon", title: "title", total: "total", items: "items", cardClass: "cardClass", layout: "layout", totalPercent: "totalPercent" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div *ngIf=\"layout === '1'\"\n class=\"cqa-w-full cqa-box-border cqa-flex cqa-flex-col cqa-items-start cqa-px-px cqa-pb-px md:cqa-h-[63px] cqa-bg-white cqa-border-t-2 cqa-border-r cqa-border-b-0 cqa-border-l cqa-border-solid cqa-border-[#3F43EE] cqa-rounded-[10px] cqa-flex-none cqa-order-0 cqa-self-stretch cqa-grow-0\"\n [ngClass]=\"cardClass\">\n <!-- Metric blocks -->\n <div\n class=\"cqa-flex-1 cqa-w-full cqa-grid cqa-grid-cols-2 md:cqa-gap-0 cqa-gap-y-2 cqa-py-1 md:cqa-p-0 sm:cqa-grid-cols-2 md:cqa-grid-cols-4 sm:cqa-border-r cqa-border-[#E5E7EB]\">\n <!-- Left total block -->\n <div\n class=\"cqa-flex cqa-flex-col cqa-justify-center cqa-items-start cqa-py-3 md:cqa-pt-1 md:cqa-pb-0.5 cqa-px-6 cqa-gap-1 cqa-w-full sm:cqa-w-[100%] md:cqa-w-[180px] lg:cqa-w-[290px] md:cqa-h-[61px] cqa-border-b-1 sm:cqa-border-b-0 md:cqa-border-b-0 cqa-border-[#E5E7EB]\">\n <span class=\"cqa-text-[12px] cqa-leading-[16px] cqa-font-medium cqa-text-[#6A7282]\"> {{ title }} </span>\n <h3 class=\"cqa-text-[30px] cqa-leading-[36px] cqa-font-bold cqa-tracking-[0.395508px] cqa-text-[#101828]\"> {{\n total | number }} </h3>\n </div>\n <cqa-metrics-block *ngFor=\"let it of items;\" [item]=\"it\" [itemslength]=\"items.length.toString()\"\n [progress]=\"progressWidth(it)\">\n </cqa-metrics-block>\n </div>\n </div>\n\n <div *ngIf=\"layout === '2'\"\n class=\"cqa-w-full cqa-box-border cqa-flex cqa-flex-col cqa-items-start cqa-px-px cqa-pb-px md:cqa-h-[63px] cqa-bg-white cqa-flex-none cqa-order-0 cqa-self-stretch cqa-grow-0\"\n [ngClass]=\"cardClass\">\n <div\n class=\"cqa-flex cqa-flex-col md:cqa-flex-row cqa-flex-wrap cqa-items-stretch cqa-w-full cqa-min-h-[53px] cqa-rounded-none cqa-flex-none cqa-order-0 cqa-self-stretch cqa-grow-0 cqa-gap-4\">\n <!-- Left total block -->\n <div\n class=\"cqa-flex cqa-flex-col cqa-justify-center cqa-items-start cqa-py-3 md:cqa-py-1 cqa-px-4 sm:cqa-px-6 cqa-gap-1 cqa-w-full sm:cqa-w-[100%] md:cqa-w-[240px] lg:cqa-w-[290px] md:cqa-h-[61px] cqa-rounded-[10px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-relative cqa-overflow-hidden before:cqa-content-[''] before:cqa-bg-[#4C4C51] before:cqa-h-[2px] before:cqa-w-full before:cqa-absolute before:cqa-top-0 before:cqa-left-0\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-justify-between cqa-w-full\">\n <mat-icon class=\"!cqa-w-4 !cqa-h-4 !cqa-text-[16px] cqa-text-[#6B7280]\">{{ icon }}</mat-icon>\n <span class=\"cqa-text-[#6B7280] cqa-text-[14px] cqa-leading-4\">{{ title }}</span>\n <div class=\"cqa-flex cqa-items-end cqa-gap-2 cqa-ml-auto\">\n <h4 class=\"cqa-text-[28px] cqa-leading-7 cqa-tracking-[-0.45px] cqa-text-[#111827]\">{{ total | number }}\n </h4>\n <span *ngIf=\"totalPercent !== undefined\" class=\"cqa-text-[10px] cqa-leading-4\"\n [ngClass]=\"percentClass(totalPercent)\">{{ formatPercent(totalPercent) }}</span>\n </div>\n </div>\n\n </div>\n <!-- Metric blocks -->\n <div\n class=\"cqa-flex-1 cqa-w-full cqa-grid cqa-grid-cols-2 md:cqa-grid-cols-3 cqa-items-center cqa-rounded-[10px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-relative cqa-overflow-hidden\">\n <span class=\"cqa-h-[2px] cqa-w-full cqa-absolute cqa-top-0 cqa-left-0\"\n [style.background]=\"'linear-gradient(90deg, #3B82F6 0%, rgba(0, 0, 0, 0) 100%)'\"></span>\n <cqa-metrics-block *ngFor=\"let it of items; let i = index\" [item]=\"it\" [layout]=\"layout\">\n </cqa-metrics-block>\n </div>\n </div>\n </div>\n</div>", components: [{ type: MetricsBlockComponent, selector: "cqa-metrics-block", inputs: ["item", "progress", "layout", "itemslength"] }, { type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "number": i2.DecimalPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1827
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: MetricsCardComponent, decorators: [{
1828
+ type: Component,
1829
+ args: [{ selector: 'cqa-metrics-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div *ngIf=\"layout === '1'\"\n class=\"cqa-w-full cqa-box-border cqa-flex cqa-flex-col cqa-items-start cqa-px-px cqa-pb-px md:cqa-h-[63px] cqa-bg-white cqa-border-t-2 cqa-border-r cqa-border-b-0 cqa-border-l cqa-border-solid cqa-border-[#3F43EE] cqa-rounded-[10px] cqa-flex-none cqa-order-0 cqa-self-stretch cqa-grow-0\"\n [ngClass]=\"cardClass\">\n <!-- Metric blocks -->\n <div\n class=\"cqa-flex-1 cqa-w-full cqa-grid cqa-grid-cols-2 md:cqa-gap-0 cqa-gap-y-2 cqa-py-1 md:cqa-p-0 sm:cqa-grid-cols-2 md:cqa-grid-cols-4 sm:cqa-border-r cqa-border-[#E5E7EB]\">\n <!-- Left total block -->\n <div\n class=\"cqa-flex cqa-flex-col cqa-justify-center cqa-items-start cqa-py-3 md:cqa-pt-1 md:cqa-pb-0.5 cqa-px-6 cqa-gap-1 cqa-w-full sm:cqa-w-[100%] md:cqa-w-[180px] lg:cqa-w-[290px] md:cqa-h-[61px] cqa-border-b-1 sm:cqa-border-b-0 md:cqa-border-b-0 cqa-border-[#E5E7EB]\">\n <span class=\"cqa-text-[12px] cqa-leading-[16px] cqa-font-medium cqa-text-[#6A7282]\"> {{ title }} </span>\n <h3 class=\"cqa-text-[30px] cqa-leading-[36px] cqa-font-bold cqa-tracking-[0.395508px] cqa-text-[#101828]\"> {{\n total | number }} </h3>\n </div>\n <cqa-metrics-block *ngFor=\"let it of items;\" [item]=\"it\" [itemslength]=\"items.length.toString()\"\n [progress]=\"progressWidth(it)\">\n </cqa-metrics-block>\n </div>\n </div>\n\n <div *ngIf=\"layout === '2'\"\n class=\"cqa-w-full cqa-box-border cqa-flex cqa-flex-col cqa-items-start cqa-px-px cqa-pb-px md:cqa-h-[63px] cqa-bg-white cqa-flex-none cqa-order-0 cqa-self-stretch cqa-grow-0\"\n [ngClass]=\"cardClass\">\n <div\n class=\"cqa-flex cqa-flex-col md:cqa-flex-row cqa-flex-wrap cqa-items-stretch cqa-w-full cqa-min-h-[53px] cqa-rounded-none cqa-flex-none cqa-order-0 cqa-self-stretch cqa-grow-0 cqa-gap-4\">\n <!-- Left total block -->\n <div\n class=\"cqa-flex cqa-flex-col cqa-justify-center cqa-items-start cqa-py-3 md:cqa-py-1 cqa-px-4 sm:cqa-px-6 cqa-gap-1 cqa-w-full sm:cqa-w-[100%] md:cqa-w-[240px] lg:cqa-w-[290px] md:cqa-h-[61px] cqa-rounded-[10px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-relative cqa-overflow-hidden before:cqa-content-[''] before:cqa-bg-[#4C4C51] before:cqa-h-[2px] before:cqa-w-full before:cqa-absolute before:cqa-top-0 before:cqa-left-0\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-justify-between cqa-w-full\">\n <mat-icon class=\"!cqa-w-4 !cqa-h-4 !cqa-text-[16px] cqa-text-[#6B7280]\">{{ icon }}</mat-icon>\n <span class=\"cqa-text-[#6B7280] cqa-text-[14px] cqa-leading-4\">{{ title }}</span>\n <div class=\"cqa-flex cqa-items-end cqa-gap-2 cqa-ml-auto\">\n <h4 class=\"cqa-text-[28px] cqa-leading-7 cqa-tracking-[-0.45px] cqa-text-[#111827]\">{{ total | number }}\n </h4>\n <span *ngIf=\"totalPercent !== undefined\" class=\"cqa-text-[10px] cqa-leading-4\"\n [ngClass]=\"percentClass(totalPercent)\">{{ formatPercent(totalPercent) }}</span>\n </div>\n </div>\n\n </div>\n <!-- Metric blocks -->\n <div\n class=\"cqa-flex-1 cqa-w-full cqa-grid cqa-grid-cols-2 md:cqa-grid-cols-3 cqa-items-center cqa-rounded-[10px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-relative cqa-overflow-hidden\">\n <span class=\"cqa-h-[2px] cqa-w-full cqa-absolute cqa-top-0 cqa-left-0\"\n [style.background]=\"'linear-gradient(90deg, #3B82F6 0%, rgba(0, 0, 0, 0) 100%)'\"></span>\n <cqa-metrics-block *ngFor=\"let it of items; let i = index\" [item]=\"it\" [layout]=\"layout\">\n </cqa-metrics-block>\n </div>\n </div>\n </div>\n</div>", styles: [] }]
1830
+ }], propDecorators: { icon: [{
1831
+ type: Input
1832
+ }], title: [{
1833
+ type: Input
1834
+ }], total: [{
1835
+ type: Input
1836
+ }], items: [{
1837
+ type: Input
1838
+ }], cardClass: [{
1839
+ type: Input
1840
+ }], layout: [{
1841
+ type: Input
1842
+ }], totalPercent: [{
1843
+ type: Input
1844
+ }] } });
1845
+
1846
+ class ChartCardComponent {
1847
+ constructor() {
1848
+ this.title = 'Chart';
1849
+ this.cardClass = '';
1850
+ }
1851
+ }
1852
+ ChartCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ChartCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1853
+ ChartCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ChartCardComponent, selector: "cqa-chart-card", inputs: { title: "title", subtitle: "subtitle", cardClass: "cardClass" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div\n class=\"cqa-w-full cqa-box-border cqa-flex cqa-flex-col cqa-bg-white cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-rounded-[10px]\"\n [ngClass]=\"cardClass\">\n <!-- Header -->\n <div\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-px-4 sm:cqa-px-6 cqa-py-3 cqa-border-b cqa-border-[#E5E7EB]\">\n <div class=\"cqa-flex cqa-flex-col\">\n <div class=\"cqa-text-[14px] cqa-leading-5 cqa-font-semibold cqa-text-[#101828]\">{{ title }}</div>\n <div *ngIf=\"subtitle\" class=\"cqa-text-[12px] cqa-leading-4 cqa-text-[#6A7282] cqa-mt-0.5\">{{ subtitle }}</div>\n </div>\n <!-- Right-side actions slot -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <ng-content select=\"[chart-actions]\"></ng-content>\n </div>\n </div>\n\n <!-- Chart/content area -->\n <div class=\"cqa-p-3 sm:cqa-p-4\">\n <div class=\"cqa-w-full cqa-min-h-[220px]\">\n <ng-content></ng-content>\n </div>\n </div>\n </div>\n</div>", directives: [{ type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1854
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ChartCardComponent, decorators: [{
1855
+ type: Component,
1856
+ args: [{ selector: 'cqa-chart-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div\n class=\"cqa-w-full cqa-box-border cqa-flex cqa-flex-col cqa-bg-white cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-rounded-[10px]\"\n [ngClass]=\"cardClass\">\n <!-- Header -->\n <div\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-px-4 sm:cqa-px-6 cqa-py-3 cqa-border-b cqa-border-[#E5E7EB]\">\n <div class=\"cqa-flex cqa-flex-col\">\n <div class=\"cqa-text-[14px] cqa-leading-5 cqa-font-semibold cqa-text-[#101828]\">{{ title }}</div>\n <div *ngIf=\"subtitle\" class=\"cqa-text-[12px] cqa-leading-4 cqa-text-[#6A7282] cqa-mt-0.5\">{{ subtitle }}</div>\n </div>\n <!-- Right-side actions slot -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <ng-content select=\"[chart-actions]\"></ng-content>\n </div>\n </div>\n\n <!-- Chart/content area -->\n <div class=\"cqa-p-3 sm:cqa-p-4\">\n <div class=\"cqa-w-full cqa-min-h-[220px]\">\n <ng-content></ng-content>\n </div>\n </div>\n </div>\n</div>", styles: [] }]
1857
+ }], propDecorators: { title: [{
1858
+ type: Input
1859
+ }], subtitle: [{
1860
+ type: Input
1861
+ }], cardClass: [{
1862
+ type: Input
1863
+ }] } });
1864
+
1865
+ class ProgressTextCardComponent {
1866
+ constructor() {
1867
+ this.value = 0; // 0 - 100
1868
+ this.label = 'Test Success Rate';
1869
+ this.deltaSuffix = 'since last run';
1870
+ this.cardClass = '';
1871
+ }
1872
+ get percentText() {
1873
+ const v = Math.max(0, Math.min(100, Number(this.value) || 0));
1874
+ return `${v}%`;
1875
+ }
1876
+ get fillWidth() {
1877
+ const v = Math.max(0, Math.min(100, Number(this.value) || 0));
1878
+ return v + '%';
1879
+ }
1880
+ deltaClass() {
1881
+ if (this.deltaPercent === undefined || this.deltaPercent === null || Number.isNaN(this.deltaPercent)) {
1882
+ return 'text-[#6B7280]';
1883
+ }
1884
+ if (this.deltaPercent > 0)
1885
+ return 'text-[#10B981]';
1886
+ if (this.deltaPercent < 0)
1887
+ return 'text-[#EF4444]';
1888
+ return 'text-[#6B7280]';
1889
+ }
1890
+ }
1891
+ ProgressTextCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ProgressTextCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1892
+ ProgressTextCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ProgressTextCardComponent, selector: "cqa-progress-text-card", inputs: { value: "value", label: "label", deltaPercent: "deltaPercent", deltaSuffix: "deltaSuffix", cardClass: "cardClass" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div\n class=\"cqa-w-full cqa-box-border cqa-flex cqa-flex-col cqa-bg-white cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-rounded-[10px] cqa-p-4 sm:cqa-p-5\"\n [ngClass]=\"cardClass\">\n <div class=\"cqa-flex cqa-items-baseline cqa-gap-2\">\n <div\n class=\"cqa-text-[28px] sm:cqa-text-[32px] cqa-leading-[36px] cqa-font-bold cqa-tracking-[-0.3px] cqa-text-[#111827]\">\n {{ percentText }}</div>\n <div class=\"cqa-text-[18px] sm:cqa-text-[20px] cqa-leading-[28px] cqa-text-[#111827]\">{{ label }}</div>\n </div>\n <div class=\"cqa-mt-4 cqa-w-full cqa-h-[14px] cqa-bg-[#E5E7EB] cqa-rounded-full cqa-overflow-hidden\">\n <div class=\"cqa-h-full cqa-rounded-full\" [style.width]=\"fillWidth\"\n [style.background]=\"'linear-gradient(90deg, #DD5A38 0%, #F59E0B 40%, #10B981 100%)'\">\n </div>\n </div>\n <div class=\"cqa-mt-2 cqa-text-[14px] cqa-leading-5\" [ngClass]=\"deltaClass()\" *ngIf=\"deltaPercent !== undefined\">\n {{ deltaPercent > 0 ? '+' : ''}}{{ deltaPercent }}% {{ deltaSuffix }}\n </div>\n </div>\n</div>", directives: [{ type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1893
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ProgressTextCardComponent, decorators: [{
1894
+ type: Component,
1895
+ args: [{ selector: 'cqa-progress-text-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div\n class=\"cqa-w-full cqa-box-border cqa-flex cqa-flex-col cqa-bg-white cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-rounded-[10px] cqa-p-4 sm:cqa-p-5\"\n [ngClass]=\"cardClass\">\n <div class=\"cqa-flex cqa-items-baseline cqa-gap-2\">\n <div\n class=\"cqa-text-[28px] sm:cqa-text-[32px] cqa-leading-[36px] cqa-font-bold cqa-tracking-[-0.3px] cqa-text-[#111827]\">\n {{ percentText }}</div>\n <div class=\"cqa-text-[18px] sm:cqa-text-[20px] cqa-leading-[28px] cqa-text-[#111827]\">{{ label }}</div>\n </div>\n <div class=\"cqa-mt-4 cqa-w-full cqa-h-[14px] cqa-bg-[#E5E7EB] cqa-rounded-full cqa-overflow-hidden\">\n <div class=\"cqa-h-full cqa-rounded-full\" [style.width]=\"fillWidth\"\n [style.background]=\"'linear-gradient(90deg, #DD5A38 0%, #F59E0B 40%, #10B981 100%)'\">\n </div>\n </div>\n <div class=\"cqa-mt-2 cqa-text-[14px] cqa-leading-5\" [ngClass]=\"deltaClass()\" *ngIf=\"deltaPercent !== undefined\">\n {{ deltaPercent > 0 ? '+' : ''}}{{ deltaPercent }}% {{ deltaSuffix }}\n </div>\n </div>\n</div>", styles: [] }]
1896
+ }], propDecorators: { value: [{
1897
+ type: Input
1898
+ }], label: [{
1899
+ type: Input
1900
+ }], deltaPercent: [{
1901
+ type: Input
1902
+ }], deltaSuffix: [{
1903
+ type: Input
1904
+ }], cardClass: [{
1905
+ type: Input
1906
+ }] } });
1907
+
1908
+ class DashboardHeaderComponent {
1909
+ constructor() {
1910
+ this.title = '';
1911
+ this.badgeClass = 'bg-[#D1FAE5] text-[#065F46]';
1912
+ this.headerClass = '';
1913
+ // Optional workspace select on the right
1914
+ this.workspaceOptions = [];
1915
+ this.workspacePlaceholder = 'Workspace';
1916
+ this.workspaceDisabled = false;
1917
+ this.workspaceMultiple = true;
1918
+ this.workspaceSearchable = false;
1919
+ this.workspaceValueChange = new EventEmitter();
1920
+ this.workspaceForm = new FormGroup({
1921
+ workspace: new FormControl(),
1922
+ });
1923
+ }
1924
+ ngOnInit() {
1925
+ this.syncFormFromInput();
1926
+ this.workspaceForm.get('workspace')?.valueChanges.subscribe((v) => {
1927
+ this.workspaceValue = v;
1928
+ this.workspaceValueChange.emit(v);
1929
+ });
1930
+ }
1931
+ ngOnChanges(_changes) {
1932
+ this.syncFormFromInput();
1933
+ }
1934
+ get workspaceConfig() {
1935
+ return {
1936
+ key: 'workspace',
1937
+ placeholder: this.workspacePlaceholder,
1938
+ disabled: this.workspaceDisabled,
1939
+ multiple: this.workspaceMultiple,
1940
+ searchable: this.workspaceSearchable,
1941
+ options: this.workspaceOptions,
1942
+ };
1943
+ }
1944
+ syncFormFromInput() {
1945
+ const ctrl = this.workspaceForm.get('workspace');
1946
+ if (!ctrl)
1947
+ return;
1948
+ const val = this.workspaceValue;
1949
+ const normalized = this.workspaceMultiple
1950
+ ? Array.isArray(val) ? val : (val != null ? [val] : [])
1951
+ : (Array.isArray(val) ? (val.length ? val[0] : undefined) : val);
1952
+ ctrl.setValue(normalized, { emitEvent: false });
1953
+ }
1954
+ }
1955
+ DashboardHeaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DashboardHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1956
+ DashboardHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DashboardHeaderComponent, selector: "cqa-dashboard-header", inputs: { title: "title", badgeText: "badgeText", badgeClass: "badgeClass", headerClass: "headerClass", workspaceOptions: "workspaceOptions", workspacePlaceholder: "workspacePlaceholder", workspaceDisabled: "workspaceDisabled", workspaceValue: "workspaceValue", workspaceMultiple: "workspaceMultiple", workspaceSearchable: "workspaceSearchable" }, outputs: { workspaceValueChange: "workspaceValueChange" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div\n class=\"cqa-w-full cqa-flex cqa-items-end cqa-justify-between cqa-bg-white cqa-pr-6 cqa-pl-2 lg:cqa-px-6 lg:cqa-py-[6px] cqa-py-2 cqa-border-b cqa-border-default cqa-shadow-header\"\n [ngClass]=\"headerClass\">\n <!-- Left branding block -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-min-w-0\">\n <div class=\"cqa-pr-4 lg:cqa-hidden cqa-gap-2 md:cqa-flex\">\n <cqa-button variant=\"filled\" icon=\"\" [customClass]=\"'!cqa-rounded-[10px] !cqa-p-[7px] !cqa-min-w-[47px]'\">\n <svg width=\"31\" height=\"22\" viewBox=\"0 0 31 22\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5.16675 11H25.8334\" stroke=\"white\" stroke-width=\"1.83333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M5.16675 16.5H25.8334\" stroke=\"white\" stroke-width=\"1.83333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M5.16675 5.5H25.8334\" stroke=\"white\" stroke-width=\"1.83333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </cqa-button>\n <span class=\"cqa-border-l cqa-border-primary-surface cqa-hidden md:cqa-flex\"></span>\n <cqa-button variant=\"filled\" icon=\"\" class=\"cqa-hidden md:cqa-flex\" [customClass]=\"'!cqa-rounded-[10px] !cqa-p-[7px] !cqa-min-w-[47px]'\">\n <svg width=\"22\" height=\"22\" viewBox=\"0 0 22 22\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4.58337 11H17.4167\" stroke=\"white\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M11 4.58301V17.4163\" stroke=\"white\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </cqa-button>\n </div>\n <!-- Optional projected logo -->\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n <rect x=\"0.5\" y=\"0.5\" width=\"31\" height=\"31\" rx=\"15.5\" fill=\"url(#pattern0_6303_22035)\" />\n <rect x=\"0.5\" y=\"0.5\" width=\"31\" height=\"31\" rx=\"15.5\" stroke=\"#D8D9FC\" />\n <defs>\n <pattern id=\"pattern0_6303_22035\" patternContentUnits=\"objectBoundingBox\" width=\"1\" height=\"1\">\n <use xlink:href=\"#image0_6303_22035\" transform=\"scale(0.005)\" />\n </pattern>\n <image id=\"image0_6303_22035\" width=\"200\" height=\"200\" preserveAspectRatio=\"none\"\n xlink:href=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBw8SEBMQEBASEBAQEBYRFRUXFRARERASGRkWGxYXGBUYHigsGBomGxUYITEhJSkrLjouGSAzODMsNygtLjcBCgoKDg0OGxAQGy4dICUrNy0rKysrMDctLisvLS0tKy0tLTcuLS0tMDctNi0tLSstLS0rLS0rLTEtLS0tLS0tLf/AABEIAMgAyAMBIgACEQEDEQH/xAAcAAEAAgIDAQAAAAAAAAAAAAAABQYCBwEDBAj/xABBEAABAwICAwsKBQUAAwEAAAABAAIDBBEFIQYSQQciMVFSYXGBkaHRExQWIzJUkqKxwRdCYnKyU4LC4fAkNPEV/8QAGwEBAAIDAQEAAAAAAAAAAAAAAAIDAQQFBgf/xAAuEQACAQIEAwYGAwAAAAAAAAAAAQIDEQQSIVETMUEFUmGBkbEUIjJCccEVodH/2gAMAwEAAhEDEQA/AN4oiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiA81XK5jdcDWDc3DaW8Y5xwrkyFzNeIg3F2n8ruZd6r/AJbzSo1DlTzm7eKN+3qz71KKuRbsS1JViVhLbtIJa4H2mOHCCF1UNfrPdC+zZo7awzs5p4HtvsPdwKMxwuppRVxi7HEMmbyh+V3SuNI4TJEytpT66Aa7CPzxn2mnjy2KagvJ+5jMyS8/1JxBLl5S5hdsfbNzDxOHeOtcYjiBgc18n/rvIa539F/5Sf0k5X2G3Go9zosSorsOo/hBz1oZm8Gf/ZFdOjOLCshkpapvr4wY5mG2/GY1gmTq+nMZiD08bidNeopqqV1OTvm70mHrtm1Uf00xP3uT5PBbI0br3QzvwmrOvqj1DnWPlYiCdU9X3CoWnui5optaME08pJZw7w7WH7LcoON8kkvB7lM780eT00xP3uT5PBPTTE/e5Pk8FAItvhQ2RVne5P8AppifvcnyeCemmJ+9yfJ4KAROFDZDO9yz0GnmIxyNe6YytBzY4N1XDaLgZHnW4cAxqGrhE0JyOTm/mY7aCvndTeiekMlFOJG3MbspGctvHzEcK16+GUleK1J06jT1PoBVTTKmrmtM9JO8Bou6MWOXG247lY6KqZLG2WN2sx7Q5p4wu9cqUbqx0KFXhTU7J+DNLeleIe8v+XwT0rxD3l/y+Cm9P9GvJONTC31TzvwOBjuPoJVKXOm5wdmz2uFjhcRTU4wXoia9K8Q95f8AL4J6V4h7y/5fBQqKGeW5sfCUO4vRE16V4h7y/wCXwWUel1eCD5w42PAQ0g9OSg0Wc8tw8HQ7i9Ebo0X0hjq49Yb2Vvts4uccYU4tD4ViMlPK2aI2c09ThtBW6MExSOphbLHwEZja120FbtGrnVnzPJ9qdnPDSzR+l/14EiiIrzknCjseofLQOaPaG+b0j/rKSXCynZ3MNXViuYBVNqad1PLmWt1ect2HpC8OiVa6KV9FKeBx1P3bR0EZrpmd5rXEjJhdf+x3D3/RYabQmKojqGZF4vf9bbfay21FN5ej18ym7tfY80U3/wCbiTozlTTkHmAPAf7XXHQsdNo30VbFiEIykNnjYSALg/ub/FerTyJs9HDVtHs2vzNdYEdTgEa7z7B3tOcsDevWZmO1uXWVJdJv8MxuvNGG6DTCelhxGnO+hs4OHDqEjva77qUopIsWw4tfYOcNV3HHK3gcO49ahNzesE1PNQy5t1SWj9Drhw6jn/covc7rXUte+kkOUrjGeLyjL2PWLjrCw4NRcesdV+DKlqnuUSupHwyPikFnxuLXDnH1C6FsTdewgNljq2jKUaj/AN7RvT1t/itdrepTzwUiicbOwREVhEIiIDYu5PpCWyGikO9ku6L9L8y5vQRn1c62svmmmndG9sjDZ7HBwPEQvoXB8WingjnDmjyjA61xkdo7brmYulaWZdTZoyurM9dXTMkY6N4u14LSOMLSWPYW6mqHwuz1Tdp5TTwFbv8AOGctvaFSd0uhZJC2oYWl0R1XWIuWOsO427VzMRTco32O92Ni+FXyN6S9+hrVERc89mEREAVn0Exw09QGPNoZiGu4mu/K77KsLlShJxd0U4ihGtTcJdT6DuigtDcUNRSMc43e0ajukbesWPWuV1Iu6uj5/VpunNwlzROoiLJWVDTeHfRv4wW9lrfVYY563DY5dsZF+0sPfZe7TZvqmHik+oK8EG+wuYckn6tK24P5YvZlEvqaOnBfXYbUwHPUDiP5N+YKO3Mqq00sJ4JI9a3O3/Tu5SGgJuZ2bHMb3aw+6rmgj9XEIhx67fkd4K5rSovMhfWLOjRmTzXFWsvZomfCecG4HfZYadtNPirpWZG8c7emwv3tKx0kPk8VkI/LUNd/Er37rbLVcR44P8nKa1nF7oj9r8GXLTumbUYZK5udmNnb1WP8b9q0Wt+YR6zCmA569Jq/JZaDWMJpmjsyVbowiItwpCIiAIiIAso3WIPEViijKKasydObjJSXRkui4jOQ6AuV42Ss2j6pTlmgnugiIokwiIgL5uV1lpJoScnNDx0jI/UdiKK3OpCK9g5THjuv9lyujh3eB4vtuChim91f9G3URFcckrWmz/VRjjeT2A+K8EO9wuY8o/5NCz02mvJGzktJ7f8A4sdIfU4fFDteRcdrj3kLbgvlit2US+ps6NAsjO88DWD/ACP2Vc0DYXYhEeIPcfgd9yrJhfqMLqJjkZAQP4DvJUfuZ03rJp3ZNjYG36bk9zVc5aVH5EEtYor2kI8pisgGetUtZ3tH2Xv3W5L1cbeTAO9zl59EojU4o2QjLyj53cwFyPmIXVpmTVYs6Jme/ZA3sAPzXVi0mlsjH2vxZsjC/VYUwnLUo9b5LrQa3lugVbafDZGtyL2tgYOmwPygrRqjhNVKW7M1uiCIi3CkIiIAiKUotHa2Zgkip5JI3Xs4DI2JH1Cw5Jcxa/Ii0U36I4j7pL8Kwn0Zro2l8lNIyNvtOIsAFCVWCTdyynTlKSilzZjGMh0LlEXjpO8mz6pTjlilsgiIsEgiIgLRucx3r2nksee633RS+5XR76acjIARt7y77IujhlaB4vtqanin4K37NjLhFFaSV3koTb25N437nsV8Vd2OQ3ZXK9GzzuuJ4WB1zxajbfX7rq0vkdPVsp2Zllm/3OzPdZTeG07aOldLJ7ZGs7/Fv/ca8eiVAbvrZvafctvsGes5bSkk822iKbPluR26DO2Gnho2bcz+1vB2k9yyrGeYYOWnKacWPHrP4R1M+i68IpjiFe+reP8Ax4XAMB4HW9kD+R6QurSVrsRxBlHGfU0+cjhwAm2t15BvSpq2kH01Zjd+SMtAKVtLRzV8o9ppLf2Nv9T9FF7meHOnq5K2QXEZJB45X3+gJ7QpjdBqCWwYXSt38pbdo2MbbVHRcX/tUnWTw4RhwaLOeBZo2yzHhPRfPoCw5tptc5exlJabIpu61jIknZSsO9p8388jgPo36lUFdlRM57nPeS5z3FxO0krrW9ShkiolEnd3CIisIhERAeigpHzSshjF3yODQOc/ZfROF0LYIY4WezGwNHPbb2qgblOjZaPPpW5uBbCOJvA5/Xwdq2SuXi6uaWVdDaoxsrhUPdPxWzGUrTm867+Zo9kdv0VxxSvZBE6aQ2awX5zxAc91pHFa99RM+Z/tPde3ENg7FzMRUyxtueg7Fwbq1eI+Ufc8iIi0D2AREQBcgXyGZ4FwrjueYCZZfOJB6qI73ifJ/r6qUIuTsa+KxEaFJ1JF70UwvzaljjPt21nfuOZ7ODqRTCLqJWVkeAqTdSbm+bOHEAXPAFC0lOZ5vOXj1bMoWnb+sqVqIdcap9n8w5XN0LKVhLbNOrsvxdCmnbkVtXITEITVzCLgp4XXkP8AUfyR0LHSIvl1aGn3rpB6xw4IYfE8AHSpuGFrGajAAAMhn3rGkpGsueF7zrPceF5+w2WUlO3lyI5SJr3CjpmwUzLzP9XC3K7n7XG+wcJK68LoYcOpHySHWf7cr/zSSHIAceZsOlTEVIPKGV2+kI1QdjGclv1J/wBLiajD5GPfm2M3Y3Zr8o8ZGxM/T1M5SuYPRCnE2J15DZ5RrG+fkGcDWDjNrDuWrtLdIpK2cyOu2Nu9jZyG7TzklbA0y0cxOuksHxMp2HeM1nZnlONsz9FW/wAL6/lw/E7wW5QlTXzSevsUTUnoloUdFePwvr+XD8TvBPwvr+XD8TvBbPxFPvFfDlsUdFePwvr+XD8TvBPwvr+XD8TvBPiKfeHDlsUdWvQTRN1ZLryAimjO+P8AUPIB+qlsO3LqkyN8vJG2K++1CS4jiFx3raNBRxwxtiiaGRsFgBsVFfFJK0GWQpO92d0cYaA1oAaBYAZAAJLIGgucQABck8ACzKqul2F11V6qFzGQbbuOtIeew4OZcuTaV1qb1CnGc1GTyrdlL000kNVJqRm0EZ3v6zyj9lWVb/w9reVF8TvBPw9reVF8TvBaEqdSTu0exoYzBUKahCasioIrf+Htbyovid4J+Htbyovid4KPBnsXfyeF76Kgit/4e1vKi+J3gsotzyrJGs+MNvmQXEgdFlngz2MPtPC99EJo5gclXKGNyYM3u2Nb4rctBRxwxtijbqsYLALpwbCoqaIRRCwHCdrjxkr3rcpUsi8Ty3aOPlip6aRXL/TlERXHNCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiA/9k=\" />\n </defs>\n </svg>\n\n <!-- Title + optional badge -->\n <div class=\"cqa-items-end cqa-gap-3 cqa-min-w-0 cqa-hidden md:cqa-flex\">\n <div\n class=\"cqa-truncate cqa-text-[#22223B] cqa-font-extrabold cqa-text-[32px] cqa-font-nunito-sans cqa-leading-[1]\">\n {{ title }}</div>\n <span *ngIf=\"badgeText\"\n class=\"cqa-px-2 cqa-py-[2px] cqa-rounded-lg cqa-text-[12px] cqa-font-medium cqa-leading-4 cqa-whitespace-nowrap cqa-text-[#007A55] cqa-bg-[#D0FAE5] cqa-border cqa-border-[#A4F4CF]\"\n [ngClass]=\"badgeClass\">{{ badgeText }}</span>\n </div>\n </div>\n\n <!-- Right controls/actions -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-3 cqa-flex-1\">\n <!-- Optional workspace select -->\n <div *ngIf=\"workspaceOptions?.length\" class=\"cqa-w-full cqa-max-w-[199px] cqa-ml-auto header-dropdown\">\n <cqa-dynamic-select [form]=\"workspaceForm\" [config]=\"workspaceConfig\">\n </cqa-dynamic-select>\n </div>\n\n <ng-content></ng-content>\n </div>\n </div>\n</div>", components: [{ type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "disabled", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass"], outputs: ["clicked"] }, { type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: ["form", "config"] }], directives: [{ type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1957
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DashboardHeaderComponent, decorators: [{
1958
+ type: Component,
1959
+ args: [{ selector: 'cqa-dashboard-header', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div\n class=\"cqa-w-full cqa-flex cqa-items-end cqa-justify-between cqa-bg-white cqa-pr-6 cqa-pl-2 lg:cqa-px-6 lg:cqa-py-[6px] cqa-py-2 cqa-border-b cqa-border-default cqa-shadow-header\"\n [ngClass]=\"headerClass\">\n <!-- Left branding block -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-min-w-0\">\n <div class=\"cqa-pr-4 lg:cqa-hidden cqa-gap-2 md:cqa-flex\">\n <cqa-button variant=\"filled\" icon=\"\" [customClass]=\"'!cqa-rounded-[10px] !cqa-p-[7px] !cqa-min-w-[47px]'\">\n <svg width=\"31\" height=\"22\" viewBox=\"0 0 31 22\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5.16675 11H25.8334\" stroke=\"white\" stroke-width=\"1.83333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M5.16675 16.5H25.8334\" stroke=\"white\" stroke-width=\"1.83333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M5.16675 5.5H25.8334\" stroke=\"white\" stroke-width=\"1.83333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </cqa-button>\n <span class=\"cqa-border-l cqa-border-primary-surface cqa-hidden md:cqa-flex\"></span>\n <cqa-button variant=\"filled\" icon=\"\" class=\"cqa-hidden md:cqa-flex\" [customClass]=\"'!cqa-rounded-[10px] !cqa-p-[7px] !cqa-min-w-[47px]'\">\n <svg width=\"22\" height=\"22\" viewBox=\"0 0 22 22\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4.58337 11H17.4167\" stroke=\"white\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M11 4.58301V17.4163\" stroke=\"white\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </cqa-button>\n </div>\n <!-- Optional projected logo -->\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n <rect x=\"0.5\" y=\"0.5\" width=\"31\" height=\"31\" rx=\"15.5\" fill=\"url(#pattern0_6303_22035)\" />\n <rect x=\"0.5\" y=\"0.5\" width=\"31\" height=\"31\" rx=\"15.5\" stroke=\"#D8D9FC\" />\n <defs>\n <pattern id=\"pattern0_6303_22035\" patternContentUnits=\"objectBoundingBox\" width=\"1\" height=\"1\">\n <use xlink:href=\"#image0_6303_22035\" transform=\"scale(0.005)\" />\n </pattern>\n <image id=\"image0_6303_22035\" width=\"200\" height=\"200\" preserveAspectRatio=\"none\"\n xlink:href=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBw8SEBMQEBASEBAQEBYRFRUXFRARERASGRkWGxYXGBUYHigsGBomGxUYITEhJSkrLjouGSAzODMsNygtLjcBCgoKDg0OGxAQGy4dICUrNy0rKysrMDctLisvLS0tKy0tLTcuLS0tMDctNi0tLSstLS0rLS0rLTEtLS0tLS0tLf/AABEIAMgAyAMBIgACEQEDEQH/xAAcAAEAAgIDAQAAAAAAAAAAAAAABQYCBwEDBAj/xABBEAABAwICAwsKBQUAAwEAAAABAAIDBBEFIQYSQQciMVFSYXGBkaHRExQWIzJUkqKxwRdCYnKyU4LC4fAkNPEV/8QAGwEBAAIDAQEAAAAAAAAAAAAAAAIDAQQFBgf/xAAuEQACAQIEAwYGAwAAAAAAAAAAAQIDEQQSIVETMUEFUmGBkbEUIjJCccEVodH/2gAMAwEAAhEDEQA/AN4oiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiA81XK5jdcDWDc3DaW8Y5xwrkyFzNeIg3F2n8ruZd6r/AJbzSo1DlTzm7eKN+3qz71KKuRbsS1JViVhLbtIJa4H2mOHCCF1UNfrPdC+zZo7awzs5p4HtvsPdwKMxwuppRVxi7HEMmbyh+V3SuNI4TJEytpT66Aa7CPzxn2mnjy2KagvJ+5jMyS8/1JxBLl5S5hdsfbNzDxOHeOtcYjiBgc18n/rvIa539F/5Sf0k5X2G3Go9zosSorsOo/hBz1oZm8Gf/ZFdOjOLCshkpapvr4wY5mG2/GY1gmTq+nMZiD08bidNeopqqV1OTvm70mHrtm1Uf00xP3uT5PBbI0br3QzvwmrOvqj1DnWPlYiCdU9X3CoWnui5optaME08pJZw7w7WH7LcoON8kkvB7lM780eT00xP3uT5PBPTTE/e5Pk8FAItvhQ2RVne5P8AppifvcnyeCemmJ+9yfJ4KAROFDZDO9yz0GnmIxyNe6YytBzY4N1XDaLgZHnW4cAxqGrhE0JyOTm/mY7aCvndTeiekMlFOJG3MbspGctvHzEcK16+GUleK1J06jT1PoBVTTKmrmtM9JO8Bou6MWOXG247lY6KqZLG2WN2sx7Q5p4wu9cqUbqx0KFXhTU7J+DNLeleIe8v+XwT0rxD3l/y+Cm9P9GvJONTC31TzvwOBjuPoJVKXOm5wdmz2uFjhcRTU4wXoia9K8Q95f8AL4J6V4h7y/5fBQqKGeW5sfCUO4vRE16V4h7y/wCXwWUel1eCD5w42PAQ0g9OSg0Wc8tw8HQ7i9Ebo0X0hjq49Yb2Vvts4uccYU4tD4ViMlPK2aI2c09ThtBW6MExSOphbLHwEZja120FbtGrnVnzPJ9qdnPDSzR+l/14EiiIrzknCjseofLQOaPaG+b0j/rKSXCynZ3MNXViuYBVNqad1PLmWt1ect2HpC8OiVa6KV9FKeBx1P3bR0EZrpmd5rXEjJhdf+x3D3/RYabQmKojqGZF4vf9bbfay21FN5ej18ym7tfY80U3/wCbiTozlTTkHmAPAf7XXHQsdNo30VbFiEIykNnjYSALg/ub/FerTyJs9HDVtHs2vzNdYEdTgEa7z7B3tOcsDevWZmO1uXWVJdJv8MxuvNGG6DTCelhxGnO+hs4OHDqEjva77qUopIsWw4tfYOcNV3HHK3gcO49ahNzesE1PNQy5t1SWj9Drhw6jn/covc7rXUte+kkOUrjGeLyjL2PWLjrCw4NRcesdV+DKlqnuUSupHwyPikFnxuLXDnH1C6FsTdewgNljq2jKUaj/AN7RvT1t/itdrepTzwUiicbOwREVhEIiIDYu5PpCWyGikO9ku6L9L8y5vQRn1c62svmmmndG9sjDZ7HBwPEQvoXB8WingjnDmjyjA61xkdo7brmYulaWZdTZoyurM9dXTMkY6N4u14LSOMLSWPYW6mqHwuz1Tdp5TTwFbv8AOGctvaFSd0uhZJC2oYWl0R1XWIuWOsO427VzMRTco32O92Ni+FXyN6S9+hrVERc89mEREAVn0Exw09QGPNoZiGu4mu/K77KsLlShJxd0U4ihGtTcJdT6DuigtDcUNRSMc43e0ajukbesWPWuV1Iu6uj5/VpunNwlzROoiLJWVDTeHfRv4wW9lrfVYY563DY5dsZF+0sPfZe7TZvqmHik+oK8EG+wuYckn6tK24P5YvZlEvqaOnBfXYbUwHPUDiP5N+YKO3Mqq00sJ4JI9a3O3/Tu5SGgJuZ2bHMb3aw+6rmgj9XEIhx67fkd4K5rSovMhfWLOjRmTzXFWsvZomfCecG4HfZYadtNPirpWZG8c7emwv3tKx0kPk8VkI/LUNd/Er37rbLVcR44P8nKa1nF7oj9r8GXLTumbUYZK5udmNnb1WP8b9q0Wt+YR6zCmA569Jq/JZaDWMJpmjsyVbowiItwpCIiAIiIAso3WIPEViijKKasydObjJSXRkui4jOQ6AuV42Ss2j6pTlmgnugiIokwiIgL5uV1lpJoScnNDx0jI/UdiKK3OpCK9g5THjuv9lyujh3eB4vtuChim91f9G3URFcckrWmz/VRjjeT2A+K8EO9wuY8o/5NCz02mvJGzktJ7f8A4sdIfU4fFDteRcdrj3kLbgvlit2US+ps6NAsjO88DWD/ACP2Vc0DYXYhEeIPcfgd9yrJhfqMLqJjkZAQP4DvJUfuZ03rJp3ZNjYG36bk9zVc5aVH5EEtYor2kI8pisgGetUtZ3tH2Xv3W5L1cbeTAO9zl59EojU4o2QjLyj53cwFyPmIXVpmTVYs6Jme/ZA3sAPzXVi0mlsjH2vxZsjC/VYUwnLUo9b5LrQa3lugVbafDZGtyL2tgYOmwPygrRqjhNVKW7M1uiCIi3CkIiIAiKUotHa2Zgkip5JI3Xs4DI2JH1Cw5Jcxa/Ii0U36I4j7pL8Kwn0Zro2l8lNIyNvtOIsAFCVWCTdyynTlKSilzZjGMh0LlEXjpO8mz6pTjlilsgiIsEgiIgLRucx3r2nksee633RS+5XR76acjIARt7y77IujhlaB4vtqanin4K37NjLhFFaSV3koTb25N437nsV8Vd2OQ3ZXK9GzzuuJ4WB1zxajbfX7rq0vkdPVsp2Zllm/3OzPdZTeG07aOldLJ7ZGs7/Fv/ca8eiVAbvrZvafctvsGes5bSkk822iKbPluR26DO2Gnho2bcz+1vB2k9yyrGeYYOWnKacWPHrP4R1M+i68IpjiFe+reP8Ax4XAMB4HW9kD+R6QurSVrsRxBlHGfU0+cjhwAm2t15BvSpq2kH01Zjd+SMtAKVtLRzV8o9ppLf2Nv9T9FF7meHOnq5K2QXEZJB45X3+gJ7QpjdBqCWwYXSt38pbdo2MbbVHRcX/tUnWTw4RhwaLOeBZo2yzHhPRfPoCw5tptc5exlJabIpu61jIknZSsO9p8388jgPo36lUFdlRM57nPeS5z3FxO0krrW9ShkiolEnd3CIisIhERAeigpHzSshjF3yODQOc/ZfROF0LYIY4WezGwNHPbb2qgblOjZaPPpW5uBbCOJvA5/Xwdq2SuXi6uaWVdDaoxsrhUPdPxWzGUrTm867+Zo9kdv0VxxSvZBE6aQ2awX5zxAc91pHFa99RM+Z/tPde3ENg7FzMRUyxtueg7Fwbq1eI+Ufc8iIi0D2AREQBcgXyGZ4FwrjueYCZZfOJB6qI73ifJ/r6qUIuTsa+KxEaFJ1JF70UwvzaljjPt21nfuOZ7ODqRTCLqJWVkeAqTdSbm+bOHEAXPAFC0lOZ5vOXj1bMoWnb+sqVqIdcap9n8w5XN0LKVhLbNOrsvxdCmnbkVtXITEITVzCLgp4XXkP8AUfyR0LHSIvl1aGn3rpB6xw4IYfE8AHSpuGFrGajAAAMhn3rGkpGsueF7zrPceF5+w2WUlO3lyI5SJr3CjpmwUzLzP9XC3K7n7XG+wcJK68LoYcOpHySHWf7cr/zSSHIAceZsOlTEVIPKGV2+kI1QdjGclv1J/wBLiajD5GPfm2M3Y3Zr8o8ZGxM/T1M5SuYPRCnE2J15DZ5RrG+fkGcDWDjNrDuWrtLdIpK2cyOu2Nu9jZyG7TzklbA0y0cxOuksHxMp2HeM1nZnlONsz9FW/wAL6/lw/E7wW5QlTXzSevsUTUnoloUdFePwvr+XD8TvBPwvr+XD8TvBbPxFPvFfDlsUdFePwvr+XD8TvBPwvr+XD8TvBPiKfeHDlsUdWvQTRN1ZLryAimjO+P8AUPIB+qlsO3LqkyN8vJG2K++1CS4jiFx3raNBRxwxtiiaGRsFgBsVFfFJK0GWQpO92d0cYaA1oAaBYAZAAJLIGgucQABck8ACzKqul2F11V6qFzGQbbuOtIeew4OZcuTaV1qb1CnGc1GTyrdlL000kNVJqRm0EZ3v6zyj9lWVb/w9reVF8TvBPw9reVF8TvBaEqdSTu0exoYzBUKahCasioIrf+Htbyovid4J+Htbyovid4KPBnsXfyeF76Kgit/4e1vKi+J3gsotzyrJGs+MNvmQXEgdFlngz2MPtPC99EJo5gclXKGNyYM3u2Nb4rctBRxwxtijbqsYLALpwbCoqaIRRCwHCdrjxkr3rcpUsi8Ty3aOPlip6aRXL/TlERXHNCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiA/9k=\" />\n </defs>\n </svg>\n\n <!-- Title + optional badge -->\n <div class=\"cqa-items-end cqa-gap-3 cqa-min-w-0 cqa-hidden md:cqa-flex\">\n <div\n class=\"cqa-truncate cqa-text-[#22223B] cqa-font-extrabold cqa-text-[32px] cqa-font-nunito-sans cqa-leading-[1]\">\n {{ title }}</div>\n <span *ngIf=\"badgeText\"\n class=\"cqa-px-2 cqa-py-[2px] cqa-rounded-lg cqa-text-[12px] cqa-font-medium cqa-leading-4 cqa-whitespace-nowrap cqa-text-[#007A55] cqa-bg-[#D0FAE5] cqa-border cqa-border-[#A4F4CF]\"\n [ngClass]=\"badgeClass\">{{ badgeText }}</span>\n </div>\n </div>\n\n <!-- Right controls/actions -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-3 cqa-flex-1\">\n <!-- Optional workspace select -->\n <div *ngIf=\"workspaceOptions?.length\" class=\"cqa-w-full cqa-max-w-[199px] cqa-ml-auto header-dropdown\">\n <cqa-dynamic-select [form]=\"workspaceForm\" [config]=\"workspaceConfig\">\n </cqa-dynamic-select>\n </div>\n\n <ng-content></ng-content>\n </div>\n </div>\n</div>", styles: [] }]
1960
+ }], propDecorators: { title: [{
1961
+ type: Input
1962
+ }], badgeText: [{
1963
+ type: Input
1964
+ }], badgeClass: [{
1965
+ type: Input
1966
+ }], headerClass: [{
1967
+ type: Input
1968
+ }], workspaceOptions: [{
1969
+ type: Input
1970
+ }], workspacePlaceholder: [{
1971
+ type: Input
1972
+ }], workspaceDisabled: [{
1973
+ type: Input
1974
+ }], workspaceValue: [{
1975
+ type: Input
1976
+ }], workspaceMultiple: [{
1977
+ type: Input
1978
+ }], workspaceSearchable: [{
1979
+ type: Input
1980
+ }], workspaceValueChange: [{
1981
+ type: Output
1982
+ }] } });
1983
+
1984
+ class CoverageModuleCardComponent {
1985
+ constructor() {
1986
+ /** Card title, e.g. "AI Ask" */
1987
+ this.title = 'Coverage';
1988
+ /** Number of issues to display next to title */
1989
+ this.issues = 0;
1990
+ /** Optional "View" action visibility */
1991
+ this.showViewAction = true;
1992
+ /** Middle metrics: left group */
1993
+ this.positiveCount = 0;
1994
+ this.negativeCount = 0;
1995
+ this.edgeCaseCount = 0;
1996
+ this.positiveLabel = 'Positive';
1997
+ this.negativeLabel = 'Negative';
1998
+ this.edgeCaseLabel = 'Edge Case';
1999
+ /** Middle metrics: right group */
2000
+ this.aiCount = 0;
2001
+ this.humanCount = 0;
2002
+ this.aiLabel = 'AI';
2003
+ this.humanLabel = 'Human';
2004
+ /** Rows of coverage with percentage bars */
2005
+ this.items = [];
2006
+ /** CTA button label */
2007
+ this.ctaText = 'AI Coverage';
2008
+ /** Disable CTA */
2009
+ this.ctaDisabled = false;
2010
+ this.view = new EventEmitter();
2011
+ this.ctaClicked = new EventEmitter();
2012
+ }
2013
+ statusColorClass(item) {
2014
+ const status = item.status ?? 'neutral';
2015
+ switch (status) {
2016
+ case 'success':
2017
+ return 'cqa-bg-[#10B981]'; // green
2018
+ case 'error':
2019
+ return 'cqa-bg-[#EF4444]'; // red
2020
+ default:
2021
+ return 'cqa-bg-[#3B82F6]'; // blue
2022
+ }
2023
+ }
2024
+ textColorClass(item) {
2025
+ const status = item.status ?? 'neutral';
2026
+ switch (status) {
2027
+ case 'success':
2028
+ return 'cqa-text-[#10B981]';
2029
+ case 'error':
2030
+ return 'cqa-text-[#EF4444]';
2031
+ default:
2032
+ return 'cqa-text-[#374151]';
2033
+ }
2034
+ }
2035
+ formatPercent(pct) {
2036
+ if (pct === undefined || pct === null || Number.isNaN(pct))
2037
+ return '0%';
2038
+ const clamped = Math.max(0, Math.min(100, pct));
2039
+ return `${clamped}%`;
2040
+ }
2041
+ }
2042
+ CoverageModuleCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: CoverageModuleCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2043
+ CoverageModuleCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: CoverageModuleCardComponent, selector: "cqa-coverage-module-card", inputs: { title: "title", issues: "issues", showViewAction: "showViewAction", positiveCount: "positiveCount", negativeCount: "negativeCount", edgeCaseCount: "edgeCaseCount", positiveLabel: "positiveLabel", negativeLabel: "negativeLabel", edgeCaseLabel: "edgeCaseLabel", aiCount: "aiCount", humanCount: "humanCount", aiLabel: "aiLabel", humanLabel: "humanLabel", items: "items", ctaText: "ctaText", ctaDisabled: "ctaDisabled" }, outputs: { view: "view", ctaClicked: "ctaClicked" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-bg-white cqa-rounded-[8px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-py-[10px] cqa-px-[17px] cqa-shadow-card\">\n <!-- Header -->\n <div class=\"cqa-flex cqa-items-start cqa-justify-between cqa-mb-3\">\n <h3 class=\"cqa-text-[18px] cqa-leading-[28px] cqa-font-bold cqa-text-dialog\">{{ title }}</h3>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <span\n class=\"cqa-px-[10px] cqa-py-[4px] cqa-text-[12px] cqa-leading-[16px] cqa-flex cqa-items-center cqa-gap-1 cqa-rounded-full cqa-bg-warning-light cqa-text-danger\">\n {{ issues }} {{ issues === 1 ? 'issue' : 'issues' }}\n </span>\n <button type=\"button\"\n class=\"cqa-text-[14px] cqa-leading-[20px] cqa-text-primary cqa-flex cqa-items-center cqa-gap-1\"\n *ngIf=\"showViewAction\" (click)=\"view.emit()\">\n View\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6 12L10 8L6 4\" stroke=\"#4F46E5\" stroke-width=\"1.33333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n </button>\n </div>\n </div>\n\n <!-- Middle metrics row -->\n <div class=\"cqa-flex cqa-items-stretch cqa-justify-between cqa-gap-6 cqa-mb-4\">\n <!-- Left: Positive / Negative / Edge Case -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-6\">\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-[#10B981]\">{{ positiveCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ positiveLabel }}</span>\n </div>\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-[#EF4444]\">{{ negativeCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ negativeLabel }}</span>\n </div>\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-[#F59E0B]\">{{ edgeCaseCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ edgeCaseLabel }}</span>\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"cqa-w-px cqa-bg-[#E5E7EB]\"></div>\n\n <!-- Right: AI / Human -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-6\">\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-dialog\">{{ aiCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ aiLabel }}</span>\n </div>\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-dialog\">{{ humanCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ humanLabel }}</span>\n </div>\n </div>\n </div>\n\n <!-- Body -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-3\">\n <div *ngFor=\"let it of items\" class=\"cqa-flex cqa-items-center cqa-gap-3\">\n <span class=\"cqa-min-w-[120px] cqa-text-[12px] cqa-leading-4 cqa-text-[#374151]\">{{ it.label }}</span>\n <div class=\"cqa-flex-1 cqa-h-[8px] cqa-rounded-full cqa-bg-[#F3F4F6] cqa-overflow-hidden\">\n <div class=\"cqa-h-full cqa-rounded-full\" [ngClass]=\"statusColorClass(it)\"\n [style.width]=\"formatPercent(it.percent)\"></div>\n </div>\n <span class=\"cqa-w-[40px] cqa-text-right cqa-text-[12px] cqa-leading-4\" [ngClass]=\"textColorClass(it)\">{{\n formatPercent(it.percent) }}</span>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"cqa-mt-4\">\n <button type=\"button\"\n class=\"cqa-w-full cqa-h-[40px] cqa-rounded-[8px] cqa-bg-[#4338CA] cqa-text-white cqa-text-[14px] cqa-leading-[20px] hover:cqa-bg-[#3730A3] disabled:cqa-opacity-50 disabled:cqa-cursor-not-allowed\"\n [disabled]=\"ctaDisabled\" (click)=\"ctaClicked.emit()\">\n \u2728 {{ ctaText }}\n </button>\n </div>\n </div>\n</div>", directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2044
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: CoverageModuleCardComponent, decorators: [{
2045
+ type: Component,
2046
+ args: [{ selector: 'cqa-coverage-module-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-bg-white cqa-rounded-[8px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-py-[10px] cqa-px-[17px] cqa-shadow-card\">\n <!-- Header -->\n <div class=\"cqa-flex cqa-items-start cqa-justify-between cqa-mb-3\">\n <h3 class=\"cqa-text-[18px] cqa-leading-[28px] cqa-font-bold cqa-text-dialog\">{{ title }}</h3>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <span\n class=\"cqa-px-[10px] cqa-py-[4px] cqa-text-[12px] cqa-leading-[16px] cqa-flex cqa-items-center cqa-gap-1 cqa-rounded-full cqa-bg-warning-light cqa-text-danger\">\n {{ issues }} {{ issues === 1 ? 'issue' : 'issues' }}\n </span>\n <button type=\"button\"\n class=\"cqa-text-[14px] cqa-leading-[20px] cqa-text-primary cqa-flex cqa-items-center cqa-gap-1\"\n *ngIf=\"showViewAction\" (click)=\"view.emit()\">\n View\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6 12L10 8L6 4\" stroke=\"#4F46E5\" stroke-width=\"1.33333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n </button>\n </div>\n </div>\n\n <!-- Middle metrics row -->\n <div class=\"cqa-flex cqa-items-stretch cqa-justify-between cqa-gap-6 cqa-mb-4\">\n <!-- Left: Positive / Negative / Edge Case -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-6\">\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-[#10B981]\">{{ positiveCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ positiveLabel }}</span>\n </div>\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-[#EF4444]\">{{ negativeCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ negativeLabel }}</span>\n </div>\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-[#F59E0B]\">{{ edgeCaseCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ edgeCaseLabel }}</span>\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"cqa-w-px cqa-bg-[#E5E7EB]\"></div>\n\n <!-- Right: AI / Human -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-6\">\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-dialog\">{{ aiCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ aiLabel }}</span>\n </div>\n <div class=\"cqa-flex cqa-flex-col cqa-items-start\">\n <span class=\"cqa-text-[18px] cqa-leading-6 cqa-font-semibold cqa-text-dialog\">{{ humanCount }}</span>\n <span class=\"cqa-text-[12px] cqa-leading-4 cqa-text-danger\">{{ humanLabel }}</span>\n </div>\n </div>\n </div>\n\n <!-- Body -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-3\">\n <div *ngFor=\"let it of items\" class=\"cqa-flex cqa-items-center cqa-gap-3\">\n <span class=\"cqa-min-w-[120px] cqa-text-[12px] cqa-leading-4 cqa-text-[#374151]\">{{ it.label }}</span>\n <div class=\"cqa-flex-1 cqa-h-[8px] cqa-rounded-full cqa-bg-[#F3F4F6] cqa-overflow-hidden\">\n <div class=\"cqa-h-full cqa-rounded-full\" [ngClass]=\"statusColorClass(it)\"\n [style.width]=\"formatPercent(it.percent)\"></div>\n </div>\n <span class=\"cqa-w-[40px] cqa-text-right cqa-text-[12px] cqa-leading-4\" [ngClass]=\"textColorClass(it)\">{{\n formatPercent(it.percent) }}</span>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"cqa-mt-4\">\n <button type=\"button\"\n class=\"cqa-w-full cqa-h-[40px] cqa-rounded-[8px] cqa-bg-[#4338CA] cqa-text-white cqa-text-[14px] cqa-leading-[20px] hover:cqa-bg-[#3730A3] disabled:cqa-opacity-50 disabled:cqa-cursor-not-allowed\"\n [disabled]=\"ctaDisabled\" (click)=\"ctaClicked.emit()\">\n \u2728 {{ ctaText }}\n </button>\n </div>\n </div>\n</div>", styles: [] }]
2047
+ }], propDecorators: { title: [{
2048
+ type: Input
2049
+ }], issues: [{
2050
+ type: Input
2051
+ }], showViewAction: [{
2052
+ type: Input
2053
+ }], positiveCount: [{
2054
+ type: Input
2055
+ }], negativeCount: [{
2056
+ type: Input
2057
+ }], edgeCaseCount: [{
2058
+ type: Input
2059
+ }], positiveLabel: [{
2060
+ type: Input
2061
+ }], negativeLabel: [{
2062
+ type: Input
2063
+ }], edgeCaseLabel: [{
2064
+ type: Input
2065
+ }], aiCount: [{
2066
+ type: Input
2067
+ }], humanCount: [{
2068
+ type: Input
2069
+ }], aiLabel: [{
2070
+ type: Input
2071
+ }], humanLabel: [{
2072
+ type: Input
2073
+ }], items: [{
2074
+ type: Input
2075
+ }], ctaText: [{
2076
+ type: Input
2077
+ }], ctaDisabled: [{
2078
+ type: Input
2079
+ }], view: [{
2080
+ type: Output
2081
+ }], ctaClicked: [{
2082
+ type: Output
2083
+ }] } });
2084
+
2085
+ class TestDistributionCardComponent {
2086
+ constructor() {
2087
+ this.title = 'Test Distribution';
2088
+ this.segments = [];
2089
+ this.items = [];
2090
+ }
2091
+ totalSegments() {
2092
+ return this.segments.reduce((sum, s) => sum + (s.value || 0), 0) || 1;
2093
+ }
2094
+ segmentWidth(segment) {
2095
+ const total = this.totalSegments();
2096
+ const pct = Math.max(0, Math.min(100, (segment.value / total) * 100));
2097
+ return pct + '%';
2098
+ }
2099
+ segmentColor(segment, fallback) {
2100
+ return segment.colorClass || fallback;
2101
+ }
2102
+ }
2103
+ TestDistributionCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TestDistributionCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2104
+ TestDistributionCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TestDistributionCardComponent, selector: "cqa-test-distribution-card", inputs: { title: "title", segments: "segments", items: "items" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-bg-white cqa-rounded-[10px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-p-4\">\n <!-- Title -->\n <h3 class=\"cqa-text-[16px] cqa-leading-6 cqa-font-medium cqa-text-[#111827] cqa-mb-3\">{{ title }}</h3>\n\n <!-- Stacked segments pill -->\n <div class=\"cqa-w-full cqa-h-[28px] cqa-rounded-full cqa-bg-[#F3F4F6] cqa-overflow-hidden cqa-flex cqa-mb-3\">\n <ng-container *ngFor=\"let s of segments; let i = index; let last = last\">\n <div\n class=\"cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-text-white cqa-text-[12px] cqa-leading-[12px]\"\n [ngClass]=\"[\n segmentColor(s, i === 0 ? 'cqa-bg-[#4F46E5]' : i === segments.length - 1 ? 'cqa-bg-[#10B981]' : 'cqa-bg-[#8B5CF6]'),\n i === 0 ? 'cqa-rounded-l-full' : '',\n last ? 'cqa-rounded-r-full' : ''\n ]\" [style.width]=\"segmentWidth(s)\">\n {{ s.label }}\n </div>\n </ng-container>\n </div>\n\n <!-- Items list -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-2\">\n <div *ngFor=\"let it of items\" class=\"cqa-flex cqa-flex-col\">\n <!-- Parent row -->\n <div class=\"cqa-flex cqa-items-center cqa-justify-between cqa-py-1\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <mat-icon class=\"cqa-text-[#6B7280]\" [style.width.px]=\"16\" [style.height.px]=\"16\" [style.fontSize.px]=\"16\"\n *ngIf=\"it.icon\">{{ it.icon }}</mat-icon>\n <span class=\"cqa-text-[14px] cqa-leading-5 cqa-text-[#111827]\">{{ it.label }}</span>\n </div>\n <div class=\"cqa-text-[14px] cqa-leading-5 cqa-font-semibold cqa-text-[#111827]\">{{ it.value | number }}</div>\n </div>\n\n <!-- Children rows -->\n <div *ngIf=\"it.children?.length\" class=\"cqa-flex cqa-flex-col cqa-gap-2\">\n <div *ngFor=\"let ch of it.children\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-px-2 cqa-py-1 cqa-rounded-md cqa-bg-[#F9FAFB] cqa-text-[#6B7280]\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <span class=\"cqa-w-[6px] cqa-h-[14px] cqa-rounded-full\"\n [ngClass]=\"ch.colorClass || 'cqa-bg-[#8B5CF6]'\"></span>\n <span class=\"cqa-text-[12px] cqa-leading-5\">\n {{ ch.label }}\n <span class=\"cqa-font-semibold cqa-text-[#111827]\">{{ ch.value }}</span>\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], pipes: { "number": i2.DecimalPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2105
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TestDistributionCardComponent, decorators: [{
2106
+ type: Component,
2107
+ args: [{ selector: 'cqa-test-distribution-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-bg-white cqa-rounded-[10px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-p-4\">\n <!-- Title -->\n <h3 class=\"cqa-text-[16px] cqa-leading-6 cqa-font-medium cqa-text-[#111827] cqa-mb-3\">{{ title }}</h3>\n\n <!-- Stacked segments pill -->\n <div class=\"cqa-w-full cqa-h-[28px] cqa-rounded-full cqa-bg-[#F3F4F6] cqa-overflow-hidden cqa-flex cqa-mb-3\">\n <ng-container *ngFor=\"let s of segments; let i = index; let last = last\">\n <div\n class=\"cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-text-white cqa-text-[12px] cqa-leading-[12px]\"\n [ngClass]=\"[\n segmentColor(s, i === 0 ? 'cqa-bg-[#4F46E5]' : i === segments.length - 1 ? 'cqa-bg-[#10B981]' : 'cqa-bg-[#8B5CF6]'),\n i === 0 ? 'cqa-rounded-l-full' : '',\n last ? 'cqa-rounded-r-full' : ''\n ]\" [style.width]=\"segmentWidth(s)\">\n {{ s.label }}\n </div>\n </ng-container>\n </div>\n\n <!-- Items list -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-2\">\n <div *ngFor=\"let it of items\" class=\"cqa-flex cqa-flex-col\">\n <!-- Parent row -->\n <div class=\"cqa-flex cqa-items-center cqa-justify-between cqa-py-1\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <mat-icon class=\"cqa-text-[#6B7280]\" [style.width.px]=\"16\" [style.height.px]=\"16\" [style.fontSize.px]=\"16\"\n *ngIf=\"it.icon\">{{ it.icon }}</mat-icon>\n <span class=\"cqa-text-[14px] cqa-leading-5 cqa-text-[#111827]\">{{ it.label }}</span>\n </div>\n <div class=\"cqa-text-[14px] cqa-leading-5 cqa-font-semibold cqa-text-[#111827]\">{{ it.value | number }}</div>\n </div>\n\n <!-- Children rows -->\n <div *ngIf=\"it.children?.length\" class=\"cqa-flex cqa-flex-col cqa-gap-2\">\n <div *ngFor=\"let ch of it.children\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-px-2 cqa-py-1 cqa-rounded-md cqa-bg-[#F9FAFB] cqa-text-[#6B7280]\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <span class=\"cqa-w-[6px] cqa-h-[14px] cqa-rounded-full\"\n [ngClass]=\"ch.colorClass || 'cqa-bg-[#8B5CF6]'\"></span>\n <span class=\"cqa-text-[12px] cqa-leading-5\">\n {{ ch.label }}\n <span class=\"cqa-font-semibold cqa-text-[#111827]\">{{ ch.value }}</span>\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: [] }]
2108
+ }], propDecorators: { title: [{
2109
+ type: Input
2110
+ }], segments: [{
2111
+ type: Input
2112
+ }], items: [{
2113
+ type: Input
2114
+ }] } });
2115
+
2116
+ class FailedTestCasesCardComponent {
2117
+ constructor() {
2118
+ /** E.g., "C-62: Upload Content" */
2119
+ this.title = 'Failed Test Case';
2120
+ /** Number of failures to display in the pill */
2121
+ this.failures = 0;
2122
+ /** Pill label (e.g., "failures") */
2123
+ this.failuresLabel = 'failures';
2124
+ /** Optional custom class for the pill background */
2125
+ this.pillClass = 'bg-[#EF4444] text-white';
2126
+ /** Root cause label (left part, emphasized) */
2127
+ this.rootCauseLabel = 'Root cause';
2128
+ /** If false, hide the root cause row */
2129
+ this.showRootCause = true;
2130
+ /** Label for timestamp */
2131
+ this.lastFailedLabel = 'Last failed';
2132
+ /** Optional extra class for the outer card */
2133
+ this.cardClass = '';
2134
+ /** Left border accent class */
2135
+ this.leftAccentClass = 'border-l-[4px] border-[#EF4444]';
2136
+ }
2137
+ get rootCauseDisplay() {
2138
+ return this.rootCause && this.rootCause.trim().length
2139
+ ? this.rootCause
2140
+ : 'No root cause available';
2141
+ }
2142
+ }
2143
+ FailedTestCasesCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: FailedTestCasesCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2144
+ FailedTestCasesCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: FailedTestCasesCardComponent, selector: "cqa-failed-test-cases-card", inputs: { title: "title", failures: "failures", failuresLabel: "failuresLabel", pillClass: "pillClass", rootCause: "rootCause", rootCauseLabel: "rootCauseLabel", showRootCause: "showRootCause", lastFailed: "lastFailed", lastFailedLabel: "lastFailedLabel", cardClass: "cardClass", leftAccentClass: "leftAccentClass" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-bg-white cqa-rounded-[10px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-p-4\"\n [ngClass]=\"[leftAccentClass, cardClass]\">\n <div class=\"cqa-flex cqa-items-start cqa-justify-between cqa-gap-2\">\n <!-- Title -->\n <h3 class=\"cqa-text-[16px] cqa-leading-6 cqa-font-semibold cqa-text-[#111827]\">{{ title }}</h3>\n\n <!-- Failures pill -->\n <span class=\"cqa-px-2.5 cqa-py-1 cqa-rounded-full cqa-text-[12px] cqa-leading-4 cqa-font-medium\"\n [ngClass]=\"pillClass\">\n {{ failures | number }} {{ failuresLabel }}\n </span>\n </div>\n\n <!-- Root cause -->\n <div *ngIf=\"showRootCause\" class=\"cqa-mt-2 cqa-text-[14px] cqa-leading-5\">\n <span class=\"cqa-font-semibold cqa-text-[#EF4444]\">{{ rootCauseLabel }}:</span>\n <span class=\"cqa-text-[#EF4444]\">{{ rootCauseDisplay }}</span>\n </div>\n\n <!-- Timestamp -->\n <div *ngIf=\"lastFailed\"\n class=\"cqa-mt-1 cqa-flex cqa-items-center cqa-gap-2 cqa-text-[12px] cqa-leading-5 cqa-text-[#6B7280]\">\n <mat-icon class=\"!cqa-w-4 !cqa-h-4 !cqa-text-[16px]\">schedule</mat-icon>\n <span>{{ lastFailedLabel }}: {{ lastFailed }}</span>\n </div>\n </div>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], pipes: { "number": i2.DecimalPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2145
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: FailedTestCasesCardComponent, decorators: [{
2146
+ type: Component,
2147
+ args: [{ selector: 'cqa-failed-test-cases-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-bg-white cqa-rounded-[10px] cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-p-4\"\n [ngClass]=\"[leftAccentClass, cardClass]\">\n <div class=\"cqa-flex cqa-items-start cqa-justify-between cqa-gap-2\">\n <!-- Title -->\n <h3 class=\"cqa-text-[16px] cqa-leading-6 cqa-font-semibold cqa-text-[#111827]\">{{ title }}</h3>\n\n <!-- Failures pill -->\n <span class=\"cqa-px-2.5 cqa-py-1 cqa-rounded-full cqa-text-[12px] cqa-leading-4 cqa-font-medium\"\n [ngClass]=\"pillClass\">\n {{ failures | number }} {{ failuresLabel }}\n </span>\n </div>\n\n <!-- Root cause -->\n <div *ngIf=\"showRootCause\" class=\"cqa-mt-2 cqa-text-[14px] cqa-leading-5\">\n <span class=\"cqa-font-semibold cqa-text-[#EF4444]\">{{ rootCauseLabel }}:</span>\n <span class=\"cqa-text-[#EF4444]\">{{ rootCauseDisplay }}</span>\n </div>\n\n <!-- Timestamp -->\n <div *ngIf=\"lastFailed\"\n class=\"cqa-mt-1 cqa-flex cqa-items-center cqa-gap-2 cqa-text-[12px] cqa-leading-5 cqa-text-[#6B7280]\">\n <mat-icon class=\"!cqa-w-4 !cqa-h-4 !cqa-text-[16px]\">schedule</mat-icon>\n <span>{{ lastFailedLabel }}: {{ lastFailed }}</span>\n </div>\n </div>\n</div>", styles: [] }]
2148
+ }], propDecorators: { title: [{
2149
+ type: Input
2150
+ }], failures: [{
2151
+ type: Input
2152
+ }], failuresLabel: [{
2153
+ type: Input
2154
+ }], pillClass: [{
2155
+ type: Input
2156
+ }], rootCause: [{
2157
+ type: Input
2158
+ }], rootCauseLabel: [{
2159
+ type: Input
2160
+ }], showRootCause: [{
2161
+ type: Input
2162
+ }], lastFailed: [{
2163
+ type: Input
2164
+ }], lastFailedLabel: [{
2165
+ type: Input
2166
+ }], cardClass: [{
2167
+ type: Input
2168
+ }], leftAccentClass: [{
2169
+ type: Input
2170
+ }] } });
2171
+
2172
+ class SelectedFiltersComponent {
2173
+ constructor() {
2174
+ this.filterApplied = false;
2175
+ this.chips = [];
2176
+ this.removeChip = new EventEmitter();
2177
+ this.clearAll = new EventEmitter();
2178
+ }
2179
+ }
2180
+ SelectedFiltersComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: SelectedFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2181
+ SelectedFiltersComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: SelectedFiltersComponent, selector: "cqa-selected-filters", inputs: { filterApplied: "filterApplied", chips: "chips" }, outputs: { removeChip: "removeChip", clearAll: "clearAll" }, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-flex cqa-flex-wrap cqa-justify-end cqa-gap-2 cqa-mb-3\"\n *ngIf=\"filterApplied && (chips?.length || 0)\">\n <div\n class=\"cqa-inline-flex cqa-items-center cqa-gap-2 cqa-px-2 cqa-py-1 cqa-rounded-full cqa-border cqa-border-success-100 cqa-bg-primary-surface\"\n *ngFor=\"let chip of chips\" [matTooltip]=\"chip.hasMore ? chip.fullText : ''\" [matTooltipDisabled]=\"!chip.hasMore\"\n matTooltipPosition=\"above\">\n <span class=\"cqa-text-[12px] cqa-leading-[16px] cqa-font-medium cqa-text-primary\">{{ chip.label || chip.key\n }}:</span>\n <span class=\"cqa-text-[12px] cqa-leading-[16px]\">{{ chip.text }}</span>\n <button type=\"button\"\n class=\"cqa-ml-1 cqa-inline-flex cqa-items-center cqa-justify-center cqa-rounded-full cqa-border cqa-border-transparent hover:cqa-bg-[#EEF2FF]\"\n (click)=\"removeChip.emit(chip)\" aria-label=\"Remove filter\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 12 11\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M8.625 2.875L3.375 8.125\" stroke=\"#3F43EE\" stroke-width=\"0.875\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M3.375 2.875L8.625 8.125\" stroke=\"#3F43EE\" stroke-width=\"0.875\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <button type=\"button\" class=\"cqa-text-[12px] cqa-leading-[16px] cqa-text-grey-300 hover:cqa-underline\"\n (click)=\"clearAll.emit()\">\n Clear All\n </button>\n </div>\n</div>", directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2182
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: SelectedFiltersComponent, decorators: [{
2183
+ type: Component,
2184
+ args: [{ selector: 'cqa-selected-filters', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-flex cqa-flex-wrap cqa-justify-end cqa-gap-2 cqa-mb-3\"\n *ngIf=\"filterApplied && (chips?.length || 0)\">\n <div\n class=\"cqa-inline-flex cqa-items-center cqa-gap-2 cqa-px-2 cqa-py-1 cqa-rounded-full cqa-border cqa-border-success-100 cqa-bg-primary-surface\"\n *ngFor=\"let chip of chips\" [matTooltip]=\"chip.hasMore ? chip.fullText : ''\" [matTooltipDisabled]=\"!chip.hasMore\"\n matTooltipPosition=\"above\">\n <span class=\"cqa-text-[12px] cqa-leading-[16px] cqa-font-medium cqa-text-primary\">{{ chip.label || chip.key\n }}:</span>\n <span class=\"cqa-text-[12px] cqa-leading-[16px]\">{{ chip.text }}</span>\n <button type=\"button\"\n class=\"cqa-ml-1 cqa-inline-flex cqa-items-center cqa-justify-center cqa-rounded-full cqa-border cqa-border-transparent hover:cqa-bg-[#EEF2FF]\"\n (click)=\"removeChip.emit(chip)\" aria-label=\"Remove filter\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 12 11\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M8.625 2.875L3.375 8.125\" stroke=\"#3F43EE\" stroke-width=\"0.875\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n <path d=\"M3.375 2.875L8.625 8.125\" stroke=\"#3F43EE\" stroke-width=\"0.875\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n\n <button type=\"button\" class=\"cqa-text-[12px] cqa-leading-[16px] cqa-text-grey-300 hover:cqa-underline\"\n (click)=\"clearAll.emit()\">\n Clear All\n </button>\n </div>\n</div>", styles: [] }]
2185
+ }], propDecorators: { filterApplied: [{
2186
+ type: Input
2187
+ }], chips: [{
2188
+ type: Input
2189
+ }], removeChip: [{
2190
+ type: Output
2191
+ }], clearAll: [{
2192
+ type: Output
2193
+ }] } });
2194
+
2195
+ /**
2196
+ * Utility for getting consistent colors for metadata values
2197
+ * Used across Status, Priority, and Result fields
2198
+ *
2199
+ * Colors are defined here and also exported in metadata-colors.constants.js for tailwind.config.js
2200
+ */
2201
+ // Color constants (also exported in metadata-colors.constants.js for Tailwind)
2202
+ const PRIORITY_COLOR_VALUES = {
2203
+ critical: '#DC2626',
2204
+ major: '#C10007',
2205
+ medium: '#CA8A04',
2206
+ minor: '#2563EB',
2207
+ 'not-set': '#101828', // Default dark
2208
+ };
2209
+ const RESULT_COLOR_VALUES = {
2210
+ passed: '#16A34A',
2211
+ failed: '#DC2626',
2212
+ aborted: '#EA580C',
2213
+ 'in-review': '#2563EB',
2214
+ 'not-executed': '#101828', // Default dark
2215
+ };
2216
+ const STATUS_COLOR_VALUES = {
2217
+ active: '#16A34A',
2218
+ inactive: '#6B7280',
2219
+ pending: '#CA8A04',
2220
+ completed: '#16A34A', // Green-600
2221
+ };
2222
+ const DEFAULT_METADATA_COLOR_VALUE = '#101828';
2223
+ /**
2224
+ * Color mappings for Priority values
2225
+ */
2226
+ const PRIORITY_COLORS = {
2227
+ 'critical': PRIORITY_COLOR_VALUES.critical,
2228
+ 'major': PRIORITY_COLOR_VALUES.major,
2229
+ 'medium': PRIORITY_COLOR_VALUES.medium,
2230
+ 'minor': PRIORITY_COLOR_VALUES.minor,
2231
+ 'not set': PRIORITY_COLOR_VALUES['not-set'],
2232
+ };
2233
+ /**
2234
+ * Color mappings for Result values
2235
+ */
2236
+ const RESULT_COLORS = {
2237
+ 'passed': RESULT_COLOR_VALUES.passed,
2238
+ 'failed': RESULT_COLOR_VALUES.failed,
2239
+ 'aborted': RESULT_COLOR_VALUES.aborted,
2240
+ 'in review': RESULT_COLOR_VALUES['in-review'],
2241
+ 'not executed': RESULT_COLOR_VALUES['not-executed'],
2242
+ };
2243
+ /**
2244
+ * Color mappings for Status values
2245
+ */
2246
+ const STATUS_COLORS = {
2247
+ 'active': STATUS_COLOR_VALUES.active,
2248
+ 'inactive': STATUS_COLOR_VALUES.inactive,
2249
+ 'pending': STATUS_COLOR_VALUES.pending,
2250
+ 'completed': STATUS_COLOR_VALUES.completed,
2251
+ };
2252
+ /**
2253
+ * Default color for metadata values
2254
+ */
2255
+ const DEFAULT_METADATA_COLOR = DEFAULT_METADATA_COLOR_VALUE;
2256
+ /**
2257
+ * Gets the color for a metadata value based on its key and value
2258
+ * @param key - The metadata key (priority, result, or status)
2259
+ * @param value - The metadata value (string or object with value property)
2260
+ * @returns Color hex code as a string
2261
+ */
2262
+ function getMetadataColor(key, value) {
2263
+ // Extract the actual value
2264
+ const actualValue = typeof value === 'string' ? value : value.value;
2265
+ const normalizedValue = actualValue.toLowerCase().trim();
2266
+ const normalizedKey = key.toLowerCase().trim();
2267
+ // Get color based on key
2268
+ let colorMap;
2269
+ switch (normalizedKey) {
2270
+ case 'priority':
2271
+ colorMap = PRIORITY_COLORS;
2272
+ break;
2273
+ case 'result':
2274
+ colorMap = RESULT_COLORS;
2275
+ break;
2276
+ case 'status':
2277
+ colorMap = STATUS_COLORS;
2278
+ break;
2279
+ default:
2280
+ return DEFAULT_METADATA_COLOR;
2281
+ }
2282
+ // Return color for the value, or default if not found
2283
+ return colorMap[normalizedValue] || DEFAULT_METADATA_COLOR;
2284
+ }
2285
+ /**
2286
+ * Gets inline style object for metadata value color
2287
+ * @param key - The metadata key
2288
+ * @param value - The metadata value
2289
+ * @returns Style object with color property
2290
+ */
2291
+ function getMetadataValueStyle(key, value) {
2292
+ return { color: getMetadataColor(key, value) };
2293
+ }
2294
+
2295
+ class BadgeComponent {
2296
+ constructor() {
2297
+ this.label = '';
2298
+ this.variant = 'default';
2299
+ }
2300
+ get badgeClasses() {
2301
+ const baseClasses = [
2302
+ 'cqa-inline-flex',
2303
+ 'cqa-items-center',
2304
+ 'cqa-justify-center',
2305
+ 'cqa-rounded-[6px]',
2306
+ 'cqa-font-normal',
2307
+ 'cqa-leading-[17px]'
2308
+ ];
2309
+ // Only apply variant-specific Tailwind classes if custom colors are not provided
2310
+ if (!this.backgroundColor && !this.textColor) {
2311
+ switch (this.variant) {
2312
+ case 'error':
2313
+ return [...baseClasses, 'cqa-bg-red-100', 'cqa-text-red-800'].join(' ');
2314
+ case 'warning':
2315
+ return [...baseClasses, 'cqa-bg-yellow-100', 'cqa-text-yellow-800'].join(' ');
2316
+ case 'info':
2317
+ return [...baseClasses, 'cqa-bg-blue-100', 'cqa-text-blue-800'].join(' ');
2318
+ case 'success':
2319
+ return [...baseClasses, 'cqa-bg-green-100', 'cqa-text-green-800'].join(' ');
2320
+ case 'outline':
2321
+ return [...baseClasses, 'cqa-bg-transparent', 'cqa-text-gray-800'].join(' ');
2322
+ default:
2323
+ return [...baseClasses, 'cqa-bg-gray-100', 'cqa-text-gray-800'].join(' ');
2324
+ }
2325
+ }
2326
+ // If custom colors are provided, only return base classes
2327
+ return baseClasses.join(' ');
2328
+ }
2329
+ get badgeStyles() {
2330
+ const styles = {};
2331
+ if (this.backgroundColor) {
2332
+ styles['background-color'] = this.backgroundColor;
2333
+ }
2334
+ else if (this.variant === 'outline') {
2335
+ styles['background-color'] = 'transparent';
2336
+ }
2337
+ if (this.textColor) {
2338
+ styles['color'] = this.textColor;
2339
+ }
2340
+ return styles;
2341
+ }
2342
+ get iconContainerClasses() {
2343
+ const baseClasses = [
2344
+ 'cqa-inline-flex',
2345
+ 'cqa-items-center',
2346
+ 'cqa-justify-center',
2347
+ 'cqa-flex-shrink-0',
2348
+ 'cqa-mr-1.5'
2349
+ ];
2350
+ // No circular background for any variant - just return base classes
2351
+ return baseClasses.join(' ');
2352
+ }
2353
+ get iconContainerStyles() {
2354
+ const styles = {
2355
+ 'display': 'inline-flex',
2356
+ 'align-items': 'center',
2357
+ 'justify-content': 'center'
2358
+ };
2359
+ // No circular background - only apply custom icon background color if explicitly provided
2360
+ if (this.iconBackgroundColor) {
2361
+ styles['background-color'] = this.iconBackgroundColor;
2362
+ // If custom background is provided, add circle dimensions
2363
+ styles['width'] = '16px';
2364
+ styles['height'] = '16px';
2365
+ styles['min-width'] = '16px';
2366
+ styles['min-height'] = '16px';
2367
+ styles['border-radius'] = '50%';
2368
+ }
2369
+ return styles;
2370
+ }
2371
+ get iconClasses() {
2372
+ const baseClasses = [];
2373
+ // Only apply white text class if custom icon color is not provided
2374
+ if (!this.iconColor) {
2375
+ return [...baseClasses, 'cqa-text-white'].join(' ');
2376
+ }
2377
+ return baseClasses.join(' ');
2378
+ }
2379
+ get iconStyles() {
2380
+ const styles = {
2381
+ 'font-size': '14px',
2382
+ 'width': '14px',
2383
+ 'height': '14px',
2384
+ 'line-height': '14px'
2385
+ };
2386
+ if (this.iconColor) {
2387
+ styles['color'] = this.iconColor;
2388
+ }
2389
+ else {
2390
+ // Use variant-specific text color for icon (no white background anymore)
2391
+ switch (this.variant) {
2392
+ case 'error':
2393
+ styles['color'] = '#991B1B'; // red-800
2394
+ break;
2395
+ case 'warning':
2396
+ styles['color'] = '#854D0E'; // yellow-800
2397
+ break;
2398
+ case 'info':
2399
+ styles['color'] = '#1E40AF'; // blue-800
2400
+ break;
2401
+ case 'success':
2402
+ styles['color'] = '#166534'; // green-800
2403
+ break;
2404
+ default:
2405
+ styles['color'] = this.textColor || '#374151'; // gray-700 default
2406
+ }
2407
+ }
2408
+ return styles;
2409
+ }
2410
+ }
2411
+ BadgeComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: BadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2412
+ BadgeComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: BadgeComponent, selector: "cqa-badge", inputs: { label: "label", icon: "icon", variant: "variant", backgroundColor: "backgroundColor", textColor: "textColor", iconBackgroundColor: "iconBackgroundColor", iconColor: "iconColor" }, ngImport: i0, template: "<div id=\"cqa-ui-root\" style=\"display: inline-block;\">\n <span \n [ngClass]=\"badgeClasses\" \n [ngStyle]=\"badgeStyles\"\n class=\"cqa-font-inter cqa-font-normal cqa-text-sm cqa-leading-[17px] cqa-py-[4px] cqa-px-3\">\n <span \n *ngIf=\"icon\" \n [ngClass]=\"iconContainerClasses\"\n [ngStyle]=\"iconContainerStyles\">\n <mat-icon \n [ngClass]=\"iconClasses\"\n [ngStyle]=\"iconStyles\">{{ icon }}</mat-icon>\n </span>\n {{ label }}\n </span>\n</div>\n\n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
2413
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: BadgeComponent, decorators: [{
2414
+ type: Component,
2415
+ args: [{ selector: 'cqa-badge', template: "<div id=\"cqa-ui-root\" style=\"display: inline-block;\">\n <span \n [ngClass]=\"badgeClasses\" \n [ngStyle]=\"badgeStyles\"\n class=\"cqa-font-inter cqa-font-normal cqa-text-sm cqa-leading-[17px] cqa-py-[4px] cqa-px-3\">\n <span \n *ngIf=\"icon\" \n [ngClass]=\"iconContainerClasses\"\n [ngStyle]=\"iconContainerStyles\">\n <mat-icon \n [ngClass]=\"iconClasses\"\n [ngStyle]=\"iconStyles\">{{ icon }}</mat-icon>\n </span>\n {{ label }}\n </span>\n</div>\n\n", styles: [] }]
2416
+ }], propDecorators: { label: [{
2417
+ type: Input
2418
+ }], icon: [{
2419
+ type: Input
2420
+ }], variant: [{
2421
+ type: Input
2422
+ }], backgroundColor: [{
2423
+ type: Input
2424
+ }], textColor: [{
2425
+ type: Input
2426
+ }], iconBackgroundColor: [{
2427
+ type: Input
2428
+ }], iconColor: [{
2429
+ type: Input
2430
+ }] } });
2431
+
2432
+ class InsightCardComponent {
2433
+ constructor() {
2434
+ this.title = '';
2435
+ this.description = '';
2436
+ this._badges = [];
2437
+ this.metadataExpanded = true;
2438
+ this.isPrerequisiteMissing = false;
2439
+ this.isTestDataMissing = false;
2440
+ // Track expanded state for sections
2441
+ this.sectionExpandedState = new Map();
2442
+ // Loading state for main action
2443
+ this.isApplying = false;
2444
+ this.metadataToggle = new EventEmitter();
2445
+ this.sectionToggle = new EventEmitter();
2446
+ this.sectionActionClick = new EventEmitter();
2447
+ this.onApplySuggestionClick = new EventEmitter();
2448
+ this.onAttachPrerequisitesClick = new EventEmitter();
2449
+ this.onImportTestDataClick = new EventEmitter();
2450
+ }
2451
+ set badges(value) {
2452
+ this._badges = value;
2453
+ }
2454
+ get badges() {
2455
+ return this._badges;
2456
+ }
2457
+ toggleMetadata() {
2458
+ this.metadataExpanded = !this.metadataExpanded;
2459
+ this.metadataToggle.emit(this.metadataExpanded);
2460
+ }
2461
+ toggleSection(section) {
2462
+ const currentState = this.sectionExpandedState.get(section.id) ?? (section.expanded ?? true);
2463
+ const newState = !currentState;
2464
+ this.sectionExpandedState.set(section.id, newState);
2465
+ this.sectionToggle.emit({ id: section.id, expanded: newState });
2466
+ }
2467
+ onSectionAction(sectionId) {
2468
+ this.sectionActionClick.emit(sectionId);
2469
+ // Emit specific events for known actions
2470
+ if (sectionId === 'prerequisite') {
2471
+ this.onAttachPrerequisitesClick.emit();
2472
+ }
2473
+ else if (sectionId === 'test-data') {
2474
+ this.onImportTestDataClick.emit();
2475
+ }
2476
+ }
2477
+ get visibleBadges() {
2478
+ const visible = [];
2479
+ if (this.isPrerequisiteMissing) {
2480
+ visible.push({ label: 'Prerequisite Missing', variant: 'warning' });
2481
+ }
2482
+ if (this.isTestDataMissing) {
2483
+ visible.push({ label: 'Test Data Missing', variant: 'error' });
2484
+ }
2485
+ // Also include any custom badges
2486
+ if (this.badges) {
2487
+ visible.push(...this.badges);
2488
+ }
2489
+ return visible;
2490
+ }
2491
+ get visibleSections() {
2492
+ const visible = [];
2493
+ if (this.isPrerequisiteMissing && this.prerequisiteSection) {
2494
+ // Use stored state if available, otherwise default to true
2495
+ const expanded = this.sectionExpandedState.has('prerequisite')
2496
+ ? this.sectionExpandedState.get('prerequisite')
2497
+ : true;
2498
+ visible.push({
2499
+ id: 'prerequisite',
2500
+ title: 'Prerequisite Missing',
2501
+ variant: 'warning',
2502
+ reason: this.prerequisiteSection,
2503
+ actionButtonLabel: 'Attach Prerequisites',
2504
+ expanded: expanded,
2505
+ });
2506
+ }
2507
+ if (this.isTestDataMissing && this.testDataSection) {
2508
+ // Use stored state if available, otherwise default to true
2509
+ const expanded = this.sectionExpandedState.has('test-data')
2510
+ ? this.sectionExpandedState.get('test-data')
2511
+ : true;
2512
+ visible.push({
2513
+ id: 'test-data',
2514
+ title: 'Test Data Missing',
2515
+ variant: 'error',
2516
+ reason: this.testDataSection,
2517
+ actionButtonLabel: 'Import Test Data',
2518
+ expanded: expanded,
2519
+ });
2520
+ }
2521
+ return visible;
2522
+ }
2523
+ async onMainAction() {
2524
+ if (this.isApplying) {
2525
+ return; // Prevent multiple clicks
2526
+ }
2527
+ this.isApplying = true;
2528
+ try {
2529
+ this.onApplySuggestionClick.emit();
2530
+ }
2531
+ finally {
2532
+ // Reset loading state after action completes
2533
+ // If you need to keep the loading state longer, call resetApplyingState() manually
2534
+ // after your async operation completes
2535
+ this.isApplying = false;
2536
+ }
2537
+ }
2538
+ // Method to reset loading state (can be called externally if needed)
2539
+ resetApplyingState() {
2540
+ this.isApplying = false;
2541
+ }
2542
+ // Removed getBadgeClasses - now using BadgeComponent
2543
+ getSectionBorderClass(section) {
2544
+ switch (section.variant) {
2545
+ case 'warning':
2546
+ return 'cqa-border-l-4 cqa-border-l-yellow-500';
2547
+ case 'error':
2548
+ return 'cqa-border-l-4 cqa-border-l-red-500';
2549
+ default:
2550
+ return 'cqa-border-l-4 cqa-border-l-gray-500';
2551
+ }
2552
+ }
2553
+ getMetadataValueClasses(key, value) {
2554
+ // Base classes for all values (font-size: 14px, line-height: 18px, font-weight: 400)
2555
+ return 'cqa-font-normal cqa-leading-[18px] cqa-tracking-normal';
2556
+ }
2557
+ getMetadataValue(value) {
2558
+ if (typeof value === 'string') {
2559
+ return value;
2560
+ }
2561
+ return value.value;
2562
+ }
2563
+ getMetadataValueStyle(key, value) {
2564
+ // Use the shared utility function for consistent colors
2565
+ return getMetadataValueStyle(key, value);
2566
+ }
2567
+ getSectionTitleClasses(section) {
2568
+ const baseClasses = ['cqa-text-sm', 'cqa-font-bold'];
2569
+ switch (section.variant) {
2570
+ case 'warning':
2571
+ return [...baseClasses, 'cqa-text-yellow-800'].join(' ');
2572
+ case 'error':
2573
+ return [...baseClasses, 'cqa-text-red-600'].join(' ');
2574
+ default:
2575
+ return [...baseClasses, 'cqa-text-ink'].join(' ');
2576
+ }
2577
+ }
2578
+ getSectionIconColor(section) {
2579
+ switch (section.variant) {
2580
+ case 'warning':
2581
+ return 'cqa-text-yellow-800';
2582
+ case 'error':
2583
+ return 'cqa-text-red-600';
2584
+ default:
2585
+ return 'cqa-text-gray-400';
2586
+ }
2587
+ }
2588
+ }
2589
+ InsightCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: InsightCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2590
+ InsightCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: InsightCardComponent, selector: "cqa-insight-card", inputs: { title: "title", description: "description", badges: "badges", metadata: "metadata", prerequisiteSection: "prerequisiteSection", testDataSection: "testDataSection", metadataExpanded: "metadataExpanded", isPrerequisiteMissing: "isPrerequisiteMissing", isTestDataMissing: "isTestDataMissing" }, outputs: { metadataToggle: "metadataToggle", sectionToggle: "sectionToggle", sectionActionClick: "sectionActionClick", onApplySuggestionClick: "onApplySuggestionClick", onAttachPrerequisitesClick: "onAttachPrerequisitesClick", onImportTestDataClick: "onImportTestDataClick" }, ngImport: i0, template: "<div id=\"cqa-ui-root\" style=\"background-color: white; border-radius: 0.5rem; border: 1px solid #E5E7EB; box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-4 cqa-px-[21px] cqa-py-3 cqa-border cqa-border-gray-200 cqa-rounded-2xl\">\n <!-- Section 1: Badges -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\" *ngIf=\"visibleBadges.length > 0\">\n <mat-icon *ngIf=\"isPrerequisiteMissing || isTestDataMissing\"\n class=\"cqa-text-yellow-500 cqa-w-6 cqa-h-6\">warning</mat-icon>\n <div class=\"cqa-flex cqa-flex-wrap cqa-gap-2\">\n <cqa-badge \n *ngFor=\"let badge of visibleBadges\" \n [label]=\"badge.label\"\n [icon]=\"badge.icon\"\n [variant]=\"badge.variant || 'default'\"\n ></cqa-badge>\n </div>\n </div>\n\n <!-- Section 2: Title & Description -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-3\">\n <h2 class=\"cqa-font-medium cqa-text-lg cqa-text-title\">\n {{ title }}\n </h2>\n <p class=\"cqa-text-base cqa-font-normal cqa-text-description\">\n {{ description }}\n </p>\n </div>\n\n <!-- Section 3: Metadata Section (always visible) -->\n <div *ngIf=\"metadata\" class=\"cqa-flex cqa-flex-col cqa-gap-4\">\n <div class=\"cqa-bg-surface-default cqa-rounded-xl cqa-border cqa-border-border-light cqa-px-4 cqa-py-3 cqa-border-t-2 cqa-border-t-primary-surface\">\n <div class=\"cqa-flex cqa-flex-wrap cqa-items-center cqa-gap-3\">\n <ng-container *ngFor=\"let item of metadata | keyvalue; let last = last\">\n <div class=\"cqa-flex cqa-items-baseline cqa-gap-2\">\n <span class=\"cqa-text-xs cqa-font-normal cqa-tracking-normal cqa-font-inter cqa-text-metadata-key\">\n {{ item.key }}:\n </span>\n <span \n [ngClass]=\"getMetadataValueClasses(item.key, item.value)\" \n [ngStyle]=\"getMetadataValueStyle(item.key, item.value)\"\n class=\"cqa-font-normal cqa-leading-[18px] cqa-tracking-normal cqa-font-inter cqa-text-sm\">\n {{ getMetadataValue(item.value) }}\n </span>\n </div>\n <div *ngIf=\"!last\" class=\"cqa-h-4 cqa-w-px cqa-bg-gray-200\"></div>\n </ng-container>\n </div>\n </div>\n\n <!-- Section 4: Metadata toggle -->\n <button *ngIf=\"metadata && (isPrerequisiteMissing || isTestDataMissing)\" type=\"button\"\n class=\"cqa-text-sm cqa-text-primary-hover cqa-font-medium cqa-inline-flex cqa-items-center cqa-gap-1 cqa-cursor-pointer cqa-bg-transparent cqa-border-0 cqa-p-0 cqa-hover:cqa-text-primary cqa-transition-colors\"\n (click)=\"toggleMetadata()\">\n <span>{{ metadataExpanded ? 'Hide details' : 'Show details' }}</span>\n <mat-icon class=\"cqa-w-4 cqa-h-4 cqa-text-[16px] cqa-leading-[16px]\">\n {{ metadataExpanded ? 'expand_less' : 'expand_more' }}\n </mat-icon>\n </button>\n\n <!-- Section 5: Sections (toggle visibility) -->\n <div *ngIf=\"metadataExpanded && visibleSections?.length\" class=\"cqa-flex cqa-flex-col cqa-gap-4\">\n <div\n *ngFor=\"let section of visibleSections\"\n class=\"cqa-border cqa-border-gray-200 cqa-rounded-md cqa-bg-white cqa-overflow-hidden\"\n [ngClass]=\"getSectionBorderClass(section)\"\n >\n <div class=\"cqa-p-4 cqa-flex cqa-flex-col cqa-gap-3\">\n <!-- Section Title with Inline Collapse Button -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <h3 [ngClass]=\"getSectionTitleClasses(section)\" class=\"cqa-m-0\">\n {{ section.title }}\n </h3>\n <button\n type=\"button\"\n class=\"cqa-ml-auto cqa-inline-flex cqa-items-center cqa-justify-center cqa-cursor-pointer cqa-bg-transparent cqa-border-0 cqa-p-1 cqa-rounded-full cqa-hover:cqa-bg-gray-100 cqa-transition-colors\"\n (click)=\"toggleSection(section)\"\n [attr.aria-label]=\"section.expanded !== false ? 'Collapse section' : 'Expand section'\"\n >\n <mat-icon [ngClass]=\"getSectionIconColor(section)\" class=\"cqa-w-4 cqa-h-4 cqa-text-[16px] cqa-leading-[16px]\">\n {{ section.expanded !== false ? 'expand_less' : 'expand_more' }}\n </mat-icon>\n </button>\n </div>\n\n <!-- Collapsible Content: Reason and Action Button -->\n <div *ngIf=\"section.expanded !== false\" class=\"cqa-flex cqa-flex-col cqa-gap-3\">\n <!-- Reason -->\n <p class=\"cqa-text-sm cqa-font-normal cqa-leading-4 cqa-text-neutral-600\">\n <strong>Reason:</strong> {{ section.reason }}\n </p>\n \n <!-- Action Button -->\n <div>\n <cqa-button\n variant=\"outlined\"\n (clicked)=\"onSectionAction(section.id)\"\n >\n {{ section.actionButtonLabel }}\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n </div>\n \n <cqa-button\n variant=\"filled\"\n [icon]=\"isApplying ? 'hourglass_empty' : 'auto_awesome'\"\n iconPosition=\"start\"\n (clicked)=\"onMainAction()\"\n [fullWidth]=\"true\"\n [disabled]=\"isApplying\"\n [iconColor]=\"isApplying ? '#EAB308' : undefined\"\n >\n {{ isApplying ? 'Applying suggestion' : 'Apply suggestion' }}\n </cqa-button>\n </div>\n</div>\n \n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: BadgeComponent, selector: "cqa-badge", inputs: ["label", "icon", "variant", "backgroundColor", "textColor", "iconBackgroundColor", "iconColor"] }, { type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "disabled", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass"], outputs: ["clicked"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], pipes: { "keyvalue": i2.KeyValuePipe } });
2591
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: InsightCardComponent, decorators: [{
2592
+ type: Component,
2593
+ args: [{ selector: 'cqa-insight-card', template: "<div id=\"cqa-ui-root\" style=\"background-color: white; border-radius: 0.5rem; border: 1px solid #E5E7EB; box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-4 cqa-px-[21px] cqa-py-3 cqa-border cqa-border-gray-200 cqa-rounded-2xl\">\n <!-- Section 1: Badges -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\" *ngIf=\"visibleBadges.length > 0\">\n <mat-icon *ngIf=\"isPrerequisiteMissing || isTestDataMissing\"\n class=\"cqa-text-yellow-500 cqa-w-6 cqa-h-6\">warning</mat-icon>\n <div class=\"cqa-flex cqa-flex-wrap cqa-gap-2\">\n <cqa-badge \n *ngFor=\"let badge of visibleBadges\" \n [label]=\"badge.label\"\n [icon]=\"badge.icon\"\n [variant]=\"badge.variant || 'default'\"\n ></cqa-badge>\n </div>\n </div>\n\n <!-- Section 2: Title & Description -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-3\">\n <h2 class=\"cqa-font-medium cqa-text-lg cqa-text-title\">\n {{ title }}\n </h2>\n <p class=\"cqa-text-base cqa-font-normal cqa-text-description\">\n {{ description }}\n </p>\n </div>\n\n <!-- Section 3: Metadata Section (always visible) -->\n <div *ngIf=\"metadata\" class=\"cqa-flex cqa-flex-col cqa-gap-4\">\n <div class=\"cqa-bg-surface-default cqa-rounded-xl cqa-border cqa-border-border-light cqa-px-4 cqa-py-3 cqa-border-t-2 cqa-border-t-primary-surface\">\n <div class=\"cqa-flex cqa-flex-wrap cqa-items-center cqa-gap-3\">\n <ng-container *ngFor=\"let item of metadata | keyvalue; let last = last\">\n <div class=\"cqa-flex cqa-items-baseline cqa-gap-2\">\n <span class=\"cqa-text-xs cqa-font-normal cqa-tracking-normal cqa-font-inter cqa-text-metadata-key\">\n {{ item.key }}:\n </span>\n <span \n [ngClass]=\"getMetadataValueClasses(item.key, item.value)\" \n [ngStyle]=\"getMetadataValueStyle(item.key, item.value)\"\n class=\"cqa-font-normal cqa-leading-[18px] cqa-tracking-normal cqa-font-inter cqa-text-sm\">\n {{ getMetadataValue(item.value) }}\n </span>\n </div>\n <div *ngIf=\"!last\" class=\"cqa-h-4 cqa-w-px cqa-bg-gray-200\"></div>\n </ng-container>\n </div>\n </div>\n\n <!-- Section 4: Metadata toggle -->\n <button *ngIf=\"metadata && (isPrerequisiteMissing || isTestDataMissing)\" type=\"button\"\n class=\"cqa-text-sm cqa-text-primary-hover cqa-font-medium cqa-inline-flex cqa-items-center cqa-gap-1 cqa-cursor-pointer cqa-bg-transparent cqa-border-0 cqa-p-0 cqa-hover:cqa-text-primary cqa-transition-colors\"\n (click)=\"toggleMetadata()\">\n <span>{{ metadataExpanded ? 'Hide details' : 'Show details' }}</span>\n <mat-icon class=\"cqa-w-4 cqa-h-4 cqa-text-[16px] cqa-leading-[16px]\">\n {{ metadataExpanded ? 'expand_less' : 'expand_more' }}\n </mat-icon>\n </button>\n\n <!-- Section 5: Sections (toggle visibility) -->\n <div *ngIf=\"metadataExpanded && visibleSections?.length\" class=\"cqa-flex cqa-flex-col cqa-gap-4\">\n <div\n *ngFor=\"let section of visibleSections\"\n class=\"cqa-border cqa-border-gray-200 cqa-rounded-md cqa-bg-white cqa-overflow-hidden\"\n [ngClass]=\"getSectionBorderClass(section)\"\n >\n <div class=\"cqa-p-4 cqa-flex cqa-flex-col cqa-gap-3\">\n <!-- Section Title with Inline Collapse Button -->\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <h3 [ngClass]=\"getSectionTitleClasses(section)\" class=\"cqa-m-0\">\n {{ section.title }}\n </h3>\n <button\n type=\"button\"\n class=\"cqa-ml-auto cqa-inline-flex cqa-items-center cqa-justify-center cqa-cursor-pointer cqa-bg-transparent cqa-border-0 cqa-p-1 cqa-rounded-full cqa-hover:cqa-bg-gray-100 cqa-transition-colors\"\n (click)=\"toggleSection(section)\"\n [attr.aria-label]=\"section.expanded !== false ? 'Collapse section' : 'Expand section'\"\n >\n <mat-icon [ngClass]=\"getSectionIconColor(section)\" class=\"cqa-w-4 cqa-h-4 cqa-text-[16px] cqa-leading-[16px]\">\n {{ section.expanded !== false ? 'expand_less' : 'expand_more' }}\n </mat-icon>\n </button>\n </div>\n\n <!-- Collapsible Content: Reason and Action Button -->\n <div *ngIf=\"section.expanded !== false\" class=\"cqa-flex cqa-flex-col cqa-gap-3\">\n <!-- Reason -->\n <p class=\"cqa-text-sm cqa-font-normal cqa-leading-4 cqa-text-neutral-600\">\n <strong>Reason:</strong> {{ section.reason }}\n </p>\n \n <!-- Action Button -->\n <div>\n <cqa-button\n variant=\"outlined\"\n (clicked)=\"onSectionAction(section.id)\"\n >\n {{ section.actionButtonLabel }}\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n </div>\n \n <cqa-button\n variant=\"filled\"\n [icon]=\"isApplying ? 'hourglass_empty' : 'auto_awesome'\"\n iconPosition=\"start\"\n (clicked)=\"onMainAction()\"\n [fullWidth]=\"true\"\n [disabled]=\"isApplying\"\n [iconColor]=\"isApplying ? '#EAB308' : undefined\"\n >\n {{ isApplying ? 'Applying suggestion' : 'Apply suggestion' }}\n </cqa-button>\n </div>\n</div>\n \n", styles: [] }]
2594
+ }], propDecorators: { title: [{
2595
+ type: Input
2596
+ }], description: [{
2597
+ type: Input
2598
+ }], badges: [{
2599
+ type: Input
2600
+ }], metadata: [{
2601
+ type: Input
2602
+ }], prerequisiteSection: [{
2603
+ type: Input
2604
+ }], testDataSection: [{
2605
+ type: Input
2606
+ }], metadataExpanded: [{
2607
+ type: Input
2608
+ }], isPrerequisiteMissing: [{
2609
+ type: Input
2610
+ }], isTestDataMissing: [{
2611
+ type: Input
2612
+ }], metadataToggle: [{
2613
+ type: Output
2614
+ }], sectionToggle: [{
2615
+ type: Output
2616
+ }], sectionActionClick: [{
2617
+ type: Output
2618
+ }], onApplySuggestionClick: [{
2619
+ type: Output
2620
+ }], onAttachPrerequisitesClick: [{
2621
+ type: Output
2622
+ }], onImportTestDataClick: [{
2623
+ type: Output
2624
+ }] } });
2625
+
2626
+ class DropdownButtonComponent {
2627
+ constructor() {
2628
+ this.label = 'Select an option';
2629
+ this.options = [];
2630
+ this.disabled = false;
2631
+ this.selectionChange = new EventEmitter();
2632
+ this.opened = new EventEmitter();
2633
+ this.closed = new EventEmitter();
2634
+ this.isOpen = false;
2635
+ this.clickInside = false;
2636
+ }
2637
+ get displayLabel() {
2638
+ if (this.selectedValue !== undefined && this.selectedValue !== null) {
2639
+ const selectedOption = this.options.find(opt => opt.value === this.selectedValue);
2640
+ return selectedOption ? selectedOption.label : this.label;
2641
+ }
2642
+ return this.placeholder || this.label;
2643
+ }
2644
+ get buttonClasses() {
2645
+ const baseClasses = [
2646
+ 'cqa-h-9',
2647
+ 'cqa-px-3',
2648
+ 'cqa-py-2',
2649
+ 'cqa-bg-gray-100',
2650
+ 'cqa-rounded-[5px]',
2651
+ 'cqa-outline',
2652
+ 'cqa-outline-1',
2653
+ 'cqa-outline-offset-[-1px]',
2654
+ 'cqa-outline-gray-200',
2655
+ 'cqa-inline-flex',
2656
+ 'cqa-justify-center',
2657
+ 'cqa-items-center',
2658
+ 'cqa-gap-1',
2659
+ 'cqa-cursor-pointer',
2660
+ 'cqa-transition-colors'
2661
+ ];
2662
+ if (this.disabled) {
2663
+ baseClasses.push('cqa-opacity-50', 'cqa-cursor-not-allowed');
2664
+ }
2665
+ else {
2666
+ baseClasses.push('cqa-hover:cqa-bg-gray-200');
2667
+ }
2668
+ if (this.isOpen) {
2669
+ baseClasses.push('cqa-bg-gray-200');
2670
+ }
2671
+ return baseClasses.join(' ');
2672
+ }
2673
+ get labelClasses() {
2674
+ return [
2675
+ 'cqa-text-center',
2676
+ 'cqa-justify-center',
2677
+ 'cqa-text-gray-950',
2678
+ 'cqa-text-xs',
2679
+ 'cqa-font-medium',
2680
+ 'cqa-leading-4'
2681
+ ].join(' ');
2682
+ }
2683
+ get arrowClasses() {
2684
+ const baseClasses = [
2685
+ 'cqa-w-4',
2686
+ 'cqa-h-4',
2687
+ 'cqa-relative',
2688
+ 'cqa-transition-transform'
2689
+ ];
2690
+ if (this.isOpen) {
2691
+ baseClasses.push('cqa-rotate-180');
2692
+ }
2693
+ return baseClasses.join(' ');
2694
+ }
2695
+ toggleDropdown(event) {
2696
+ if (this.disabled)
2697
+ return;
2698
+ if (event) {
2699
+ event.stopPropagation();
2700
+ event.preventDefault();
2701
+ this.clickInside = true;
2702
+ }
2703
+ this.isOpen = !this.isOpen;
2704
+ // Reset clickInside flag after a short delay
2705
+ setTimeout(() => {
2706
+ this.clickInside = false;
2707
+ if (this.isOpen) {
2708
+ this.opened.emit();
2709
+ }
2710
+ else {
2711
+ this.closed.emit();
2712
+ }
2713
+ }, 100);
2714
+ }
2715
+ selectOption(option, event) {
2716
+ if (option.disabled)
2717
+ return;
2718
+ if (event) {
2719
+ event.stopPropagation();
2720
+ this.clickInside = true;
2721
+ }
2722
+ this.selectedValue = option.value;
2723
+ this.isOpen = false;
2724
+ this.selectionChange.emit(option.value);
2725
+ this.closed.emit();
2726
+ // Reset flag after selection
2727
+ setTimeout(() => {
2728
+ this.clickInside = false;
2729
+ }, 100);
2730
+ }
2731
+ getButtonWidth() {
2732
+ if (this.buttonElement?.nativeElement) {
2733
+ const width = this.buttonElement.nativeElement.offsetWidth;
2734
+ return width > 0 ? width : 150; // Fallback to 150px if width is 0
2735
+ }
2736
+ return 150; // Default fallback width
2737
+ }
2738
+ getOptionClasses(option) {
2739
+ const baseClasses = [
2740
+ 'cqa-p-1.5',
2741
+ 'cqa-rounded-md',
2742
+ 'cqa-inline-flex',
2743
+ 'cqa-justify-start',
2744
+ 'cqa-items-center',
2745
+ 'cqa-cursor-pointer',
2746
+ 'cqa-transition-colors'
2747
+ ];
2748
+ if (option.disabled) {
2749
+ baseClasses.push('cqa-opacity-50', 'cqa-cursor-not-allowed');
2750
+ }
2751
+ else {
2752
+ baseClasses.push('cqa-hover:cqa-bg-gray-50');
2753
+ }
2754
+ if (this.selectedValue === option.value) {
2755
+ baseClasses.push('cqa-bg-primary-surface');
2756
+ }
2757
+ return baseClasses.join(' ');
2758
+ }
2759
+ onDocumentClick(event) {
2760
+ // Ignore if click was inside the component (button click)
2761
+ if (this.clickInside) {
2762
+ return;
2763
+ }
2764
+ // Use a longer delay to ensure dropdown has time to render
2765
+ setTimeout(() => {
2766
+ if (!this.isOpen)
2767
+ return;
2768
+ const target = event.target;
2769
+ // Check if click is inside the component container
2770
+ if (this.dropdownContainer?.nativeElement?.contains(target)) {
2771
+ return;
2772
+ }
2773
+ // Close dropdown if click is outside
2774
+ this.isOpen = false;
2775
+ this.closed.emit();
2776
+ }, 200);
2777
+ }
2778
+ }
2779
+ DropdownButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DropdownButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2780
+ DropdownButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DropdownButtonComponent, selector: "cqa-dropdown-button", inputs: { label: "label", options: "options", selectedValue: "selectedValue", disabled: "disabled", placeholder: "placeholder" }, outputs: { selectionChange: "selectionChange", opened: "opened", closed: "closed" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "dropdownContainer", first: true, predicate: ["dropdownContainer"], descendants: true }, { propertyName: "buttonElement", first: true, predicate: ["buttonElement"], descendants: true }], ngImport: i0, template: "<div id=\"cqa-ui-root\" #dropdownContainer style=\"position: relative; display: inline-block; width: auto;\">\n <button\n #buttonElement\n type=\"button\"\n [class]=\"buttonClasses\"\n [disabled]=\"disabled\"\n (click)=\"toggleDropdown($event)\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-haspopup]=\"true\"\n >\n <div [class]=\"labelClasses\">\n {{ displayLabel }}\n </div>\n <div [class]=\"arrowClasses\">\n <svg\n class=\"cqa-w-2 cqa-h-1 cqa-absolute cqa-left-[4px] cqa-top-[6px]\"\n viewBox=\"0 0 8 4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M0 0L4 4L8 0\"\n stroke=\"#0B0B0C\"\n stroke-width=\"1.33\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n </button>\n\n <!-- Dropdown Menu -->\n <div\n *ngIf=\"isOpen && options && options.length > 0\"\n class=\"cqa-absolute cqa-top-full cqa-left-0 cqa-mt-1 cqa-p-1.5 cqa-bg-white cqa-rounded-lg cqa-shadow-[0px_2px_4px_-2px_rgba(0,0,0,0.10)] cqa-shadow-md cqa-outline cqa-outline-1 cqa-outline-offset-[-1px] cqa-outline-gray-200 cqa-flex cqa-flex-col cqa-justify-start cqa-items-start cqa-z-[9999] cqa-w-auto cqa-max-w-[350px]\"\n >\n <div class=\"cqa-flex cqa-flex-col cqa-justify-start cqa-items-start cqa-gap-0.5 cqa-w-full\">\n <div\n *ngFor=\"let option of options\"\n (click)=\"selectOption(option, $event)\"\n [class]=\"getOptionClasses(option)\"\n class=\"cqa-w-full cqa-whitespace-nowrap\"\n >\n <div class=\"cqa-w-6 cqa-h-4 cqa-pr-2 cqa-inline-flex cqa-flex-col cqa-justify-center cqa-items-center\">\n <mat-icon\n *ngIf=\"selectedValue === option.value\"\n class=\"cqa-w-4 cqa-h-4 cqa-text-primary cqa-text-base cqa-leading-4\"\n >check</mat-icon>\n </div>\n <div class=\"cqa-flex-1 cqa-inline-flex cqa-flex-col cqa-justify-start cqa-items-start\">\n <div class=\"cqa-justify-center cqa-text-gray-950 cqa-text-sm cqa-font-normal cqa-leading-5 cqa-font-geist\">\n {{ option.label }}\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n\n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
2781
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DropdownButtonComponent, decorators: [{
2782
+ type: Component,
2783
+ args: [{ selector: 'cqa-dropdown-button', template: "<div id=\"cqa-ui-root\" #dropdownContainer style=\"position: relative; display: inline-block; width: auto;\">\n <button\n #buttonElement\n type=\"button\"\n [class]=\"buttonClasses\"\n [disabled]=\"disabled\"\n (click)=\"toggleDropdown($event)\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-haspopup]=\"true\"\n >\n <div [class]=\"labelClasses\">\n {{ displayLabel }}\n </div>\n <div [class]=\"arrowClasses\">\n <svg\n class=\"cqa-w-2 cqa-h-1 cqa-absolute cqa-left-[4px] cqa-top-[6px]\"\n viewBox=\"0 0 8 4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M0 0L4 4L8 0\"\n stroke=\"#0B0B0C\"\n stroke-width=\"1.33\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n </button>\n\n <!-- Dropdown Menu -->\n <div\n *ngIf=\"isOpen && options && options.length > 0\"\n class=\"cqa-absolute cqa-top-full cqa-left-0 cqa-mt-1 cqa-p-1.5 cqa-bg-white cqa-rounded-lg cqa-shadow-[0px_2px_4px_-2px_rgba(0,0,0,0.10)] cqa-shadow-md cqa-outline cqa-outline-1 cqa-outline-offset-[-1px] cqa-outline-gray-200 cqa-flex cqa-flex-col cqa-justify-start cqa-items-start cqa-z-[9999] cqa-w-auto cqa-max-w-[350px]\"\n >\n <div class=\"cqa-flex cqa-flex-col cqa-justify-start cqa-items-start cqa-gap-0.5 cqa-w-full\">\n <div\n *ngFor=\"let option of options\"\n (click)=\"selectOption(option, $event)\"\n [class]=\"getOptionClasses(option)\"\n class=\"cqa-w-full cqa-whitespace-nowrap\"\n >\n <div class=\"cqa-w-6 cqa-h-4 cqa-pr-2 cqa-inline-flex cqa-flex-col cqa-justify-center cqa-items-center\">\n <mat-icon\n *ngIf=\"selectedValue === option.value\"\n class=\"cqa-w-4 cqa-h-4 cqa-text-primary cqa-text-base cqa-leading-4\"\n >check</mat-icon>\n </div>\n <div class=\"cqa-flex-1 cqa-inline-flex cqa-flex-col cqa-justify-start cqa-items-start\">\n <div class=\"cqa-justify-center cqa-text-gray-950 cqa-text-sm cqa-font-normal cqa-leading-5 cqa-font-geist\">\n {{ option.label }}\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n\n", styles: [] }]
2784
+ }], propDecorators: { label: [{
2785
+ type: Input
2786
+ }], options: [{
2787
+ type: Input
2788
+ }], selectedValue: [{
2789
+ type: Input
2790
+ }], disabled: [{
2791
+ type: Input
2792
+ }], placeholder: [{
2793
+ type: Input
2794
+ }], selectionChange: [{
2795
+ type: Output
2796
+ }], opened: [{
2797
+ type: Output
2798
+ }], closed: [{
2799
+ type: Output
2800
+ }], dropdownContainer: [{
2801
+ type: ViewChild,
2802
+ args: ['dropdownContainer', { static: false }]
2803
+ }], buttonElement: [{
2804
+ type: ViewChild,
2805
+ args: ['buttonElement', { static: false }]
2806
+ }], onDocumentClick: [{
2807
+ type: HostListener,
2808
+ args: ['document:click', ['$event']]
2809
+ }] } });
2810
+
2811
+ class HeatErrorMapCellComponent {
2812
+ constructor() {
2813
+ this.type = 'smoke';
2814
+ this.cases = 0;
2815
+ this.defects = 0;
2816
+ this.progress = 0; // Progress value from 0 to 100
2817
+ }
2818
+ get backgroundColorStyle() {
2819
+ switch (this.type) {
2820
+ case 'smoke':
2821
+ return { 'background-color': '#FDBA74' }; // orange-300
2822
+ case 'sanity':
2823
+ return { 'background-color': '#FCD34D' }; // amber-300
2824
+ case 'regression':
2825
+ return { 'background-color': '#A7F3D0' }; // emerald-200
2826
+ case 'revisit':
2827
+ return { 'background-color': '#F4F4F5' }; // zinc-100
2828
+ default:
2829
+ return { 'background-color': '#FDBA74' }; // orange-300
2830
+ }
2831
+ }
2832
+ get progressWidth() {
2833
+ // Clamp progress between 0 and 100
2834
+ const clampedProgress = Math.max(0, Math.min(100, this.progress));
2835
+ return `${clampedProgress}%`;
2836
+ }
2837
+ }
2838
+ HeatErrorMapCellComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: HeatErrorMapCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2839
+ HeatErrorMapCellComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: HeatErrorMapCellComponent, selector: "cqa-heat-error-map-cell", inputs: { type: "type", cases: "cases", defects: "defects", progress: "progress" }, ngImport: i0, template: "<div id=\"cqa-ui-root\" style=\"width: 14rem; height: 6rem; display: inline-flex; justify-content: space-between; align-items: flex-start;\">\n <div class=\"min-w-56 min-h-24 cqa-self-stretch cqa-flex cqa-flex-col cqa-rounded-2xl cqa-justify-start cqa-items-start cqa-gap-4 cqa-p-4\" [ngStyle]=\"backgroundColorStyle\">\n <!-- section 1 -->\n <div class=\"cqa-self-stretch cqa-flex cqa-flex-col cqa-justify-start cqa-items-start cqa-gap-px\">\n <div class=\"cqa-self-stretch cqa-flex cqa-flex-col cqa-justify-start cqa-items-start\">\n <div class=\"cqa-self-stretch cqa-justify-center cqa-text-gray-900 cqa-text-base cqa-font-bold cqa-leading-5 cqa-font-geist\">\n {{ cases }} cases\n </div>\n </div>\n <div class=\"cqa-self-stretch cqa-flex cqa-flex-col cqa-justify-start cqa-items-start\">\n <div class=\"cqa-self-stretch cqa-justify-center cqa-text-gray-900 cqa-text-xs cqa-font-normal cqa-leading-4 cqa-font-geist\">\n {{ defects }} defects\n </div>\n </div>\n </div>\n <!-- section 2 -->\n <div class=\"cqa-self-stretch cqa-h-1 cqa-relative cqa-rounded-full cqa-overflow-hidden cqa-bg-white/70\">\n <div \n class=\"cqa-h-1 cqa-left-0 cqa-top-0 cqa-absolute cqa-bg-red-500 cqa-rounded-full\"\n [style.width]=\"progressWidth\"\n ></div>\n </div>\n </div>\n</div>\n\n\n", directives: [{ type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
2840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: HeatErrorMapCellComponent, decorators: [{
2841
+ type: Component,
2842
+ args: [{ selector: 'cqa-heat-error-map-cell', template: "<div id=\"cqa-ui-root\" style=\"width: 14rem; height: 6rem; display: inline-flex; justify-content: space-between; align-items: flex-start;\">\n <div class=\"min-w-56 min-h-24 cqa-self-stretch cqa-flex cqa-flex-col cqa-rounded-2xl cqa-justify-start cqa-items-start cqa-gap-4 cqa-p-4\" [ngStyle]=\"backgroundColorStyle\">\n <!-- section 1 -->\n <div class=\"cqa-self-stretch cqa-flex cqa-flex-col cqa-justify-start cqa-items-start cqa-gap-px\">\n <div class=\"cqa-self-stretch cqa-flex cqa-flex-col cqa-justify-start cqa-items-start\">\n <div class=\"cqa-self-stretch cqa-justify-center cqa-text-gray-900 cqa-text-base cqa-font-bold cqa-leading-5 cqa-font-geist\">\n {{ cases }} cases\n </div>\n </div>\n <div class=\"cqa-self-stretch cqa-flex cqa-flex-col cqa-justify-start cqa-items-start\">\n <div class=\"cqa-self-stretch cqa-justify-center cqa-text-gray-900 cqa-text-xs cqa-font-normal cqa-leading-4 cqa-font-geist\">\n {{ defects }} defects\n </div>\n </div>\n </div>\n <!-- section 2 -->\n <div class=\"cqa-self-stretch cqa-h-1 cqa-relative cqa-rounded-full cqa-overflow-hidden cqa-bg-white/70\">\n <div \n class=\"cqa-h-1 cqa-left-0 cqa-top-0 cqa-absolute cqa-bg-red-500 cqa-rounded-full\"\n [style.width]=\"progressWidth\"\n ></div>\n </div>\n </div>\n</div>\n\n\n", styles: [] }]
2843
+ }], propDecorators: { type: [{
2844
+ type: Input
2845
+ }], cases: [{
2846
+ type: Input
2847
+ }], defects: [{
2848
+ type: Input
2849
+ }], progress: [{
2850
+ type: Input
2851
+ }] } });
2852
+
2853
+ class EmptyStateComponent {
2854
+ constructor() {
2855
+ this.title = '';
2856
+ this.description = '';
2857
+ this.actions = [];
2858
+ this.actionClick = new EventEmitter();
2859
+ }
2860
+ onActionClick(action, event) {
2861
+ if (!action.disabled) {
2862
+ if (action.onClick) {
2863
+ action.onClick();
2864
+ }
2865
+ this.actionClick.emit(action);
2866
+ }
2867
+ }
2868
+ }
2869
+ EmptyStateComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: EmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2870
+ EmptyStateComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: EmptyStateComponent, selector: "cqa-empty-state", inputs: { imageUrl: "imageUrl", title: "title", description: "description", actions: "actions" }, outputs: { actionClick: "actionClick" }, ngImport: i0, template: "<div id=\"cqa-ui-root\" style=\"background-color: white; border-radius: 14px; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 84.63px 33px 84.62px 33px;\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-[62px] cqa-items-center\">\n <!-- Icon Container -->\n <div *ngIf=\"imageUrl\" class=\"cqa-relative cqa-shrink-0 cqa-w-32 cqa-h-32\">\n <!-- Main Icon Container with Gradient Background and Shadow -->\n <div class=\"cqa-relative cqa-rounded-3xl cqa-w-32 cqa-h-32 cqa-shadow-sm\">\n <div class=\"cqa-absolute cqa-inset-0 cqa-bg-gradient-to-br cqa-from-indigo-500 cqa-to-violet-950 cqa-rounded-3xl cqa-opacity-10\"></div>\n <!-- Icon/Image centered inside on top layer - fully opaque -->\n <div class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-rounded-3xl\">\n <div class=\"cqa-w-20 cqa-h-20 cqa-flex cqa-items-center cqa-justify-center cqa-relative\">\n <img [src]=\"imageUrl\" alt=\"\" width=\"80px\" height=\"80px\" class=\"cqa-block cqa-max-w-none cqa-w-20 cqa-h-20 cqa-object-contain\" />\n </div>\n </div>\n </div>\n <!-- Decorative Dots -->\n <div class=\"cqa-absolute cqa-rounded-full cqa-bg-primary-300 cqa-opacity-[0.815]\" style=\"left: 120.79px; top: -9.21px; width: 18.416px; height: 18.416px; z-index: 20;\"></div>\n <div class=\"cqa-absolute cqa-rounded-full cqa-bg-primary-300 cqa-opacity-[0.695]\" style=\"left: -9.02px; top: 124.98px; width: 14.044px; height: 14.044px; z-index: 20;\"></div>\n </div>\n\n <!-- Content Container -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-9 cqa-items-center\">\n <!-- Title and Description -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-3 cqa-items-center cqa-w-full\">\n <!-- Title -->\n <div *ngIf=\"title\" class=\"cqa-flex cqa-flex-col cqa-items-center cqa-w-full\">\n <h3 class=\"cqa-font-inter cqa-text-lg cqa-font-medium cqa-leading-[18px] cqa-text-center cqa-text-neutral-900\">\n {{ title }}\n </h3>\n </div>\n <!-- Description -->\n <div *ngIf=\"description\" class=\"cqa-flex cqa-flex-col cqa-items-center cqa-w-full\">\n <p class=\"cqa-font-inter cqa-font-medium cqa-text-sm cqa-leading-[14px] cqa-text-center cqa-text-neutral-500\">\n {{ description }}\n </p>\n </div>\n </div>\n\n <!-- Action Buttons -->\n <div\n *ngIf=\"actions && actions.length > 0\"\n class=\"cqa-flex cqa-items-center cqa-justify-center\"\n [ngClass]=\"actions.length > 1 ? 'cqa-flex-row cqa-flex-wrap cqa-gap-4' : 'cqa-flex-col cqa-gap-2'\"\n >\n <cqa-button\n *ngFor=\"let action of actions\"\n [variant]=\"action.variant || 'filled'\"\n [icon]=\"action.icon\"\n [iconPosition]=\"action.iconPosition || 'start'\"\n [disabled]=\"action.disabled\"\n (clicked)=\"onActionClick(action, $event)\"\n >\n {{ action.label }}\n </cqa-button>\n </div>\n </div>\n </div>\n</div>\n\n", components: [{ type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "disabled", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass"], outputs: ["clicked"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
2871
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: EmptyStateComponent, decorators: [{
2872
+ type: Component,
2873
+ args: [{ selector: 'cqa-empty-state', template: "<div id=\"cqa-ui-root\" style=\"background-color: white; border-radius: 14px; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 84.63px 33px 84.62px 33px;\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-[62px] cqa-items-center\">\n <!-- Icon Container -->\n <div *ngIf=\"imageUrl\" class=\"cqa-relative cqa-shrink-0 cqa-w-32 cqa-h-32\">\n <!-- Main Icon Container with Gradient Background and Shadow -->\n <div class=\"cqa-relative cqa-rounded-3xl cqa-w-32 cqa-h-32 cqa-shadow-sm\">\n <div class=\"cqa-absolute cqa-inset-0 cqa-bg-gradient-to-br cqa-from-indigo-500 cqa-to-violet-950 cqa-rounded-3xl cqa-opacity-10\"></div>\n <!-- Icon/Image centered inside on top layer - fully opaque -->\n <div class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-rounded-3xl\">\n <div class=\"cqa-w-20 cqa-h-20 cqa-flex cqa-items-center cqa-justify-center cqa-relative\">\n <img [src]=\"imageUrl\" alt=\"\" width=\"80px\" height=\"80px\" class=\"cqa-block cqa-max-w-none cqa-w-20 cqa-h-20 cqa-object-contain\" />\n </div>\n </div>\n </div>\n <!-- Decorative Dots -->\n <div class=\"cqa-absolute cqa-rounded-full cqa-bg-primary-300 cqa-opacity-[0.815]\" style=\"left: 120.79px; top: -9.21px; width: 18.416px; height: 18.416px; z-index: 20;\"></div>\n <div class=\"cqa-absolute cqa-rounded-full cqa-bg-primary-300 cqa-opacity-[0.695]\" style=\"left: -9.02px; top: 124.98px; width: 14.044px; height: 14.044px; z-index: 20;\"></div>\n </div>\n\n <!-- Content Container -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-9 cqa-items-center\">\n <!-- Title and Description -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-3 cqa-items-center cqa-w-full\">\n <!-- Title -->\n <div *ngIf=\"title\" class=\"cqa-flex cqa-flex-col cqa-items-center cqa-w-full\">\n <h3 class=\"cqa-font-inter cqa-text-lg cqa-font-medium cqa-leading-[18px] cqa-text-center cqa-text-neutral-900\">\n {{ title }}\n </h3>\n </div>\n <!-- Description -->\n <div *ngIf=\"description\" class=\"cqa-flex cqa-flex-col cqa-items-center cqa-w-full\">\n <p class=\"cqa-font-inter cqa-font-medium cqa-text-sm cqa-leading-[14px] cqa-text-center cqa-text-neutral-500\">\n {{ description }}\n </p>\n </div>\n </div>\n\n <!-- Action Buttons -->\n <div\n *ngIf=\"actions && actions.length > 0\"\n class=\"cqa-flex cqa-items-center cqa-justify-center\"\n [ngClass]=\"actions.length > 1 ? 'cqa-flex-row cqa-flex-wrap cqa-gap-4' : 'cqa-flex-col cqa-gap-2'\"\n >\n <cqa-button\n *ngFor=\"let action of actions\"\n [variant]=\"action.variant || 'filled'\"\n [icon]=\"action.icon\"\n [iconPosition]=\"action.iconPosition || 'start'\"\n [disabled]=\"action.disabled\"\n (clicked)=\"onActionClick(action, $event)\"\n >\n {{ action.label }}\n </cqa-button>\n </div>\n </div>\n </div>\n</div>\n\n", styles: [] }]
2874
+ }], propDecorators: { imageUrl: [{
2875
+ type: Input
2876
+ }], title: [{
2877
+ type: Input
2878
+ }], description: [{
2879
+ type: Input
2880
+ }], actions: [{
2881
+ type: Input
2882
+ }], actionClick: [{
2883
+ type: Output
2884
+ }] } });
2885
+
2886
+ /**
2887
+ * Image assets constants for the UI library
2888
+ *
2889
+ * Place your image files in src/lib/assets/images/ and reference them here
2890
+ *
2891
+ * Usage in components:
2892
+ * import { EMPTY_STATE_IMAGES } from '../assets/images/image-assets.constants';
2893
+ * imageUrl: EMPTY_STATE_IMAGES.TEST_CASE
2894
+ */
2895
+ const EMPTY_STATE_IMAGES = {
2896
+ // Test Case icon (document with gear)
2897
+ TEST_CASE: 'assets/images/TestCaseIcon.png',
2898
+ // Search/Debug icon (magnifying glass with question mark)
2899
+ SEARCH_DEBUG: 'assets/images/SearchIcon.png',
2900
+ // Upload/Folder icon (folder with upload arrow and plus)
2901
+ UPLOAD_FOLDER: 'assets/images/FilesIcon.png',
2902
+ // Dashboard overview
2903
+ DASHBOARD: 'assets/images/DashboardIcon.png',
2904
+ // Checklist/Add icon (clipboard with plus)
2905
+ CHECKLIST_ADD: 'assets/images/StepsIcon.png',
2906
+ // Document/Gear icon (document with gear overlay)
2907
+ DOCUMENT_GEAR: 'assets/images/document-gear-icon.svg',
2908
+ // Analytics/Chart icon (bar chart)
2909
+ ANALYTICS_CHART: 'assets/images/ReportsIcon.png',
2910
+ // Default empty state icon
2911
+ DEFAULT: 'assets/images/SearchIcon.png',
2912
+ };
2913
+
2914
+ class TableTemplateComponent {
2915
+ constructor() {
2916
+ // Search bar inputs
2917
+ this.searchPlaceholder = 'Search components';
2918
+ this.searchValue = '';
2919
+ this.showClear = true;
2920
+ this.showSearchBar = true;
2921
+ // Filter inputs
2922
+ this.filterConfig = [];
2923
+ this.showFilterPanel = false;
2924
+ this.showFilterButton = true;
2925
+ // Other button input
2926
+ this.otherButtonLabel = 'Other Button';
2927
+ this.otherButtonVariant = 'filled';
2928
+ this.showOtherButton = true;
2929
+ // Action menu button (three-dot menu in table rows)
2930
+ this.showActionButton = true;
2931
+ // Settings and refresh buttons
2932
+ this.showSettingsButton = true;
2933
+ this.showAutoRefreshButton = true;
2934
+ // Data input
2935
+ this.data = [];
2936
+ // Empty state inputs
2937
+ this.isEmptyState = false;
2938
+ this.emptyStateConfig = {
2939
+ title: 'No Data Available Yet',
2940
+ description: 'Run or upload your first test to see your analytics and trends here. Watch your quality metrics come to life with real-time insights.',
2941
+ imageUrl: EMPTY_STATE_IMAGES.DASHBOARD,
2942
+ actions: [{ label: 'Run Test Suite', variant: 'filled' }],
2943
+ };
2944
+ // Action bar inputs
2945
+ this.actions = [
2946
+ {
2947
+ id: 'delete',
2948
+ label: 'Delete',
2949
+ icon: 'delete',
2950
+ tooltip: 'Delete selected',
2951
+ onClick: (context) => {
2952
+ console.log('Delete action clicked:', context);
2953
+ }
2954
+ },
2955
+ {
2956
+ id: 'edit',
2957
+ label: 'Edit',
2958
+ icon: 'edit',
2959
+ tooltip: 'Edit selected',
2960
+ onClick: (context) => {
2961
+ console.log('Edit action clicked:', context);
2962
+ }
2963
+ },
2964
+ {
2965
+ id: 'add-tag',
2966
+ label: 'Add Tag',
2967
+ icon: 'local_offer',
2968
+ tooltip: 'Add tags',
2969
+ onClick: (context) => {
2970
+ console.log('Add tag action clicked:', context);
2971
+ }
2972
+ },
2973
+ {
2974
+ id: 'remove-tag',
2975
+ label: 'Remove Tag',
2976
+ icon: 'label_off',
2977
+ tooltip: 'Remove tags',
2978
+ onClick: (context) => {
2979
+ console.log('Remove tag action clicked:', context);
2980
+ }
2981
+ },
2982
+ ];
2983
+ // Chips inputs
2984
+ this.chips = [];
2985
+ this.filterApplied = false;
2986
+ // Table inputs
2987
+ this.columns = [];
2988
+ this.selectedAutoRefreshInterval = 0;
2989
+ this.pageIndex = 0;
2990
+ this.pageSize = 10;
2991
+ // Backward-compatibility flag; if provided, dynamic table will use it when specific flags are undefined
2992
+ // Internal state for column visibility
2993
+ this._columnVisibility = {};
2994
+ this._cachedVisibilityColumns = [];
2995
+ this.filteredRows = [];
2996
+ this.pagedRows = [];
2997
+ }
2998
+ // Derived columns with visibility applied. Avoid mutating @Input() columns so parent bindings aren't overridden.
2999
+ get computedColumns() {
3000
+ const visibility = this._columnVisibility || {};
3001
+ const source = this.columns || [];
3002
+ return source.map(col => {
3003
+ if (['checkbox', 'actions'].includes(col.fieldId)) {
3004
+ return col;
3005
+ }
3006
+ const show = visibility[col.fieldId];
3007
+ return { ...col, isShow: show !== false };
3008
+ });
3009
+ }
3010
+ // Auto-generated visibility columns from columns input (excludes default columns and checkbox/actions)
3011
+ // Cached to avoid creating new arrays on every change detection cycle
3012
+ get visibilityColumns() {
3013
+ return this._cachedVisibilityColumns;
3014
+ }
3015
+ // Internal column visibility state
3016
+ get columnVisibility() {
3017
+ return this._columnVisibility;
3018
+ }
3019
+ ngOnInit() {
3020
+ this.initializeComponent();
3021
+ }
3022
+ ngOnChanges(changes) {
3023
+ if (changes['data'] || changes['isEmptyState']) {
3024
+ this.initializeComponent();
3025
+ }
3026
+ if (changes['columns']) {
3027
+ this.initializeColumnVisibility();
3028
+ }
3029
+ }
3030
+ initializeComponent() {
3031
+ if (this.isEmptyState) {
3032
+ this.filteredRows = [];
3033
+ this.pagedRows = [];
3034
+ return;
3035
+ }
3036
+ this.filteredRows = [...this.data];
3037
+ this.applyPagination();
3038
+ this.initializeColumnVisibility();
3039
+ }
3040
+ initializeColumnVisibility() {
3041
+ // Cache visibility columns to avoid creating new arrays on every change detection
3042
+ this._cachedVisibilityColumns = this.mapVisibilityColumns();
3043
+ // Initialize visibility state for all visibility columns (default to true)
3044
+ for (const col of this._cachedVisibilityColumns) {
3045
+ if (this._columnVisibility[col.id] === undefined) {
3046
+ this._columnVisibility[col.id] = true;
3047
+ }
3048
+ }
3049
+ }
3050
+ get anyRowSelected() {
3051
+ return !!(this.pagedRows && this.pagedRows.some(r => !!r.isSelected));
3052
+ }
3053
+ get currentSelectedItems() {
3054
+ return (this.pagedRows || []).filter(r => !!r.isSelected);
3055
+ }
3056
+ actionClick(data) {
3057
+ console.log('action toolbar', data);
3058
+ }
3059
+ view(id) {
3060
+ console.log('View', id);
3061
+ }
3062
+ edit(row) {
3063
+ console.log('Edit', row);
3064
+ }
3065
+ delete(row) {
3066
+ console.log('Delete', row);
3067
+ }
3068
+ onRowCheckboxChange(event, row) {
3069
+ const input = event.target;
3070
+ const checked = !!input?.checked;
3071
+ row.isSelected = checked;
3072
+ }
3073
+ toggleFilter() {
3074
+ this.showFilterPanel = !this.showFilterPanel;
3075
+ }
3076
+ onColumnVisibilityChange(cfg) {
3077
+ this._columnVisibility = { ...cfg };
3078
+ // Do not mutate this.columns; computedColumns getter will reflect changes
3079
+ }
3080
+ onAutoRefreshChange(intervalMs) {
3081
+ this.selectedAutoRefreshInterval = intervalMs;
3082
+ console.log('Auto refresh interval', intervalMs);
3083
+ }
3084
+ valueChange(value) {
3085
+ console.log('Value changed', value);
3086
+ }
3087
+ search(value) {
3088
+ console.log('Search', value);
3089
+ }
3090
+ cleared() {
3091
+ console.log('Cleared');
3092
+ }
3093
+ resultBadgeClass(result) {
3094
+ const value = (result || '').toUpperCase();
3095
+ if (value === 'SUCCESS')
3096
+ return 'cqa-bg-green-100 cqa-text-green-700';
3097
+ if (value === 'FAILURE' || value === 'ABORTED')
3098
+ return 'cqa-bg-red-100 cqa-text-red-700';
3099
+ if (value === 'QUEUED' || value === 'NOT_EXECUTED' || value === 'STOPPED')
3100
+ return 'cqa-bg-gray-100 cqa-text-gray-700';
3101
+ return 'cqa-bg-gray-100 cqa-text-gray-700';
3102
+ }
3103
+ onEmptyAction(action) {
3104
+ if (action?.label === 'Show filters') {
3105
+ this.toggleFilter();
3106
+ }
3107
+ }
3108
+ onFiltersChanged(current) {
3109
+ this.filteredRows = this.data.filter(r => this.passFilters(current, r));
3110
+ this.pageIndex = 0;
3111
+ this.applyPagination();
3112
+ const chips = [];
3113
+ if (current) {
3114
+ for (const key of Object.keys(current)) {
3115
+ const value = current[key];
3116
+ if (value == null || value === '' || (Array.isArray(value) && value.length === 0))
3117
+ continue;
3118
+ let text = '';
3119
+ if (Array.isArray(value)) {
3120
+ text = value.map((v) => (v?.name ?? v?.label ?? v?.value ?? v)).join(', ');
3121
+ }
3122
+ else if (typeof value === 'object') {
3123
+ if ('start' in value || 'end' in value) {
3124
+ const s = value.start ? new Date(value.start).toLocaleDateString() : '';
3125
+ const e = value.end ? new Date(value.end).toLocaleDateString() : '';
3126
+ text = [s, e].filter(Boolean).join(' - ');
3127
+ }
3128
+ else {
3129
+ text = (value?.name ?? value?.label ?? value?.value ?? JSON.stringify(value));
3130
+ }
3131
+ }
3132
+ else {
3133
+ text = String(value);
3134
+ }
3135
+ chips.push({ key, text, fullText: text, hasMore: text.length > 30 });
3136
+ }
3137
+ }
3138
+ this.chips = chips;
3139
+ this.filterApplied = this.chips.length > 0;
3140
+ }
3141
+ onFiltersApplied(_) {
3142
+ // handled in onFiltersChanged for this demo
3143
+ }
3144
+ onPaginate(e) {
3145
+ this.pageIndex = e.pageIndex;
3146
+ this.pageSize = e.pageSize;
3147
+ this.applyPagination();
3148
+ }
3149
+ onPageSizeChange(size) {
3150
+ this.pageSize = size;
3151
+ this.pageIndex = 0;
3152
+ this.applyPagination();
3153
+ }
3154
+ onRemoveChip(chip) {
3155
+ this.chips = this.chips.filter(c => c !== chip);
3156
+ this.filterApplied = this.chips.length > 0;
3157
+ }
3158
+ onClearAllChips() {
3159
+ this.chips = [];
3160
+ this.filterApplied = false;
3161
+ }
3162
+ applyPagination() {
3163
+ const start = this.pageIndex * this.pageSize;
3164
+ const end = start + this.pageSize;
3165
+ this.pagedRows = this.filteredRows.slice(start, end);
3166
+ }
3167
+ mapVisibilityColumns() {
3168
+ return (this.columns || [])
3169
+ .filter(c => c.isDefault === false)
3170
+ .map(c => ({ id: c.fieldId, label: c.fieldName || c.fieldId }));
3171
+ }
3172
+ normalizeDate(d) {
3173
+ if (!d)
3174
+ return null;
3175
+ const ts = Date.parse(d);
3176
+ return isNaN(ts) ? null : ts;
3177
+ }
3178
+ passFilters(filters, row) {
3179
+ if (!filters)
3180
+ return true;
3181
+ if (filters.status && Array.isArray(filters.status) && filters.status.length) {
3182
+ if (!filters.status.includes(row.status))
3183
+ return false;
3184
+ }
3185
+ if (filters.priority && Array.isArray(filters.priority) && filters.priority.length) {
3186
+ if (!filters.priority.includes(row.priorityName))
3187
+ return false;
3188
+ }
3189
+ if (filters.testType && Array.isArray(filters.testType) && filters.testType.length) {
3190
+ if (!filters.testType.includes(row.testType))
3191
+ return false;
3192
+ }
3193
+ if (filters.created_date && (filters.created_date.start || filters.created_date.end)) {
3194
+ const startTs = this.normalizeDate(filters.created_date.start);
3195
+ const endTs = this.normalizeDate(filters.created_date.end);
3196
+ const rowTs = this.normalizeDate(row.createdAt);
3197
+ if (rowTs != null) {
3198
+ if (startTs != null && rowTs < startTs)
3199
+ return false;
3200
+ if (endTs != null && rowTs > endTs)
3201
+ return false;
3202
+ }
3203
+ }
3204
+ return true;
3205
+ }
3206
+ }
3207
+ TableTemplateComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TableTemplateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3208
+ TableTemplateComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TableTemplateComponent, selector: "cqa-table-template", inputs: { searchPlaceholder: "searchPlaceholder", searchValue: "searchValue", showClear: "showClear", showSearchBar: "showSearchBar", filterConfig: "filterConfig", showFilterPanel: "showFilterPanel", showFilterButton: "showFilterButton", otherButtonLabel: "otherButtonLabel", otherButtonVariant: "otherButtonVariant", showOtherButton: "showOtherButton", showActionButton: "showActionButton", showSettingsButton: "showSettingsButton", showAutoRefreshButton: "showAutoRefreshButton", data: "data", isEmptyState: "isEmptyState", emptyStateConfig: "emptyStateConfig", actions: "actions", chips: "chips", filterApplied: "filterApplied", columns: "columns", selectedAutoRefreshInterval: "selectedAutoRefreshInterval", pageIndex: "pageIndex", pageSize: "pageSize", isTableLoading: "isTableLoading", isTableDataLoading: "isTableDataLoading" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-flex cqa-flex-col cqa-relative\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center cqa-justify-between cqa-gap-3 cqa-flex-wrap cqa-mb-3\">\n <cqa-search-bar\n *ngIf=\"showSearchBar\"\n [placeholder]=\"searchPlaceholder\"\n [value]=\"searchValue\"\n [showClear]=\"showClear\"\n (valueChange)=\"valueChange($event)\"\n (search)=\"search($event)\"\n (cleared)=\"cleared()\"\n ></cqa-search-bar>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-flex-wrap\">\n <cqa-button\n *ngIf=\"showFilterButton\"\n variant=\"grey-solid\"\n icon=\"add\"\n [text]=\"'Filter'\"\n (clicked)=\"toggleFilter()\"\n >\n <span>Filter</span>\n </cqa-button>\n <cqa-column-visibility\n *ngIf=\"showSettingsButton\"\n [columns]=\"visibilityColumns\"\n [columnVisibility]=\"columnVisibility\"\n [selectedAutoRefreshInterval]=\"selectedAutoRefreshInterval\"\n (columnVisibilityChange)=\"onColumnVisibilityChange($event)\"\n (autoRefreshChange)=\"onAutoRefreshChange($event)\"\n ></cqa-column-visibility>\n <cqa-button *ngIf=\"showAutoRefreshButton\" variant=\"grey-solid\" icon=\"refresh\"></cqa-button>\n <cqa-button *ngIf=\"showOtherButton\" [variant]=\"otherButtonVariant\" [text]=\"otherButtonLabel\"></cqa-button>\n </div>\n </div>\n\n <cqa-selected-filters \n *ngIf=\"showFilterPanel\"\n [filterApplied]=\"filterApplied\"\n [chips]=\"chips\"\n (removeChip)=\"onRemoveChip($event)\"\n (clearAll)=\"onClearAllChips()\"\n >\n </cqa-selected-filters>\n\n <cqa-dynamic-filter\n *ngIf=\"showFilterPanel\"\n [config]=\"filterConfig\"\n [showFilterPanel]=\"showFilterPanel\"\n (filtersChanged)=\"onFiltersChanged($event)\"\n (filtersApplied)=\"onFiltersApplied($event)\"\n >\n </cqa-dynamic-filter>\n\n <div class=\"cqa-rounded-[7px] cqa-overflow-hidden cqa-border-t cqa-border-l cqa-border-r cqa-border-grey-200 cqa-relative\">\n <ng-container *ngIf=\"!isEmptyState && pagedRows && pagedRows.length > 0; else storyEmptyTpl\">\n <app-dynamic-table\n [columns]=\"computedColumns\"\n [data]=\"pagedRows\"\n [isTableLoading]=\"isTableLoading\"\n [isTableDataLoading]=\"isTableDataLoading\">\n <ng-template dynamicCell=\"checkbox\" let-row=\"row\">\n <div class=\"custom-checkbox\">\n <input\n type=\"checkbox\"\n class=\"hidden-checkbox\"\n [attr.id]=\"'row-checkbox-' + row.id\"\n [checked]=\"row.isSelected\"\n aria-label=\"Select row\"\n (change)=\"onRowCheckboxChange($event, row)\" />\n <label\n class=\"custom-checkbox-label\"\n [attr.for]=\"'row-checkbox-' + row.id\">\n </label>\n </div>\n </ng-template>\n\n <ng-template dynamicCell=\"testCases\" let-row=\"row\" let-value=\"value\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-1 cqa-text-[12.3px] cqa-leading-[17.5px] cqa-font-medium\">\n <div class=\"cqa-text-[#3F43EE] cqa-truncate\">\n #{{ row.id }}\n </div>\n <div class=\"cqa-text-[#0A0A0A]\">{{ value }}</div>\n </div>\n </ng-template>\n\n <ng-template dynamicCell=\"type\" let-row=\"row\" let-value=\"value\">\n <div class=\"cqa-text-xs cqa-text-[#111827] cqa-truncate\">{{ value }}</div>\n </ng-template>\n\n <ng-template dynamicCell=\"priority\" let-row=\"row\" let-value=\"value\">\n <span\n class=\"cqa-inline-flex cqa-items-center cqa-px-2 cqa-py-0.5 cqa-rounded cqa-text-xs\"\n [ngClass]=\"{\n 'cqa-bg-red-100 cqa-text-red-700': value === 'Critical' || value === 'Major',\n 'cqa-bg-yellow-100 cqa-text-yellow-700': value === 'Medium',\n 'cqa-bg-blue-100 cqa-text-blue-700': value === 'Minor',\n 'cqa-bg-gray-100 cqa-text-gray-700': !value || value === 'Not Set'\n }\"\n >{{ value || 'Not Set' }}</span\n >\n </ng-template>\n\n <ng-template dynamicCell=\"result\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-inline-flex cqa-items-center cqa-px-2 cqa-py-0.5 cqa-rounded cqa-text-xs\" [ngClass]=\"resultBadgeClass(value)\">\n {{ value || 'NOT_EXECUTED' }}\n </span>\n </ng-template>\n\n <ng-template dynamicCell=\"status\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-text-xs\">{{ value }}</span>\n </ng-template>\n\n <ng-template dynamicCell=\"labels\" let-row=\"row\" let-value=\"value\">\n <div class=\"cqa-flex cqa-flex-wrap cqa-gap-1\">\n <span *ngFor=\"let tag of (row.tags || []) | slice: 0:3\" class=\"cqa-px-2 cqa-py-0.5 cqa-rounded cqa-bg-gray-100 cqa-text-gray-700 cqa-text-xs\">\n {{ tag }}\n </span>\n <span *ngIf=\"(row.tags?.length || 0) > 3\" class=\"cqa-px-2 cqa-py-0.5 cqa-rounded cqa-bg-gray-100 cqa-text-gray-700 cqa-text-xs\">\n +{{ (row.tags?.length || 0) - 3 }} more\n </span>\n </div>\n </ng-template>\n\n <ng-template dynamicCell=\"createdBy\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-text-xs\">{{ value }}</span>\n </ng-template>\n\n <ng-template dynamicCell=\"createdAt\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-text-xs cqa-text-gray-700\">{{ value | date: 'medium' }}</span>\n </ng-template>\n\n <ng-template dynamicCell=\"updatedAt\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-text-xs cqa-text-gray-700\">{{ value | date: 'medium' }}</span>\n </ng-template>\n\n <ng-template dynamicCell=\"lastRun\" let-row=\"row\">\n <div class=\"cqa-flex cqa-flex-col\">\n <span class=\"cqa-text-xs cqa-text-[#111827]\">{{ row.lastRun?.startTime | date: 'medium' }}</span>\n </div>\n </ng-template>\n\n <ng-template dynamicCell=\"actions\" let-row=\"row\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\" *ngIf=\"showActionButton\">\n <cqa-action-menu-button\n [row]=\"row\"\n (view)=\"view($event)\"\n (edit)=\"edit($event)\"\n (delete)=\"delete($event)\"\n ></cqa-action-menu-button>\n </div>\n </ng-template>\n\n <ng-template #emptyTableTpl>\n <div class=\"cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-py-8\">\n <img src=\"/assets/illustrations/empty-state.svg\" alt=\"No data\" class=\"cqa-w-32 cqa-h-32 cqa-mb-4\" />\n <h3 class=\"cqa-text-lg cqa-font-semibold cqa-mb-2\">No test cases</h3>\n <p class=\"cqa-text-sm cqa-text-neutral-500 cqa-mb-4\">Try adjusting filters or create a new test case.</p>\n <cqa-button variant=\"filled\" (clicked)=\"toggleFilter()\">Show Filters</cqa-button>\n </div>\n </ng-template>\n </app-dynamic-table>\n </ng-container>\n\n <ng-template #storyEmptyTpl>\n <div class=\"cqa-p-6 cqa-flex cqa-flex-col cqa-items-center cqa-justify-center\">\n <cqa-empty-state\n *ngIf=\"isEmptyState\"\n [title]=\"emptyStateConfig.title\"\n [description]=\"emptyStateConfig.description\"\n [imageUrl]=\"emptyStateConfig.imageUrl\"\n [actions]=\"emptyStateConfig.actions\"\n (actionClick)=\"onEmptyAction($event)\"\n >\n </cqa-empty-state>\n </div>\n </ng-template>\n\n </div>\n\n <cqa-pagination\n [totalElements]=\"filteredRows.length\"\n [pageIndex]=\"pageIndex\"\n [pageSize]=\"pageSize\"\n [pageItemCount]=\"pagedRows.length\"\n (paginate)=\"onPaginate($event)\"\n (pageSizeChange)=\"onPageSizeChange($event)\"\n >\n </cqa-pagination>\n\n <div *ngIf=\"anyRowSelected\" class=\"cqa-absolute cqa-bottom-[18.75px] cqa-left-[50%] cqa-translate-x-[-50%] cqa-w-full lg:cqa-max-w-[68%] cqa-sm:max-w-[75%] cqa-max-w-[90%] cqa-z-[1]\" >\n <cqa-table-action-toolbar\n [selectedItems]=\"currentSelectedItems\"\n [actions]=\"actions\"\n (actionClick)=\"actionClick($event)\"\n ></cqa-table-action-toolbar>\n </div>\n \n </div>\n</div>\n\n", components: [{ type: SearchBarComponent, selector: "cqa-search-bar", inputs: ["placeholder", "value", "disabled", "showClear", "ariaLabel", "autoFocus", "size", "fullWidth"], outputs: ["valueChange", "search", "cleared"] }, { type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "disabled", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass"], outputs: ["clicked"] }, { type: ColumnVisibilityComponent, selector: "cqa-column-visibility", inputs: ["isStepGroup", "columns", "columnVisibility", "selectedAutoRefreshInterval"], outputs: ["columnVisibilityChange", "autoRefreshChange"] }, { type: SelectedFiltersComponent, selector: "cqa-selected-filters", inputs: ["filterApplied", "chips"], outputs: ["removeChip", "clearAll"] }, { type: DynamicFilterComponent, selector: "cqa-dynamic-filter", inputs: ["config", "model", "showFilterPanel"], outputs: ["filtersApplied", "filtersChanged", "resetAction"] }, { type: DynamicTableComponent, selector: "app-dynamic-table", inputs: ["data", "columns", "emptyState", "gridTemplateColumns", "screenWidth", "enableSelectAll", "enableLocalSort", "isTableLoading", "isTableDataLoading"], outputs: ["sortChange"] }, { type: ActionMenuButtonComponent, selector: "cqa-action-menu-button", inputs: ["row"], outputs: ["view", "edit", "delete"] }, { type: EmptyStateComponent, selector: "cqa-empty-state", inputs: ["imageUrl", "title", "description", "actions"], outputs: ["actionClick"] }, { type: PaginationComponent, selector: "cqa-pagination", inputs: ["totalElements", "totalPages", "pageIndex", "pageSize", "pageItemCount", "pageSizeOptions"], outputs: ["pageIndexChange", "pageSizeChange", "paginate"] }, { type: TableActionToolbarComponent, selector: "cqa-table-action-toolbar", inputs: ["selectedItems", "actions"], outputs: ["actionClick"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: DynamicCellTemplateDirective, selector: "ng-template[dynamicCell]", inputs: ["dynamicCell"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "slice": i2.SlicePipe, "date": i2.DatePipe } });
3209
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TableTemplateComponent, decorators: [{
3210
+ type: Component,
3211
+ args: [{ selector: 'cqa-table-template', template: "<div id=\"cqa-ui-root\">\n <div class=\"cqa-w-full cqa-flex cqa-flex-col cqa-relative\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center cqa-justify-between cqa-gap-3 cqa-flex-wrap cqa-mb-3\">\n <cqa-search-bar\n *ngIf=\"showSearchBar\"\n [placeholder]=\"searchPlaceholder\"\n [value]=\"searchValue\"\n [showClear]=\"showClear\"\n (valueChange)=\"valueChange($event)\"\n (search)=\"search($event)\"\n (cleared)=\"cleared()\"\n ></cqa-search-bar>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-flex-wrap\">\n <cqa-button\n *ngIf=\"showFilterButton\"\n variant=\"grey-solid\"\n icon=\"add\"\n [text]=\"'Filter'\"\n (clicked)=\"toggleFilter()\"\n >\n <span>Filter</span>\n </cqa-button>\n <cqa-column-visibility\n *ngIf=\"showSettingsButton\"\n [columns]=\"visibilityColumns\"\n [columnVisibility]=\"columnVisibility\"\n [selectedAutoRefreshInterval]=\"selectedAutoRefreshInterval\"\n (columnVisibilityChange)=\"onColumnVisibilityChange($event)\"\n (autoRefreshChange)=\"onAutoRefreshChange($event)\"\n ></cqa-column-visibility>\n <cqa-button *ngIf=\"showAutoRefreshButton\" variant=\"grey-solid\" icon=\"refresh\"></cqa-button>\n <cqa-button *ngIf=\"showOtherButton\" [variant]=\"otherButtonVariant\" [text]=\"otherButtonLabel\"></cqa-button>\n </div>\n </div>\n\n <cqa-selected-filters \n *ngIf=\"showFilterPanel\"\n [filterApplied]=\"filterApplied\"\n [chips]=\"chips\"\n (removeChip)=\"onRemoveChip($event)\"\n (clearAll)=\"onClearAllChips()\"\n >\n </cqa-selected-filters>\n\n <cqa-dynamic-filter\n *ngIf=\"showFilterPanel\"\n [config]=\"filterConfig\"\n [showFilterPanel]=\"showFilterPanel\"\n (filtersChanged)=\"onFiltersChanged($event)\"\n (filtersApplied)=\"onFiltersApplied($event)\"\n >\n </cqa-dynamic-filter>\n\n <div class=\"cqa-rounded-[7px] cqa-overflow-hidden cqa-border-t cqa-border-l cqa-border-r cqa-border-grey-200 cqa-relative\">\n <ng-container *ngIf=\"!isEmptyState && pagedRows && pagedRows.length > 0; else storyEmptyTpl\">\n <app-dynamic-table\n [columns]=\"computedColumns\"\n [data]=\"pagedRows\"\n [isTableLoading]=\"isTableLoading\"\n [isTableDataLoading]=\"isTableDataLoading\">\n <ng-template dynamicCell=\"checkbox\" let-row=\"row\">\n <div class=\"custom-checkbox\">\n <input\n type=\"checkbox\"\n class=\"hidden-checkbox\"\n [attr.id]=\"'row-checkbox-' + row.id\"\n [checked]=\"row.isSelected\"\n aria-label=\"Select row\"\n (change)=\"onRowCheckboxChange($event, row)\" />\n <label\n class=\"custom-checkbox-label\"\n [attr.for]=\"'row-checkbox-' + row.id\">\n </label>\n </div>\n </ng-template>\n\n <ng-template dynamicCell=\"testCases\" let-row=\"row\" let-value=\"value\">\n <div class=\"cqa-flex cqa-flex-col cqa-gap-1 cqa-text-[12.3px] cqa-leading-[17.5px] cqa-font-medium\">\n <div class=\"cqa-text-[#3F43EE] cqa-truncate\">\n #{{ row.id }}\n </div>\n <div class=\"cqa-text-[#0A0A0A]\">{{ value }}</div>\n </div>\n </ng-template>\n\n <ng-template dynamicCell=\"type\" let-row=\"row\" let-value=\"value\">\n <div class=\"cqa-text-xs cqa-text-[#111827] cqa-truncate\">{{ value }}</div>\n </ng-template>\n\n <ng-template dynamicCell=\"priority\" let-row=\"row\" let-value=\"value\">\n <span\n class=\"cqa-inline-flex cqa-items-center cqa-px-2 cqa-py-0.5 cqa-rounded cqa-text-xs\"\n [ngClass]=\"{\n 'cqa-bg-red-100 cqa-text-red-700': value === 'Critical' || value === 'Major',\n 'cqa-bg-yellow-100 cqa-text-yellow-700': value === 'Medium',\n 'cqa-bg-blue-100 cqa-text-blue-700': value === 'Minor',\n 'cqa-bg-gray-100 cqa-text-gray-700': !value || value === 'Not Set'\n }\"\n >{{ value || 'Not Set' }}</span\n >\n </ng-template>\n\n <ng-template dynamicCell=\"result\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-inline-flex cqa-items-center cqa-px-2 cqa-py-0.5 cqa-rounded cqa-text-xs\" [ngClass]=\"resultBadgeClass(value)\">\n {{ value || 'NOT_EXECUTED' }}\n </span>\n </ng-template>\n\n <ng-template dynamicCell=\"status\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-text-xs\">{{ value }}</span>\n </ng-template>\n\n <ng-template dynamicCell=\"labels\" let-row=\"row\" let-value=\"value\">\n <div class=\"cqa-flex cqa-flex-wrap cqa-gap-1\">\n <span *ngFor=\"let tag of (row.tags || []) | slice: 0:3\" class=\"cqa-px-2 cqa-py-0.5 cqa-rounded cqa-bg-gray-100 cqa-text-gray-700 cqa-text-xs\">\n {{ tag }}\n </span>\n <span *ngIf=\"(row.tags?.length || 0) > 3\" class=\"cqa-px-2 cqa-py-0.5 cqa-rounded cqa-bg-gray-100 cqa-text-gray-700 cqa-text-xs\">\n +{{ (row.tags?.length || 0) - 3 }} more\n </span>\n </div>\n </ng-template>\n\n <ng-template dynamicCell=\"createdBy\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-text-xs\">{{ value }}</span>\n </ng-template>\n\n <ng-template dynamicCell=\"createdAt\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-text-xs cqa-text-gray-700\">{{ value | date: 'medium' }}</span>\n </ng-template>\n\n <ng-template dynamicCell=\"updatedAt\" let-row=\"row\" let-value=\"value\">\n <span class=\"cqa-text-xs cqa-text-gray-700\">{{ value | date: 'medium' }}</span>\n </ng-template>\n\n <ng-template dynamicCell=\"lastRun\" let-row=\"row\">\n <div class=\"cqa-flex cqa-flex-col\">\n <span class=\"cqa-text-xs cqa-text-[#111827]\">{{ row.lastRun?.startTime | date: 'medium' }}</span>\n </div>\n </ng-template>\n\n <ng-template dynamicCell=\"actions\" let-row=\"row\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\" *ngIf=\"showActionButton\">\n <cqa-action-menu-button\n [row]=\"row\"\n (view)=\"view($event)\"\n (edit)=\"edit($event)\"\n (delete)=\"delete($event)\"\n ></cqa-action-menu-button>\n </div>\n </ng-template>\n\n <ng-template #emptyTableTpl>\n <div class=\"cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-py-8\">\n <img src=\"/assets/illustrations/empty-state.svg\" alt=\"No data\" class=\"cqa-w-32 cqa-h-32 cqa-mb-4\" />\n <h3 class=\"cqa-text-lg cqa-font-semibold cqa-mb-2\">No test cases</h3>\n <p class=\"cqa-text-sm cqa-text-neutral-500 cqa-mb-4\">Try adjusting filters or create a new test case.</p>\n <cqa-button variant=\"filled\" (clicked)=\"toggleFilter()\">Show Filters</cqa-button>\n </div>\n </ng-template>\n </app-dynamic-table>\n </ng-container>\n\n <ng-template #storyEmptyTpl>\n <div class=\"cqa-p-6 cqa-flex cqa-flex-col cqa-items-center cqa-justify-center\">\n <cqa-empty-state\n *ngIf=\"isEmptyState\"\n [title]=\"emptyStateConfig.title\"\n [description]=\"emptyStateConfig.description\"\n [imageUrl]=\"emptyStateConfig.imageUrl\"\n [actions]=\"emptyStateConfig.actions\"\n (actionClick)=\"onEmptyAction($event)\"\n >\n </cqa-empty-state>\n </div>\n </ng-template>\n\n </div>\n\n <cqa-pagination\n [totalElements]=\"filteredRows.length\"\n [pageIndex]=\"pageIndex\"\n [pageSize]=\"pageSize\"\n [pageItemCount]=\"pagedRows.length\"\n (paginate)=\"onPaginate($event)\"\n (pageSizeChange)=\"onPageSizeChange($event)\"\n >\n </cqa-pagination>\n\n <div *ngIf=\"anyRowSelected\" class=\"cqa-absolute cqa-bottom-[18.75px] cqa-left-[50%] cqa-translate-x-[-50%] cqa-w-full lg:cqa-max-w-[68%] cqa-sm:max-w-[75%] cqa-max-w-[90%] cqa-z-[1]\" >\n <cqa-table-action-toolbar\n [selectedItems]=\"currentSelectedItems\"\n [actions]=\"actions\"\n (actionClick)=\"actionClick($event)\"\n ></cqa-table-action-toolbar>\n </div>\n \n </div>\n</div>\n\n", styles: [] }]
3212
+ }], propDecorators: { searchPlaceholder: [{
3213
+ type: Input
3214
+ }], searchValue: [{
3215
+ type: Input
3216
+ }], showClear: [{
3217
+ type: Input
3218
+ }], showSearchBar: [{
3219
+ type: Input
3220
+ }], filterConfig: [{
3221
+ type: Input
3222
+ }], showFilterPanel: [{
3223
+ type: Input
3224
+ }], showFilterButton: [{
3225
+ type: Input
3226
+ }], otherButtonLabel: [{
3227
+ type: Input
3228
+ }], otherButtonVariant: [{
3229
+ type: Input
3230
+ }], showOtherButton: [{
3231
+ type: Input
3232
+ }], showActionButton: [{
3233
+ type: Input
3234
+ }], showSettingsButton: [{
3235
+ type: Input
3236
+ }], showAutoRefreshButton: [{
3237
+ type: Input
3238
+ }], data: [{
3239
+ type: Input
3240
+ }], isEmptyState: [{
3241
+ type: Input
3242
+ }], emptyStateConfig: [{
3243
+ type: Input
3244
+ }], actions: [{
3245
+ type: Input
3246
+ }], chips: [{
3247
+ type: Input
3248
+ }], filterApplied: [{
3249
+ type: Input
3250
+ }], columns: [{
3251
+ type: Input
3252
+ }], selectedAutoRefreshInterval: [{
3253
+ type: Input
3254
+ }], pageIndex: [{
3255
+ type: Input
3256
+ }], pageSize: [{
3257
+ type: Input
3258
+ }], isTableLoading: [{
3259
+ type: Input
3260
+ }], isTableDataLoading: [{
3261
+ type: Input
3262
+ }] } });
3263
+
3264
+ /**
3265
+ * Ensures Angular CDK overlay content (e.g., MatSelect, Datepicker panels)
3266
+ * is nested under an element with id="cqa-ui-root" so Tailwind utilities
3267
+ * configured with important: '#cqa-ui-root' are applied inside overlays.
3268
+ */
3269
+ class TailwindOverlayContainer extends OverlayContainer {
3270
+ _createContainer() {
3271
+ super._createContainer();
3272
+ if (this._containerElement) {
3273
+ this._containerElement.id = 'cqa-ui-root';
3274
+ }
3275
+ }
3276
+ }
3277
+ TailwindOverlayContainer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TailwindOverlayContainer, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
3278
+ TailwindOverlayContainer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TailwindOverlayContainer });
3279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TailwindOverlayContainer, decorators: [{
3280
+ type: Injectable
3281
+ }] });
3282
+
3283
+ class UiKitModule {
3284
+ }
3285
+ UiKitModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: UiKitModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
3286
+ UiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: UiKitModule, declarations: [ButtonComponent,
3287
+ SearchBarComponent,
3288
+ SegmentControlComponent,
3289
+ DialogComponent,
3290
+ DynamicTableComponent,
3291
+ DynamicCellTemplateDirective,
3292
+ DynamicHeaderTemplateDirective,
3293
+ InlineSortComponent,
3294
+ PaginationComponent,
3295
+ ActionMenuButtonComponent,
3296
+ OtherButtonComponent,
3297
+ DynamicFilterComponent,
3298
+ ColumnVisibilityComponent,
3299
+ TableActionToolbarComponent,
3300
+ MetricsCardComponent,
3301
+ MetricsBlockComponent,
3302
+ ChartCardComponent,
3303
+ ProgressTextCardComponent,
3304
+ DashboardHeaderComponent,
3305
+ CoverageModuleCardComponent,
3306
+ TestDistributionCardComponent,
3307
+ FailedTestCasesCardComponent,
3308
+ DynamicSelectFieldComponent,
3309
+ SelectedFiltersComponent,
3310
+ InsightCardComponent,
3311
+ BadgeComponent,
3312
+ DropdownButtonComponent,
3313
+ HeatErrorMapCellComponent,
3314
+ EmptyStateComponent,
3315
+ TableTemplateComponent,
3316
+ FullTableLoaderComponent,
3317
+ TableDataLoaderComponent], imports: [CommonModule,
3318
+ FormsModule,
3319
+ ReactiveFormsModule,
3320
+ MatIconModule,
3321
+ MatTooltipModule,
3322
+ MatMenuModule,
3323
+ MatButtonModule,
3324
+ MatFormFieldModule,
3325
+ MatSelectModule,
3326
+ MatOptionModule,
3327
+ MatCheckboxModule,
3328
+ MatRadioModule,
3329
+ MatDatepickerModule,
3330
+ MatNativeDateModule,
3331
+ MatProgressSpinnerModule,
3332
+ OverlayModule,
3333
+ PortalModule], exports: [ButtonComponent,
3334
+ SearchBarComponent,
3335
+ SegmentControlComponent,
3336
+ DialogComponent,
3337
+ DynamicTableComponent,
3338
+ DynamicCellTemplateDirective,
3339
+ DynamicHeaderTemplateDirective,
3340
+ InlineSortComponent,
3341
+ PaginationComponent,
3342
+ ActionMenuButtonComponent,
3343
+ OtherButtonComponent,
3344
+ DynamicFilterComponent,
3345
+ ColumnVisibilityComponent,
3346
+ TableActionToolbarComponent,
3347
+ MetricsCardComponent,
3348
+ ChartCardComponent,
3349
+ ProgressTextCardComponent,
3350
+ DashboardHeaderComponent,
3351
+ CoverageModuleCardComponent,
3352
+ TestDistributionCardComponent,
3353
+ FailedTestCasesCardComponent,
3354
+ DynamicSelectFieldComponent,
3355
+ SelectedFiltersComponent,
3356
+ InsightCardComponent,
3357
+ BadgeComponent,
3358
+ DropdownButtonComponent,
3359
+ HeatErrorMapCellComponent,
3360
+ EmptyStateComponent,
3361
+ TableTemplateComponent,
3362
+ FullTableLoaderComponent,
3363
+ TableDataLoaderComponent] });
3364
+ UiKitModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: UiKitModule, providers: [
3365
+ { provide: OverlayContainer, useClass: TailwindOverlayContainer }
3366
+ ], imports: [[
3367
+ CommonModule,
3368
+ FormsModule,
3369
+ ReactiveFormsModule,
3370
+ MatIconModule,
3371
+ MatTooltipModule,
3372
+ MatMenuModule,
3373
+ MatButtonModule,
3374
+ MatFormFieldModule,
3375
+ MatSelectModule,
3376
+ MatOptionModule,
3377
+ MatCheckboxModule,
3378
+ MatRadioModule,
3379
+ MatDatepickerModule,
3380
+ MatNativeDateModule,
3381
+ MatProgressSpinnerModule,
3382
+ OverlayModule,
3383
+ PortalModule
3384
+ ]] });
3385
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: UiKitModule, decorators: [{
3386
+ type: NgModule,
3387
+ args: [{
3388
+ declarations: [
3389
+ ButtonComponent,
3390
+ SearchBarComponent,
3391
+ SegmentControlComponent,
3392
+ DialogComponent,
3393
+ DynamicTableComponent,
3394
+ DynamicCellTemplateDirective,
3395
+ DynamicHeaderTemplateDirective,
3396
+ InlineSortComponent,
3397
+ PaginationComponent,
3398
+ ActionMenuButtonComponent,
3399
+ OtherButtonComponent,
3400
+ DynamicFilterComponent,
3401
+ ColumnVisibilityComponent,
3402
+ TableActionToolbarComponent,
3403
+ MetricsCardComponent,
3404
+ MetricsBlockComponent,
3405
+ ChartCardComponent,
3406
+ ProgressTextCardComponent,
3407
+ DashboardHeaderComponent,
3408
+ CoverageModuleCardComponent,
3409
+ TestDistributionCardComponent,
3410
+ FailedTestCasesCardComponent,
3411
+ DynamicSelectFieldComponent,
3412
+ SelectedFiltersComponent,
3413
+ InsightCardComponent,
3414
+ BadgeComponent,
3415
+ DropdownButtonComponent,
3416
+ HeatErrorMapCellComponent,
3417
+ EmptyStateComponent,
3418
+ TableTemplateComponent,
3419
+ FullTableLoaderComponent,
3420
+ TableDataLoaderComponent
3421
+ ],
3422
+ imports: [
3423
+ CommonModule,
3424
+ FormsModule,
3425
+ ReactiveFormsModule,
3426
+ MatIconModule,
3427
+ MatTooltipModule,
3428
+ MatMenuModule,
3429
+ MatButtonModule,
3430
+ MatFormFieldModule,
3431
+ MatSelectModule,
3432
+ MatOptionModule,
3433
+ MatCheckboxModule,
3434
+ MatRadioModule,
3435
+ MatDatepickerModule,
3436
+ MatNativeDateModule,
3437
+ MatProgressSpinnerModule,
3438
+ OverlayModule,
3439
+ PortalModule
3440
+ ],
3441
+ exports: [
3442
+ ButtonComponent,
3443
+ SearchBarComponent,
3444
+ SegmentControlComponent,
3445
+ DialogComponent,
3446
+ DynamicTableComponent,
3447
+ DynamicCellTemplateDirective,
3448
+ DynamicHeaderTemplateDirective,
3449
+ InlineSortComponent,
3450
+ PaginationComponent,
3451
+ ActionMenuButtonComponent,
3452
+ OtherButtonComponent,
3453
+ DynamicFilterComponent,
3454
+ ColumnVisibilityComponent,
3455
+ TableActionToolbarComponent,
3456
+ MetricsCardComponent,
3457
+ ChartCardComponent,
3458
+ ProgressTextCardComponent,
3459
+ DashboardHeaderComponent,
3460
+ CoverageModuleCardComponent,
3461
+ TestDistributionCardComponent,
3462
+ FailedTestCasesCardComponent,
3463
+ DynamicSelectFieldComponent,
3464
+ SelectedFiltersComponent,
3465
+ InsightCardComponent,
3466
+ BadgeComponent,
3467
+ DropdownButtonComponent,
3468
+ HeatErrorMapCellComponent,
3469
+ EmptyStateComponent,
3470
+ TableTemplateComponent,
3471
+ FullTableLoaderComponent,
3472
+ TableDataLoaderComponent
3473
+ ],
3474
+ providers: [
3475
+ { provide: OverlayContainer, useClass: TailwindOverlayContainer }
3476
+ ]
3477
+ }]
3478
+ }] });
3479
+
3480
+ class DialogRef {
3481
+ constructor(overlayRef) {
3482
+ this.overlayRef = overlayRef;
3483
+ this.closed$ = new Subject();
3484
+ this.isClosed = false;
3485
+ this.overlayRef.detachments().subscribe(() => {
3486
+ this.finishClose(undefined);
3487
+ });
3488
+ }
3489
+ close(result) {
3490
+ if (this.isClosed) {
3491
+ return;
3492
+ }
3493
+ this.finishClose(result);
3494
+ this.overlayRef.dispose();
3495
+ }
3496
+ afterClosed() {
3497
+ return this.closed$.asObservable();
3498
+ }
3499
+ setComponentInstance(instance) {
3500
+ this.componentInstance = instance;
3501
+ }
3502
+ getComponentInstance() {
3503
+ return this.componentInstance;
3504
+ }
3505
+ finishClose(result) {
3506
+ if (this.isClosed) {
3507
+ return;
3508
+ }
3509
+ this.isClosed = true;
3510
+ this.closed$.next(result);
3511
+ this.closed$.complete();
3512
+ }
3513
+ }
3514
+
3515
+ const DIALOG_REF = new InjectionToken('CQA_DIALOG_REF');
3516
+ const DIALOG_DATA = new InjectionToken('CQA_DIALOG_DATA');
3517
+
3518
+ class DialogService {
3519
+ constructor(overlay, injector) {
3520
+ this.overlay = overlay;
3521
+ this.injector = injector;
3522
+ }
3523
+ open(config) {
3524
+ this.assertValidConfig(config);
3525
+ const overlayRef = this.overlay.create(this.buildOverlayConfig(config));
3526
+ const dialogRef = new DialogRef(overlayRef);
3527
+ const injector = Injector.create({
3528
+ parent: this.injector,
3529
+ providers: [
3530
+ { provide: DIALOG_REF, useValue: dialogRef },
3531
+ { provide: DIALOG_DATA, useValue: config.data },
3532
+ ],
3533
+ });
3534
+ const containerPortal = new ComponentPortal(DialogComponent, undefined, injector);
3535
+ const containerRef = overlayRef.attach(containerPortal);
3536
+ const containerInstance = containerRef.instance;
3537
+ containerInstance.config = config;
3538
+ containerInstance.dialogRef = dialogRef;
3539
+ if (config.content?.type === 'template') {
3540
+ containerInstance.attachTemplate(config.content.template, config.content.context ?? {
3541
+ $implicit: config.data,
3542
+ data: config.data,
3543
+ });
3544
+ }
3545
+ if (config.content?.type === 'component') {
3546
+ const componentPortal = new ComponentPortal(config.content.component, undefined, Injector.create({
3547
+ parent: injector,
3548
+ providers: [
3549
+ { provide: DIALOG_REF, useValue: dialogRef },
3550
+ { provide: DIALOG_DATA, useValue: config.data },
3551
+ ],
3552
+ }));
3553
+ const componentRef = containerInstance.attachComponent(componentPortal);
3554
+ if (componentRef && config.content.inputs) {
3555
+ Object.entries(config.content.inputs).forEach(([key, value]) => {
3556
+ componentRef.instance[key] = value;
3557
+ });
3558
+ componentRef.changeDetectorRef.markForCheck();
3559
+ }
3560
+ if (componentRef) {
3561
+ dialogRef.setComponentInstance(componentRef.instance);
3562
+ }
3563
+ }
3564
+ containerRef.changeDetectorRef.markForCheck();
3565
+ if (!config.disableClose) {
3566
+ overlayRef.backdropClick().subscribe(() => dialogRef.close());
3567
+ overlayRef
3568
+ .keydownEvents()
3569
+ .pipe(filter((event) => {
3570
+ return event.key === 'Escape' || event.key === 'Esc';
3571
+ }))
3572
+ .subscribe(() => dialogRef.close());
3573
+ }
3574
+ return dialogRef;
3575
+ }
3576
+ assertValidConfig(config) {
3577
+ if (!config.title) {
3578
+ throw new Error('Dialog title is required.');
3579
+ }
3580
+ if (!config.buttons || config.buttons.length < 2) {
3581
+ throw new Error('Dialog requires at least two buttons to be provided.');
3582
+ }
3583
+ }
3584
+ buildOverlayConfig(config) {
3585
+ const panelClass = Array.isArray(config.panelClass)
3586
+ ? config.panelClass
3587
+ : config.panelClass
3588
+ ? [config.panelClass]
3589
+ : [];
3590
+ return new OverlayConfig({
3591
+ hasBackdrop: true,
3592
+ backdropClass: ['cdk-overlay-dark-backdrop', 'cqa-dialog-backdrop'],
3593
+ scrollStrategy: this.overlay.scrollStrategies.block(),
3594
+ positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),
3595
+ width: config.width,
3596
+ maxWidth: config.maxWidth ?? '90vw',
3597
+ panelClass: ['cqa-dialog-panel', ...panelClass],
3598
+ });
3599
+ }
3600
+ }
3601
+ DialogService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DialogService, deps: [{ token: i1$4.Overlay }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
3602
+ DialogService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DialogService, providedIn: 'root' });
3603
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DialogService, decorators: [{
3604
+ type: Injectable,
3605
+ args: [{
3606
+ providedIn: 'root',
3607
+ }]
3608
+ }], ctorParameters: function () { return [{ type: i1$4.Overlay }, { type: i0.Injector }]; } });
3609
+
3610
+ /**
3611
+ * Generated bundle index. Do not edit.
3612
+ */
3613
+
3614
+ export { ActionMenuButtonComponent, BadgeComponent, ButtonComponent, ChartCardComponent, ColumnVisibilityComponent, CoverageModuleCardComponent, DEFAULT_METADATA_COLOR, DIALOG_DATA, DIALOG_REF, DashboardHeaderComponent, DialogComponent, DialogRef, DialogService, DropdownButtonComponent, DynamicCellTemplateDirective, DynamicFilterComponent, DynamicHeaderTemplateDirective, DynamicSelectFieldComponent, DynamicTableComponent, EMPTY_STATE_IMAGES, EmptyStateComponent, FailedTestCasesCardComponent, FullTableLoaderComponent, HeatErrorMapCellComponent, InlineSortComponent, InsightCardComponent, MetricsCardComponent, OtherButtonComponent, PRIORITY_COLORS, PaginationComponent, ProgressTextCardComponent, RESULT_COLORS, STATUS_COLORS, SearchBarComponent, SegmentControlComponent, SelectedFiltersComponent, TableActionToolbarComponent, TableDataLoaderComponent, TableTemplateComponent, TestDistributionCardComponent, UiKitModule, getMetadataColor, getMetadataValueStyle };
3615
+ //# sourceMappingURL=cqa-lib-cqa-ui.mjs.map