ngx-t-forms 2.0.30 → 2.0.32

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 (161) hide show
  1. package/fesm2022/ngx-t-forms-auto-complete-input-element.component-CaXs4561.mjs +104 -0
  2. package/fesm2022/ngx-t-forms-auto-complete-input-element.component-CaXs4561.mjs.map +1 -0
  3. package/fesm2022/ngx-t-forms-basic-input-input-element.component-Dotyd-Qs.mjs +85 -0
  4. package/fesm2022/ngx-t-forms-basic-input-input-element.component-Dotyd-Qs.mjs.map +1 -0
  5. package/fesm2022/ngx-t-forms-calculated-field-rules.component-BhxT6tRq.mjs +643 -0
  6. package/fesm2022/ngx-t-forms-calculated-field-rules.component-BhxT6tRq.mjs.map +1 -0
  7. package/fesm2022/ngx-t-forms-chip-options-creator-editor.component-d4QeVhsp.mjs +97 -0
  8. package/fesm2022/ngx-t-forms-chip-options-creator-editor.component-d4QeVhsp.mjs.map +1 -0
  9. package/fesm2022/ngx-t-forms-config-mscoa-additional-inputs.component-Gn8exJ9a.mjs +195 -0
  10. package/fesm2022/ngx-t-forms-config-mscoa-additional-inputs.component-Gn8exJ9a.mjs.map +1 -0
  11. package/fesm2022/ngx-t-forms-data-source-picker.component-Ebf_if9j.mjs +261 -0
  12. package/fesm2022/ngx-t-forms-data-source-picker.component-Ebf_if9j.mjs.map +1 -0
  13. package/fesm2022/ngx-t-forms-date-picker-input-element.component-kdinBGRA.mjs +85 -0
  14. package/fesm2022/ngx-t-forms-date-picker-input-element.component-kdinBGRA.mjs.map +1 -0
  15. package/fesm2022/ngx-t-forms-date-range-picker-input-element.component-4W6uvrDU.mjs +156 -0
  16. package/fesm2022/ngx-t-forms-date-range-picker-input-element.component-4W6uvrDU.mjs.map +1 -0
  17. package/fesm2022/ngx-t-forms-document-list-label-config-editor.component-CR6EvgJO.mjs +368 -0
  18. package/fesm2022/ngx-t-forms-document-list-label-config-editor.component-CR6EvgJO.mjs.map +1 -0
  19. package/fesm2022/ngx-t-forms-document-picker.component-BThdRFec.mjs +704 -0
  20. package/fesm2022/ngx-t-forms-document-picker.component-BThdRFec.mjs.map +1 -0
  21. package/fesm2022/ngx-t-forms-editor-input-element.component-1X6uAPeZ.mjs +294 -0
  22. package/fesm2022/ngx-t-forms-editor-input-element.component-1X6uAPeZ.mjs.map +1 -0
  23. package/fesm2022/ngx-t-forms-editor-js-input.component-5MD8wRj0.mjs +240 -0
  24. package/fesm2022/ngx-t-forms-editor-js-input.component-5MD8wRj0.mjs.map +1 -0
  25. package/fesm2022/ngx-t-forms-file-upload-input-element.component-BAtuymMY.mjs +205 -0
  26. package/fesm2022/ngx-t-forms-file-upload-input-element.component-BAtuymMY.mjs.map +1 -0
  27. package/fesm2022/ngx-t-forms-form-input-selector.component-B42xP3jh.mjs +86 -0
  28. package/fesm2022/ngx-t-forms-form-input-selector.component-B42xP3jh.mjs.map +1 -0
  29. package/fesm2022/ngx-t-forms-form-json-view.component-DnnLXqR0.mjs +22 -0
  30. package/fesm2022/ngx-t-forms-form-json-view.component-DnnLXqR0.mjs.map +1 -0
  31. package/fesm2022/ngx-t-forms-form-payload-projection.component-Ip9ewB18.mjs +179 -0
  32. package/fesm2022/ngx-t-forms-form-payload-projection.component-Ip9ewB18.mjs.map +1 -0
  33. package/fesm2022/ngx-t-forms-form-section-stepper.component-BPgPfZSy.mjs +319 -0
  34. package/fesm2022/ngx-t-forms-form-section-stepper.component-BPgPfZSy.mjs.map +1 -0
  35. package/fesm2022/ngx-t-forms-forms-builder-menu.component-Dv0Dfw79.mjs +379 -0
  36. package/fesm2022/ngx-t-forms-forms-builder-menu.component-Dv0Dfw79.mjs.map +1 -0
  37. package/fesm2022/ngx-t-forms-geo-location.component-Bmd84Gcb.mjs +124 -0
  38. package/fesm2022/ngx-t-forms-geo-location.component-Bmd84Gcb.mjs.map +1 -0
  39. package/fesm2022/ngx-t-forms-getInputIcon-B4ADgevZ.mjs +31 -0
  40. package/fesm2022/ngx-t-forms-getInputIcon-B4ADgevZ.mjs.map +1 -0
  41. package/fesm2022/ngx-t-forms-image-capture-input-element.component-CUd04Ghl.mjs +180 -0
  42. package/fesm2022/ngx-t-forms-image-capture-input-element.component-CUd04Ghl.mjs.map +1 -0
  43. package/fesm2022/ngx-t-forms-index-BcrQ01DQ.mjs +2 -0
  44. package/fesm2022/ngx-t-forms-index-BcrQ01DQ.mjs.map +1 -0
  45. package/fesm2022/ngx-t-forms-input-custom.component-Cn-KH0Lb.mjs +105 -0
  46. package/fesm2022/ngx-t-forms-input-custom.component-Cn-KH0Lb.mjs.map +1 -0
  47. package/fesm2022/ngx-t-forms-input-editor.component-DLru1Ezu.mjs +193 -0
  48. package/fesm2022/ngx-t-forms-input-editor.component-DLru1Ezu.mjs.map +1 -0
  49. package/fesm2022/{ngx-t-forms-map-mat-options-keys-SM5XM9uy.mjs → ngx-t-forms-map-mat-options-keys-CVlPdrCO.mjs} +12 -14
  50. package/fesm2022/ngx-t-forms-map-mat-options-keys-CVlPdrCO.mjs.map +1 -0
  51. package/fesm2022/ngx-t-forms-mat-chip-list-editor.component-BWisS3Em.mjs +66 -0
  52. package/fesm2022/ngx-t-forms-mat-chip-list-editor.component-BWisS3Em.mjs.map +1 -0
  53. package/fesm2022/ngx-t-forms-mat-slider-editor.component-CTSBrM-j.mjs +211 -0
  54. package/fesm2022/ngx-t-forms-mat-slider-editor.component-CTSBrM-j.mjs.map +1 -0
  55. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-CcYiwx-8.mjs +165 -0
  56. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-CcYiwx-8.mjs.map +1 -0
  57. package/fesm2022/ngx-t-forms-missing-form-configs.component-DxdynZY6.mjs +38 -0
  58. package/fesm2022/ngx-t-forms-missing-form-configs.component-DxdynZY6.mjs.map +1 -0
  59. package/fesm2022/ngx-t-forms-mscoa-chart-toolbar.component-D4Xa_Yi0.mjs +38 -0
  60. package/fesm2022/ngx-t-forms-mscoa-chart-toolbar.component-D4Xa_Yi0.mjs.map +1 -0
  61. package/fesm2022/ngx-t-forms-mscoa-error-display.component-99DpVSy7.mjs +126 -0
  62. package/fesm2022/ngx-t-forms-mscoa-error-display.component-99DpVSy7.mjs.map +1 -0
  63. package/fesm2022/ngx-t-forms-mscoa-segment-config.component-Bo0aDEMy.mjs +447 -0
  64. package/fesm2022/ngx-t-forms-mscoa-segment-config.component-Bo0aDEMy.mjs.map +1 -0
  65. package/fesm2022/ngx-t-forms-mscoa-temporary-hint.component-B1Z-IXSL.mjs +74 -0
  66. package/fesm2022/ngx-t-forms-mscoa-temporary-hint.component-B1Z-IXSL.mjs.map +1 -0
  67. package/fesm2022/ngx-t-forms-multiple-input-input-element.component-C8JP3D6r.mjs +905 -0
  68. package/fesm2022/ngx-t-forms-multiple-input-input-element.component-C8JP3D6r.mjs.map +1 -0
  69. package/fesm2022/ngx-t-forms-ngx-t-forms-C2G8_WQk.mjs +20310 -0
  70. package/fesm2022/ngx-t-forms-ngx-t-forms-C2G8_WQk.mjs.map +1 -0
  71. package/fesm2022/ngx-t-forms-paginated-selection-table-0OI1ikWW.mjs +555 -0
  72. package/fesm2022/ngx-t-forms-paginated-selection-table-0OI1ikWW.mjs.map +1 -0
  73. package/fesm2022/ngx-t-forms-pipeline-generator.component-CZ21sd77.mjs +748 -0
  74. package/fesm2022/ngx-t-forms-pipeline-generator.component-CZ21sd77.mjs.map +1 -0
  75. package/fesm2022/ngx-t-forms-record-list-manager.component-CykBq_nW.mjs +358 -0
  76. package/fesm2022/ngx-t-forms-record-list-manager.component-CykBq_nW.mjs.map +1 -0
  77. package/fesm2022/ngx-t-forms-required-inputs.component-ONbhxVSH.mjs +272 -0
  78. package/fesm2022/ngx-t-forms-required-inputs.component-ONbhxVSH.mjs.map +1 -0
  79. package/fesm2022/ngx-t-forms-rest-api-call-setup.component-WPUxtY7Q.mjs +398 -0
  80. package/fesm2022/ngx-t-forms-rest-api-call-setup.component-WPUxtY7Q.mjs.map +1 -0
  81. package/fesm2022/ngx-t-forms-search-field.component-B2ZO7lqO.mjs +38 -0
  82. package/fesm2022/ngx-t-forms-search-field.component-B2ZO7lqO.mjs.map +1 -0
  83. package/fesm2022/ngx-t-forms-section-report.component-C1w16LYm.mjs +98 -0
  84. package/fesm2022/ngx-t-forms-section-report.component-C1w16LYm.mjs.map +1 -0
  85. package/fesm2022/ngx-t-forms-select-input-element.component-CWcywuS6.mjs +150 -0
  86. package/fesm2022/ngx-t-forms-select-input-element.component-CWcywuS6.mjs.map +1 -0
  87. package/fesm2022/ngx-t-forms-selection-options-editor.component-KjbZhc2u.mjs +169 -0
  88. package/fesm2022/ngx-t-forms-selection-options-editor.component-KjbZhc2u.mjs.map +1 -0
  89. package/fesm2022/ngx-t-forms-t-workflow-picker.component-CtavFAUq.mjs +204 -0
  90. package/fesm2022/ngx-t-forms-t-workflow-picker.component-CtavFAUq.mjs.map +1 -0
  91. package/fesm2022/ngx-t-forms-textarea-input-element.component-DkJkBQif.mjs +95 -0
  92. package/fesm2022/ngx-t-forms-textarea-input-element.component-DkJkBQif.mjs.map +1 -0
  93. package/fesm2022/ngx-t-forms-toggle-input-element.component-Dr7MNli8.mjs +82 -0
  94. package/fesm2022/ngx-t-forms-toggle-input-element.component-Dr7MNli8.mjs.map +1 -0
  95. package/fesm2022/ngx-t-forms-validators-config.component-BknyAmV_.mjs +574 -0
  96. package/fesm2022/ngx-t-forms-validators-config.component-BknyAmV_.mjs.map +1 -0
  97. package/fesm2022/ngx-t-forms-workflow-adjudication.component-CPvwm7f4.mjs +1303 -0
  98. package/fesm2022/ngx-t-forms-workflow-adjudication.component-CPvwm7f4.mjs.map +1 -0
  99. package/fesm2022/ngx-t-forms.mjs +2 -1
  100. package/fesm2022/ngx-t-forms.mjs.map +1 -1
  101. package/package.json +20 -18
  102. package/styles/_editor-mixins.scss +62 -0
  103. package/styles/_json-editor-syntax.scss +26 -0
  104. package/styles/_signature-pad.scss +26 -0
  105. package/styles/_tokens.scss +148 -0
  106. package/types/ngx-t-forms.d.ts +1921 -733
  107. package/fesm2022/ngx-t-forms-calculated-field-rules.component-Ct6_c_Lj.mjs +0 -313
  108. package/fesm2022/ngx-t-forms-calculated-field-rules.component-Ct6_c_Lj.mjs.map +0 -1
  109. package/fesm2022/ngx-t-forms-chip-options-creator-editor.component-yuM1KHho.mjs +0 -191
  110. package/fesm2022/ngx-t-forms-chip-options-creator-editor.component-yuM1KHho.mjs.map +0 -1
  111. package/fesm2022/ngx-t-forms-config-mscoa-additional-inputs.component-BptpYSe-.mjs +0 -207
  112. package/fesm2022/ngx-t-forms-config-mscoa-additional-inputs.component-BptpYSe-.mjs.map +0 -1
  113. package/fesm2022/ngx-t-forms-data-source-picker.component-Badna1Rl.mjs +0 -204
  114. package/fesm2022/ngx-t-forms-data-source-picker.component-Badna1Rl.mjs.map +0 -1
  115. package/fesm2022/ngx-t-forms-document-list-label-config-editor.component-2_8XzUgD.mjs +0 -289
  116. package/fesm2022/ngx-t-forms-document-list-label-config-editor.component-2_8XzUgD.mjs.map +0 -1
  117. package/fesm2022/ngx-t-forms-form-input-selector.component-DV4Sts9F.mjs +0 -134
  118. package/fesm2022/ngx-t-forms-form-input-selector.component-DV4Sts9F.mjs.map +0 -1
  119. package/fesm2022/ngx-t-forms-form-json-view.component-B8seYzMQ.mjs +0 -22
  120. package/fesm2022/ngx-t-forms-form-json-view.component-B8seYzMQ.mjs.map +0 -1
  121. package/fesm2022/ngx-t-forms-form-section-stepper.component-x_83iAWA.mjs +0 -281
  122. package/fesm2022/ngx-t-forms-form-section-stepper.component-x_83iAWA.mjs.map +0 -1
  123. package/fesm2022/ngx-t-forms-forms-builder-menu.component-UWo_dyVt.mjs +0 -345
  124. package/fesm2022/ngx-t-forms-forms-builder-menu.component-UWo_dyVt.mjs.map +0 -1
  125. package/fesm2022/ngx-t-forms-input-editor.component-B_kkOoEO.mjs +0 -147
  126. package/fesm2022/ngx-t-forms-input-editor.component-B_kkOoEO.mjs.map +0 -1
  127. package/fesm2022/ngx-t-forms-map-mat-options-keys-SM5XM9uy.mjs.map +0 -1
  128. package/fesm2022/ngx-t-forms-mat-chip-list-editor.component-C41AL9Et.mjs +0 -105
  129. package/fesm2022/ngx-t-forms-mat-chip-list-editor.component-C41AL9Et.mjs.map +0 -1
  130. package/fesm2022/ngx-t-forms-mat-slider-editor.component-BWe8U-sI.mjs +0 -109
  131. package/fesm2022/ngx-t-forms-mat-slider-editor.component-BWe8U-sI.mjs.map +0 -1
  132. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-B_XlkHuK.mjs +0 -155
  133. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-B_XlkHuK.mjs.map +0 -1
  134. package/fesm2022/ngx-t-forms-missing-form-configs.component-DPNNyKkt.mjs +0 -28
  135. package/fesm2022/ngx-t-forms-missing-form-configs.component-DPNNyKkt.mjs.map +0 -1
  136. package/fesm2022/ngx-t-forms-mscoa-chart-toolbar.component-DY1QnG08.mjs +0 -43
  137. package/fesm2022/ngx-t-forms-mscoa-chart-toolbar.component-DY1QnG08.mjs.map +0 -1
  138. package/fesm2022/ngx-t-forms-mscoa-error-display.component-CRc_4l3l.mjs +0 -116
  139. package/fesm2022/ngx-t-forms-mscoa-error-display.component-CRc_4l3l.mjs.map +0 -1
  140. package/fesm2022/ngx-t-forms-mscoa-segment-config.component-Ckr_nuZF.mjs +0 -296
  141. package/fesm2022/ngx-t-forms-mscoa-segment-config.component-Ckr_nuZF.mjs.map +0 -1
  142. package/fesm2022/ngx-t-forms-mscoa-temporary-hint.component-GYxT-56Y.mjs +0 -83
  143. package/fesm2022/ngx-t-forms-mscoa-temporary-hint.component-GYxT-56Y.mjs.map +0 -1
  144. package/fesm2022/ngx-t-forms-ngx-t-forms-DP2koSL5.mjs +0 -17401
  145. package/fesm2022/ngx-t-forms-ngx-t-forms-DP2koSL5.mjs.map +0 -1
  146. package/fesm2022/ngx-t-forms-pipeline-generator.component-BxHetD_Q.mjs +0 -613
  147. package/fesm2022/ngx-t-forms-pipeline-generator.component-BxHetD_Q.mjs.map +0 -1
  148. package/fesm2022/ngx-t-forms-record-list-manager.component-BQuMkoXo.mjs +0 -269
  149. package/fesm2022/ngx-t-forms-record-list-manager.component-BQuMkoXo.mjs.map +0 -1
  150. package/fesm2022/ngx-t-forms-required-inputs.component-CLyq9dIR.mjs +0 -190
  151. package/fesm2022/ngx-t-forms-required-inputs.component-CLyq9dIR.mjs.map +0 -1
  152. package/fesm2022/ngx-t-forms-rest-api-call-setup.component-CWeIUKLz.mjs +0 -291
  153. package/fesm2022/ngx-t-forms-rest-api-call-setup.component-CWeIUKLz.mjs.map +0 -1
  154. package/fesm2022/ngx-t-forms-section-report.component-BtaF39WD.mjs +0 -156
  155. package/fesm2022/ngx-t-forms-section-report.component-BtaF39WD.mjs.map +0 -1
  156. package/fesm2022/ngx-t-forms-selection-options-editor.component-B4cEGWrK.mjs +0 -186
  157. package/fesm2022/ngx-t-forms-selection-options-editor.component-B4cEGWrK.mjs.map +0 -1
  158. package/fesm2022/ngx-t-forms-t-workflow-picker.component-BkVN4Wdk.mjs +0 -187
  159. package/fesm2022/ngx-t-forms-t-workflow-picker.component-BkVN4Wdk.mjs.map +0 -1
  160. package/fesm2022/ngx-t-forms-validators-config.component-Cq07Er-G.mjs +0 -215
  161. package/fesm2022/ngx-t-forms-validators-config.component-Cq07Er-G.mjs.map +0 -1
@@ -0,0 +1,1303 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, DestroyRef, input, output, computed, effect, ViewEncapsulation, ChangeDetectionStrategy, Component, signal, ElementRef } from '@angular/core';
3
+ import * as i1 from '@angular/forms';
4
+ import { ReactiveFormsModule, FormsModule, FormBuilder, FormControl, Validators, NgControl } from '@angular/forms';
5
+ import * as i2$1 from '@angular/material/form-field';
6
+ import { MatFormFieldModule, MatFormFieldControl } from '@angular/material/form-field';
7
+ import * as i3 from '@angular/material/icon';
8
+ import { MatIconModule } from '@angular/material/icon';
9
+ import * as i1$1 from '@angular/material/button';
10
+ import { MatButtonModule } from '@angular/material/button';
11
+ import * as i5$2 from '@angular/material/tooltip';
12
+ import { MatTooltipModule } from '@angular/material/tooltip';
13
+ import { N as NGX_T_FORMS_CONFIG_TOKEN, _ as _isEqual, D as DaysAgoPipe, B as BaseCustomInput, q as getInputErrorMessage, u as TFormInputStatusComponent } from './ngx-t-forms-ngx-t-forms-C2G8_WQk.mjs';
14
+ import { AsyncPipe, DecimalPipe, TitleCasePipe } from '@angular/common';
15
+ import { InputDataTypes, AdjudicationSteps } from 'ngx-t-forms-types';
16
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
17
+ import * as i2 from '@angular/cdk/text-field';
18
+ import { TextFieldModule } from '@angular/cdk/text-field';
19
+ import * as i5 from '@angular/material/autocomplete';
20
+ import { MatAutocompleteModule } from '@angular/material/autocomplete';
21
+ import * as i2$2 from '@angular/material/input';
22
+ import { MatInputModule } from '@angular/material/input';
23
+ import * as i8 from '@angular/material/select';
24
+ import { MatSelectModule } from '@angular/material/select';
25
+ import * as i10 from '@angular/material/sort';
26
+ import { MatSortModule } from '@angular/material/sort';
27
+ import * as i5$1 from '@angular/material/table';
28
+ import { MatTableModule } from '@angular/material/table';
29
+ import { tap, BehaviorSubject, switchMap, take, shareReplay, combineLatest, map, startWith, catchError, of } from 'rxjs';
30
+ import { v4 } from 'uuid';
31
+ import { g as getInputIcon } from './ngx-t-forms-getInputIcon-B4ADgevZ.mjs';
32
+ import * as i2$3 from '@angular/cdk/overlay';
33
+ import { OverlayModule } from '@angular/cdk/overlay';
34
+ import * as i1$2 from '@angular/material/dialog';
35
+ import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
36
+ import * as i3$1 from '@angular/material/progress-spinner';
37
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
38
+ import * as i7 from '@angular/material/core';
39
+ import { MatRippleModule } from '@angular/material/core';
40
+ import { EditorJsInputComponent } from './ngx-t-forms-editor-js-input.component-5MD8wRj0.mjs';
41
+ import * as i1$3 from '@angular/material/progress-bar';
42
+ import { MatProgressBarModule } from '@angular/material/progress-bar';
43
+ import * as i5$3 from '@angular/material/expansion';
44
+ import { MatExpansionModule } from '@angular/material/expansion';
45
+ import { MatDividerModule } from '@angular/material/divider';
46
+ import * as i4 from '@angular/material/list';
47
+
48
+ class PointsCreationComponent {
49
+ #config;
50
+ #destroyRef;
51
+ #points;
52
+ constructor() {
53
+ this.#config = inject(NGX_T_FORMS_CONFIG_TOKEN);
54
+ this.#destroyRef = inject(DestroyRef);
55
+ /** Tower step column config that drives column layout, types, and disabled/readonly state. */
56
+ this.inputConfig = input(undefined, ...(ngDevMode ? [{ debugName: "inputConfig" }] : /* istanbul ignore next */ []));
57
+ /** Existing points list pushed in from the parent (workflow-adjudication-reactive-input). */
58
+ // NOTE(phase-4): Trap 6 — parent (`workflow-adjudication-reactive-input`) binds a
59
+ // `WorkflowAdjudicationValue | null` union of three incompatible shapes. Until the parent
60
+ // template is split per adjudication step type, this input must remain `any`.
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO(phase-4): split parent template by adjudication step, then narrow this input.
62
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
63
+ /** Emits the updated points list whenever the user saves, edits, or removes a row. */
64
+ this.pointsChange = output();
65
+ this.pointRow = this.#createDefaultPointRow();
66
+ this.selectAllWorkflowInputs = [];
67
+ this.#points = [];
68
+ /** Material table columns derived from the input config. */
69
+ this.cols = computed(() => {
70
+ const cfg = this.inputConfig();
71
+ let cols = (cfg?.adjudicationPointTypes?.length || 0) > 0
72
+ ? ['description', 'totalPoints', 'pointType', 'actions']
73
+ : ['description', 'totalPoints', 'actions'];
74
+ if (cfg?.disabled || !!cfg?.readonly) {
75
+ cols = cols.filter(col => col !== 'actions');
76
+ }
77
+ return cols;
78
+ }, ...(ngDevMode ? [{ debugName: "cols" }] : /* istanbul ignore next */ []));
79
+ /** Mirrors the legacy `set value` setter — pushes incoming `value` into the internal `#points` buffer. */
80
+ this.valueBridge = effect(() => {
81
+ const next = this.value();
82
+ this.#points = (next || []);
83
+ }, ...(ngDevMode ? [{ debugName: "valueBridge" }] : /* istanbul ignore next */ []));
84
+ this.#config.formBuilder.getCurrentFormAvailableInputOptions().pipe(tap((inputs) => {
85
+ this.selectAllWorkflowInputs = inputs;
86
+ }), takeUntilDestroyed(this.#destroyRef)).subscribe();
87
+ }
88
+ get points() {
89
+ const cfg = this.inputConfig();
90
+ if (cfg?.disabled || !!cfg?.readonly) {
91
+ return this.#points;
92
+ }
93
+ return [...this.#points, this.pointRow];
94
+ }
95
+ get filteredInputs() {
96
+ const description = this.pointRow.description?.toLowerCase() || '';
97
+ if (!description) {
98
+ return this.selectAllWorkflowInputs;
99
+ }
100
+ const regex = new RegExp(description.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'i');
101
+ return this.selectAllWorkflowInputs.filter((input) => (typeof input.label === 'string' && regex.test(input.label)) ||
102
+ (typeof input.hintLabel === 'string' && regex.test(input.hintLabel)));
103
+ }
104
+ get pointInEditIsValid() {
105
+ const cfg = this.inputConfig();
106
+ const typeRequired = (cfg?.adjudicationPointTypes?.length || 0) > 0;
107
+ return !!this.pointRow.description && this.pointRow.totalPoints > 0 && (!typeRequired || !!this.pointRow.pointType);
108
+ }
109
+ #createDefaultPointRow() {
110
+ return {
111
+ id: '',
112
+ formControlName: '',
113
+ totalPoints: 0,
114
+ description: '',
115
+ pointType: '',
116
+ inputId: undefined,
117
+ isEditRow: true,
118
+ };
119
+ }
120
+ #resetPointRow() {
121
+ this.pointRow = this.#createDefaultPointRow();
122
+ }
123
+ selectActiveInput(input) {
124
+ const cfg = this.inputConfig();
125
+ this.pointRow = {
126
+ ...this.pointRow,
127
+ totalPoints: 0,
128
+ description: input.label || '',
129
+ id: input.id,
130
+ formControlName: input.formControlName,
131
+ pointType: this.pointRow.pointType || cfg?.adjudicationPointTypes[0] || '',
132
+ inputId: input.id,
133
+ };
134
+ }
135
+ selectPointType(pointType) {
136
+ this.pointRow = {
137
+ ...this.pointRow,
138
+ pointType: pointType.value,
139
+ };
140
+ }
141
+ savePoints() {
142
+ const pointToSave = {
143
+ ...this.pointRow,
144
+ id: this.pointRow.id || v4(),
145
+ formControlName: this.pointRow.formControlName || this.pointRow.description,
146
+ isEditRow: undefined,
147
+ };
148
+ this.#points = [...this.#points, pointToSave];
149
+ this.pointsChange.emit(this.#points);
150
+ this.#resetPointRow();
151
+ }
152
+ edit(element) {
153
+ this.pointRow = {
154
+ ...element,
155
+ isEditRow: true,
156
+ };
157
+ this.#points = this.#points.filter(p => p.id !== element.id);
158
+ this.pointsChange.emit(this.#points);
159
+ }
160
+ remove(element) {
161
+ this.#points = this.#points.filter(p => p.id !== element.id);
162
+ this.pointsChange.emit(this.#points);
163
+ }
164
+ getInputIcon(input) {
165
+ return getInputIcon(input.element);
166
+ }
167
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PointsCreationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
168
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: PointsCreationComponent, isStandalone: true, selector: "lib-points-creation", inputs: { inputConfig: { classPropertyName: "inputConfig", publicName: "inputConfig", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointsChange: "pointsChange" }, ngImport: i0, template: "<div style=\"resize: both; overflow: auto;\">\r\n @if (!points || points.length === 1) {\r\n <h5 style=\"margin-bottom: 5px; margin-top: 0px;\">\r\n &#8505;&#65039; To add a scoring item:\r\n </h5>\r\n <ol>\r\n <li>\r\n <strong>Enter Description:</strong>\r\n <ul style=\"margin-top: 5px; margin-bottom: 5px; padding-left: 20px; list-style-type: disc;\">\r\n <li>\r\n To <strong>link</strong> a field: You <strong>must select it</strong> from the list first (you can\r\n edit it after).\r\n </li>\r\n <li>\r\n For a <strong>custom</strong> point: <strong>Ignore the list</strong> and just type your own\r\n description.\r\n </li>\r\n </ul>\r\n </li>\r\n <li>\r\n <strong>Set Details:</strong>\r\n Assign the <strong>Points</strong> and select the <strong>Point Type</strong>.\r\n </li>\r\n <li>\r\n <strong>Save:</strong>\r\n Click the <strong>\"Save\"</strong> button to add the item.\r\n </li>\r\n </ol>\r\n }\r\n <table mat-table [dataSource]=\"points\" matSort>\r\n <ng-container matColumnDef=\"description\">\r\n <th mat-header-cell *matHeaderCellDef>Description</th>\r\n <td mat-cell *matCellDef=\"let element\">\r\n @if (element.isEditRow) {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"input-add\" appearance=\"outline\">\r\n <textarea [matAutocomplete]=\"auto\" [(ngModel)]=\"pointRow.description\" matInput cdkTextareaAutosize\r\n cdkAutosizeMinRows=\"1\" placeholder=\"Enter point description\"></textarea>\r\n <mat-autocomplete autoActiveFirstOption #auto=\"matAutocomplete\">\r\n @for (input of filteredInputs; track input.id) {\r\n <mat-option [value]=\"input.label\" (click)=\"selectActiveInput(input)\">\r\n <div style=\"display: flex;align-items: center;gap:8px\">\r\n <mat-icon style=\"font-size:24px; width:24px; height:24px;\"\r\n [attr.aria-label]=\"input.label\">\r\n {{ getInputIcon(input) }}\r\n </mat-icon>\r\n <span>\r\n <div style=\"font-size: 0.75em;\">\r\n <strong>\r\n {{ input.label }}\r\n </strong>\r\n </div>\r\n <div\r\n style=\"font-size: 0.75em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ input.hintLabel || input.formControlName }}\r\n </div>\r\n </span>\r\n </div>\r\n </mat-option>\r\n }\r\n </mat-autocomplete>\r\n </mat-form-field>\r\n } @else {\r\n {{ element.description }}\r\n }\r\n </td>\r\n </ng-container>\r\n <ng-container matColumnDef=\"totalPoints\">\r\n <th mat-header-cell *matHeaderCellDef>Points</th>\r\n <td mat-cell *matCellDef=\"let element\">\r\n @if (element.isEditRow) {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"input-add\" appearance=\"outline\">\r\n <input matInput [(ngModel)]=\"pointRow.totalPoints\"\r\n placeholder=\"Enter points , eg 50\" type=\"number\">\r\n </mat-form-field>\r\n } @else {\r\n {{ element.totalPoints }}\r\n }\r\n </td>\r\n </ng-container>\r\n <ng-container matColumnDef=\"pointType\">\r\n <th mat-header-cell *matHeaderCellDef>Point Type</th>\r\n <td mat-cell *matCellDef=\"let element\">\r\n @if (element.isEditRow) {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"input-add\" appearance=\"outline\">\r\n <mat-select (selectionChange)=\"selectPointType($event)\" ngDefaultControl\r\n [value]=\"pointRow.pointType\"\r\n placeholder=\"Select\">\r\n @for (type of inputConfig()?.adjudicationPointTypes || []; track $index) {\r\n <mat-option [value]=\"type\">{{ type }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n } @else {\r\n {{ element.pointType }}\r\n }\r\n </td>\r\n </ng-container>\r\n <ng-container matColumnDef=\"actions\" [stickyEnd]=\"true\">\r\n <th mat-header-cell *matHeaderCellDef></th>\r\n <td mat-cell *matCellDef=\"let element\">\r\n <span style=\"display: flex; justify-content: center;\">\r\n @if (element.isEditRow) {\r\n <button matTooltip=\"Save Point in Edit\" color=\"primary\" style=\"white-space: nowrap;\r\n height: 26px;\r\n margin-bottom: 0px;\" mat-flat-button (click)=\"savePoints()\" [disabled]=\"!pointInEditIsValid || inputConfig()?.disabled || inputConfig()?.readonly\">\r\n <span style=\"font-size: 0.875em;\">\r\n save\r\n </span>\r\n </button>\r\n } @else {\r\n <button mat-icon-button (click)=\"edit(element)\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button mat-icon-button (click)=\"remove(element)\">\r\n <mat-icon>delete_forever</mat-icon>\r\n </button>\r\n }\r\n </span>\r\n </td>\r\n </ng-container>\r\n <tr mat-header-row *matHeaderRowDef=\"cols()\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: cols()\"></tr>\r\n </table>\r\n</div>\r\n", styles: [".input-add{width:100%}th,td{padding:4px}mat-hint{text-align:start}:host .glass{background:#ffffff40;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.mat-column-totalPoints{width:100px!important;max-width:100px!important}.mat-column-pointType{width:180px!important;max-width:180px!important}.mat-column-actions{width:78px!important;max-width:78px!important}li{font-size:.75em}ol{margin-top:8px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "directive", type: i2.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i5.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i8.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i5.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i10.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
169
+ }
170
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PointsCreationComponent, decorators: [{
171
+ type: Component,
172
+ args: [{ selector: 'lib-points-creation', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [
173
+ ReactiveFormsModule,
174
+ FormsModule,
175
+ TextFieldModule,
176
+ MatAutocompleteModule,
177
+ MatButtonModule,
178
+ MatFormFieldModule,
179
+ MatIconModule,
180
+ MatInputModule,
181
+ MatSelectModule,
182
+ MatSortModule,
183
+ MatTableModule,
184
+ MatTooltipModule,
185
+ ], template: "<div style=\"resize: both; overflow: auto;\">\r\n @if (!points || points.length === 1) {\r\n <h5 style=\"margin-bottom: 5px; margin-top: 0px;\">\r\n &#8505;&#65039; To add a scoring item:\r\n </h5>\r\n <ol>\r\n <li>\r\n <strong>Enter Description:</strong>\r\n <ul style=\"margin-top: 5px; margin-bottom: 5px; padding-left: 20px; list-style-type: disc;\">\r\n <li>\r\n To <strong>link</strong> a field: You <strong>must select it</strong> from the list first (you can\r\n edit it after).\r\n </li>\r\n <li>\r\n For a <strong>custom</strong> point: <strong>Ignore the list</strong> and just type your own\r\n description.\r\n </li>\r\n </ul>\r\n </li>\r\n <li>\r\n <strong>Set Details:</strong>\r\n Assign the <strong>Points</strong> and select the <strong>Point Type</strong>.\r\n </li>\r\n <li>\r\n <strong>Save:</strong>\r\n Click the <strong>\"Save\"</strong> button to add the item.\r\n </li>\r\n </ol>\r\n }\r\n <table mat-table [dataSource]=\"points\" matSort>\r\n <ng-container matColumnDef=\"description\">\r\n <th mat-header-cell *matHeaderCellDef>Description</th>\r\n <td mat-cell *matCellDef=\"let element\">\r\n @if (element.isEditRow) {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"input-add\" appearance=\"outline\">\r\n <textarea [matAutocomplete]=\"auto\" [(ngModel)]=\"pointRow.description\" matInput cdkTextareaAutosize\r\n cdkAutosizeMinRows=\"1\" placeholder=\"Enter point description\"></textarea>\r\n <mat-autocomplete autoActiveFirstOption #auto=\"matAutocomplete\">\r\n @for (input of filteredInputs; track input.id) {\r\n <mat-option [value]=\"input.label\" (click)=\"selectActiveInput(input)\">\r\n <div style=\"display: flex;align-items: center;gap:8px\">\r\n <mat-icon style=\"font-size:24px; width:24px; height:24px;\"\r\n [attr.aria-label]=\"input.label\">\r\n {{ getInputIcon(input) }}\r\n </mat-icon>\r\n <span>\r\n <div style=\"font-size: 0.75em;\">\r\n <strong>\r\n {{ input.label }}\r\n </strong>\r\n </div>\r\n <div\r\n style=\"font-size: 0.75em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ input.hintLabel || input.formControlName }}\r\n </div>\r\n </span>\r\n </div>\r\n </mat-option>\r\n }\r\n </mat-autocomplete>\r\n </mat-form-field>\r\n } @else {\r\n {{ element.description }}\r\n }\r\n </td>\r\n </ng-container>\r\n <ng-container matColumnDef=\"totalPoints\">\r\n <th mat-header-cell *matHeaderCellDef>Points</th>\r\n <td mat-cell *matCellDef=\"let element\">\r\n @if (element.isEditRow) {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"input-add\" appearance=\"outline\">\r\n <input matInput [(ngModel)]=\"pointRow.totalPoints\"\r\n placeholder=\"Enter points , eg 50\" type=\"number\">\r\n </mat-form-field>\r\n } @else {\r\n {{ element.totalPoints }}\r\n }\r\n </td>\r\n </ng-container>\r\n <ng-container matColumnDef=\"pointType\">\r\n <th mat-header-cell *matHeaderCellDef>Point Type</th>\r\n <td mat-cell *matCellDef=\"let element\">\r\n @if (element.isEditRow) {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"input-add\" appearance=\"outline\">\r\n <mat-select (selectionChange)=\"selectPointType($event)\" ngDefaultControl\r\n [value]=\"pointRow.pointType\"\r\n placeholder=\"Select\">\r\n @for (type of inputConfig()?.adjudicationPointTypes || []; track $index) {\r\n <mat-option [value]=\"type\">{{ type }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n } @else {\r\n {{ element.pointType }}\r\n }\r\n </td>\r\n </ng-container>\r\n <ng-container matColumnDef=\"actions\" [stickyEnd]=\"true\">\r\n <th mat-header-cell *matHeaderCellDef></th>\r\n <td mat-cell *matCellDef=\"let element\">\r\n <span style=\"display: flex; justify-content: center;\">\r\n @if (element.isEditRow) {\r\n <button matTooltip=\"Save Point in Edit\" color=\"primary\" style=\"white-space: nowrap;\r\n height: 26px;\r\n margin-bottom: 0px;\" mat-flat-button (click)=\"savePoints()\" [disabled]=\"!pointInEditIsValid || inputConfig()?.disabled || inputConfig()?.readonly\">\r\n <span style=\"font-size: 0.875em;\">\r\n save\r\n </span>\r\n </button>\r\n } @else {\r\n <button mat-icon-button (click)=\"edit(element)\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button mat-icon-button (click)=\"remove(element)\">\r\n <mat-icon>delete_forever</mat-icon>\r\n </button>\r\n }\r\n </span>\r\n </td>\r\n </ng-container>\r\n <tr mat-header-row *matHeaderRowDef=\"cols()\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: cols()\"></tr>\r\n </table>\r\n</div>\r\n", styles: [".input-add{width:100%}th,td{padding:4px}mat-hint{text-align:start}:host .glass{background:#ffffff40;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.mat-column-totalPoints{width:100px!important;max-width:100px!important}.mat-column-pointType{width:180px!important;max-width:180px!important}.mat-column-actions{width:78px!important;max-width:78px!important}li{font-size:.75em}ol{margin-top:8px}\n"] }]
186
+ }], ctorParameters: () => [], propDecorators: { inputConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputConfig", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], pointsChange: [{ type: i0.Output, args: ["pointsChange"] }] } });
187
+
188
+ function getColorForNumber(targetNumber, numbers, asc = true) {
189
+ if (numbers.length === 0) {
190
+ throw new Error("The numbers array must not be empty.");
191
+ }
192
+ // Avoid sorting the original array
193
+ const sortedNumbers = [...numbers].sort((a, b) => asc ? a - b : b - a);
194
+ const min = sortedNumbers[0];
195
+ const max = sortedNumbers[sortedNumbers.length - 1];
196
+ if (!min || !max) {
197
+ return "rgb(173, 173, 173)";
198
+ }
199
+ if (min === max) {
200
+ return "rgb(173, 173, 173)"; // Return gray if there is no range
201
+ }
202
+ const interpolateColor = (value) => {
203
+ const fraction = (value - min) / (max - min);
204
+ const colors = [
205
+ { color: [247, 0, 0], pos: 0.0 }, // Red
206
+ { color: [247, 187, 0], pos: 0.25 }, // Orange
207
+ { color: [247, 247, 0], pos: 0.5 }, // Yellow
208
+ { color: [207, 247, 0], pos: 0.75 }, // Light green
209
+ { color: [96, 247, 0], pos: 1.0 } // Green
210
+ ];
211
+ for (let i = 0; i < colors.length - 1; i++) {
212
+ const nextColor = colors[i + 1];
213
+ const currentColor = colors[i];
214
+ if (currentColor && nextColor && fraction <= nextColor.pos) {
215
+ const range = nextColor.pos - currentColor.pos;
216
+ const localFraction = (fraction - currentColor.pos) / range;
217
+ const r = Math.round(currentColor.color[0] + localFraction * (nextColor.color[0] - currentColor.color[0]));
218
+ const g = Math.round(currentColor.color[1] + localFraction * (nextColor.color[1] - currentColor.color[1]));
219
+ const b = Math.round(currentColor.color[2] + localFraction * (nextColor.color[2] - currentColor.color[2]));
220
+ return `rgb(${r}, ${g}, ${b})`;
221
+ }
222
+ }
223
+ return `rgb(${colors[colors.length - 1]?.color[0]}, ${colors[colors.length - 1]?.color[1]}, ${colors[colors.length - 1]?.color[2]},0.6)`;
224
+ };
225
+ return interpolateColor(targetNumber);
226
+ }
227
+
228
+ class SubmissionReviewDialogComponent {
229
+ constructor() {
230
+ this.dialogRef = inject(MatDialogRef);
231
+ this.data = inject(MAT_DIALOG_DATA);
232
+ }
233
+ get notesInputConfig() {
234
+ return {
235
+ id: 'adj_microFlow_review_notes',
236
+ label: 'Notes',
237
+ readonly: this.data.readonly,
238
+ required: false,
239
+ formControlName: 'adj_microFlow_review_notes',
240
+ };
241
+ }
242
+ get respondentName() {
243
+ const raw = this.data.row[this.data.respondentNameKey];
244
+ return typeof raw === 'string' ? raw : '';
245
+ }
246
+ notesChanged(notes) {
247
+ this.changedNotes = notes;
248
+ }
249
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SubmissionReviewDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
250
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: SubmissionReviewDialogComponent, isStandalone: true, selector: "lib-submission-review-dialog", ngImport: i0, template: "<span mat-dialog-title>\r\n Review Feedback For <span class=\"bidder-name\">{{ respondentName }}</span>\r\n</span>\r\n\r\n<div class=\"dialog-instructions\">\r\n <p>\r\n Your notes will be visible to the respondent. Provide <strong>clear, constructive feedback</strong> to\r\n guide their future submissions.\r\n </p>\r\n</div>\r\n<mat-dialog-content style=\"padding-bottom: 8px;padding-bottom: 8px\">\r\n <mat-form-field floatLabel=\"always\" subscriptSizing=\"dynamic\" style=\"width: 100%;\r\n display: absolute;\r\n left: 0;\r\n margin-bottom: 8px;\r\n margin-top: 8px;\r\n border-radius:16px;\" appearance=\"outline\">\r\n <lib-editor-js-input [value]=\"data.row['adj_microFlow_review_notes'] || data.activeRowNotes\"\r\n (valueChanged)=\"notesChanged($event)\" ngDefaultControl [inputConfig]=\"notesInputConfig\"\r\n [required]=\"notesInputConfig.required\">\r\n </lib-editor-js-input>\r\n <mat-hint>\r\n <strong>Tip:</strong> Use bold, lists, and tables to structure your feedback.\r\n </mat-hint>\r\n </mat-form-field>\r\n</mat-dialog-content>\r\n\r\n\r\n<mat-dialog-actions align=\"end\">\r\n <button mat-dialog-close mat-button >\r\n Cancel\r\n </button>\r\n @if( !data.readonly){\r\n <button mat-flat-button\r\n [mat-dialog-close]=\"changedNotes\"\r\n >\r\n Save Feedback\r\n </button>}\r\n</mat-dialog-actions>", styles: [":host{display:block;min-width:500px;max-width:700px}.bidder-name{font-weight:500}.summary-bar{margin-bottom:24px}.summary-bar h3{margin-bottom:8px;font-weight:500}.score-item{display:flex;align-items:center;justify-content:space-between;padding:8px 4px;gap:16px}.score-description{flex:1;margin-right:16px;line-height:1.4}.score-input{flex-shrink:0;width:150px}.mat-expansion-panel-body .score-item:last-child+mat-divider{display:none}mat-dialog-actions{padding:16px 24px}.mat-mdc-form-field-error{font-size:.8125em}.dialog-instructions{margin-top:-8px;margin-bottom:24px;max-width:95%;padding-left:24px;padding-right:24px}li{font-size:.75em}ol,ul{margin-top:8px}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$2.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$2.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$2.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$2.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: EditorJsInputComponent, selector: "lib-editor-js-input", inputs: ["inputConfig", "value"], outputs: ["valueChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
251
+ }
252
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SubmissionReviewDialogComponent, decorators: [{
253
+ type: Component,
254
+ args: [{ selector: 'lib-submission-review-dialog', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [MatDialogModule, MatFormFieldModule, MatButtonModule, EditorJsInputComponent], template: "<span mat-dialog-title>\r\n Review Feedback For <span class=\"bidder-name\">{{ respondentName }}</span>\r\n</span>\r\n\r\n<div class=\"dialog-instructions\">\r\n <p>\r\n Your notes will be visible to the respondent. Provide <strong>clear, constructive feedback</strong> to\r\n guide their future submissions.\r\n </p>\r\n</div>\r\n<mat-dialog-content style=\"padding-bottom: 8px;padding-bottom: 8px\">\r\n <mat-form-field floatLabel=\"always\" subscriptSizing=\"dynamic\" style=\"width: 100%;\r\n display: absolute;\r\n left: 0;\r\n margin-bottom: 8px;\r\n margin-top: 8px;\r\n border-radius:16px;\" appearance=\"outline\">\r\n <lib-editor-js-input [value]=\"data.row['adj_microFlow_review_notes'] || data.activeRowNotes\"\r\n (valueChanged)=\"notesChanged($event)\" ngDefaultControl [inputConfig]=\"notesInputConfig\"\r\n [required]=\"notesInputConfig.required\">\r\n </lib-editor-js-input>\r\n <mat-hint>\r\n <strong>Tip:</strong> Use bold, lists, and tables to structure your feedback.\r\n </mat-hint>\r\n </mat-form-field>\r\n</mat-dialog-content>\r\n\r\n\r\n<mat-dialog-actions align=\"end\">\r\n <button mat-dialog-close mat-button >\r\n Cancel\r\n </button>\r\n @if( !data.readonly){\r\n <button mat-flat-button\r\n [mat-dialog-close]=\"changedNotes\"\r\n >\r\n Save Feedback\r\n </button>}\r\n</mat-dialog-actions>", styles: [":host{display:block;min-width:500px;max-width:700px}.bidder-name{font-weight:500}.summary-bar{margin-bottom:24px}.summary-bar h3{margin-bottom:8px;font-weight:500}.score-item{display:flex;align-items:center;justify-content:space-between;padding:8px 4px;gap:16px}.score-description{flex:1;margin-right:16px;line-height:1.4}.score-input{flex-shrink:0;width:150px}.mat-expansion-panel-body .score-item:last-child+mat-divider{display:none}mat-dialog-actions{padding:16px 24px}.mat-mdc-form-field-error{font-size:.8125em}.dialog-instructions{margin-top:-8px;margin-bottom:24px;max-width:95%;padding-left:24px;padding-right:24px}li{font-size:.75em}ol,ul{margin-top:8px}\n"] }]
255
+ }] });
256
+
257
+ class SubmissionReviewComponent {
258
+ constructor() {
259
+ this.#config = inject(NGX_T_FORMS_CONFIG_TOKEN);
260
+ this.#destroyRef = inject(DestroyRef);
261
+ this.dialog = inject(MatDialog);
262
+ this.#refreshTrigger = new BehaviorSubject(undefined);
263
+ // A stream to push updates of the review values (`this.value`)
264
+ this.#valueChanges$ = new BehaviorSubject([]);
265
+ this.#valueSignal = signal([], ...(ngDevMode ? [{ debugName: "#valueSignal" }] : /* istanbul ignore next */ []));
266
+ this.#value = [];
267
+ this.inputConfig = input(undefined, ...(ngDevMode ? [{ debugName: "inputConfig" }] : /* istanbul ignore next */ []));
268
+ /** The current review payload. Null while loading. */
269
+ // Parent (`workflow-adjudication-reactive-input`) passes a `WorkflowAdjudicationValue | null`
270
+ // union that includes three incompatible shapes (score-sheet array, micro-flow review array,
271
+ // response-elected-supplier object). This component only consumes the micro-flow branch; the
272
+ // input must remain `any` until parent template typing is split per micro-flow.
273
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO(phase-4): split parent template by adjudication step type, then narrow this input.
274
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
275
+ this.valueBridge = effect(() => {
276
+ const next = this.value() ?? [];
277
+ if (_isEqual(next, this.#value))
278
+ return;
279
+ this.#value = next;
280
+ this.#valueSignal.set(next);
281
+ this.#valueChanges$.next(next);
282
+ }, ...(ngDevMode ? [{ debugName: "valueBridge" }] : /* istanbul ignore next */ []));
283
+ this.reviewChange = output();
284
+ this.selected = '';
285
+ this.columnMapping = undefined;
286
+ this.submissions = [];
287
+ this.#initialState = {
288
+ loading: true,
289
+ submissions: [],
290
+ columnMapping: { respondentNameKey: '', respondentCodeKey: '' },
291
+ displayCols: [],
292
+ displayColumns: [],
293
+ error: undefined,
294
+ };
295
+ // Stream for the initial data fetch. Only re-runs when `refreshTrigger` is fired.
296
+ this.#fetchedData$ = this.#refreshTrigger.pipe(switchMap(() => this.#config.formBuilder.getCurrentStepMicroflowSubmissions().pipe(take(1))), shareReplay(1));
297
+ /**
298
+ * REFACTORED: The new vm$ combines the one-time fetched data with the ongoing stream
299
+ * of local review changes (`valueChanges$`). This prevents re-fetching from the server
300
+ * every time a status or note is changed.
301
+ */
302
+ this.vm$ = combineLatest([
303
+ this.#fetchedData$,
304
+ this.#valueChanges$
305
+ ]).pipe(map(([res, reviewValues]) => {
306
+ const { submissions, columnMapping } = res;
307
+ this.columnMapping = columnMapping;
308
+ this.submissions = submissions;
309
+ // FIX: Pass reviewValues as an argument to make the function pure.
310
+ const enrichedSubmissions = this.#enrichSubmissionsData(submissions, columnMapping, reviewValues) || this.#value || [];
311
+ const calcKey = columnMapping.responsePreferenceCalculationKey || 'adjudicationPricePreferenceCalculation';
312
+ const sortedSubmissions = [...enrichedSubmissions].sort((a, b) => Number(b[calcKey]) - Number(a[calcKey]));
313
+ const newVm = {
314
+ loading: false,
315
+ submissions: sortedSubmissions || this.#value,
316
+ columnMapping: columnMapping,
317
+ displayCols: [columnMapping.respondentNameKey, 'adjudicationPricePreferenceCalculation', 'status', 'actions'],
318
+ displayColumns: [
319
+ { label: 'name', formControlName: columnMapping.respondentNameKey, type: InputDataTypes.String },
320
+ { label: 'Preference', formControlName: 'adjudicationPricePreferenceCalculation', type: InputDataTypes.Number, showColor: true },
321
+ { label: 'Actions', formControlName: 'actions', type: InputDataTypes.String },
322
+ { label: 'status and Notes', formControlName: 'status', type: 'actions' },
323
+ ],
324
+ error: undefined,
325
+ };
326
+ return newVm;
327
+ }), startWith(this.#initialState), catchError(() => {
328
+ return of({ ...this.#initialState, loading: false, error: 'Failed to load submissions.' });
329
+ }), shareReplay(1));
330
+ this.reviewStatuses = [
331
+ {
332
+ label: '✅ Compliant',
333
+ value: 'compliant',
334
+ color: 'var(--mat-sys-primary)',
335
+ },
336
+ {
337
+ label: '❌ Non-compliant',
338
+ value: 'non-compliant',
339
+ color: 'var(--mat-sys-error)',
340
+ },
341
+ ];
342
+ this.maxLimit = computed(() => {
343
+ const hasMax = this.inputConfig()?.maxLength;
344
+ if (!hasMax || isNaN(Number(hasMax))) {
345
+ return undefined;
346
+ }
347
+ return Number(hasMax);
348
+ }, ...(ngDevMode ? [{ debugName: "maxLimit" }] : /* istanbul ignore next */ []));
349
+ this.notesOpenForRowId = null;
350
+ this.bulkOperations = [
351
+ {
352
+ label: '✅ Set all to Compliant',
353
+ value: 'compliant',
354
+ color: 'var(--mat-sys-primary)',
355
+ },
356
+ {
357
+ label: '❌ Set all to Non-compliant',
358
+ value: 'non-compliant',
359
+ color: 'var(--mat-sys-error)',
360
+ },
361
+ {
362
+ label: '🔄 Reset all ',
363
+ value: 'reset',
364
+ color: 'var(--mat-sys-secondary)',
365
+ }
366
+ ];
367
+ this.bulkStatus = computed(() => {
368
+ // Re-read on submissions and value changes via the signal
369
+ void this.#valueSignal();
370
+ const allCompliant = this.submissions.every((s) => s['adj_microFlow_review'] === 'compliant');
371
+ if (allCompliant) {
372
+ return 'compliant';
373
+ }
374
+ const allNonCompliant = this.submissions.every((s) => s['adj_microFlow_review'] === 'non-compliant');
375
+ if (allNonCompliant) {
376
+ return 'non-compliant';
377
+ }
378
+ return 'mixed';
379
+ }, ...(ngDevMode ? [{ debugName: "bulkStatus" }] : /* istanbul ignore next */ []));
380
+ }
381
+ #config;
382
+ #destroyRef;
383
+ #refreshTrigger;
384
+ // A stream to push updates of the review values (`this.value`)
385
+ #valueChanges$;
386
+ #valueSignal;
387
+ #value;
388
+ #initialState;
389
+ // Stream for the initial data fetch. Only re-runs when `refreshTrigger` is fired.
390
+ #fetchedData$;
391
+ loadMicroSubmissions() {
392
+ this.#refreshTrigger.next();
393
+ }
394
+ /**
395
+ * REFACTORED: Now accepts `reviewValues` as a parameter instead of relying on `this.value`.
396
+ */
397
+ #enrichSubmissionsData(submissions, cols, reviewValues) {
398
+ const numberSets = new Map();
399
+ numberSets.set(cols.responsePreferenceCalculationKey || 'adjudicationPricePreferenceCalculation', submissions.map((s) => Number(s[cols.responsePreferenceCalculationKey || 'adjudicationPricePreferenceCalculation'] || 0)));
400
+ // NOTE(phase-4): the legacy fallback assigns review-value rows into a submission-shaped slot —
401
+ // the two only share `id`, so most downstream property reads degrade to undefined. Preserved
402
+ // for behavioural parity. `ISubmission` extends `Record<string, any>`, so review-value entries
403
+ // satisfy the structural shape (with most fields as undefined). Flagged as a pre-existing
404
+ // latent defect — out of scope for Phase 4 type tightening.
405
+ const list = submissions.length > 0
406
+ ? submissions
407
+ : (this.#value ?? []);
408
+ return list.map((submission) => {
409
+ const priceCalcKey = cols.responsePreferenceCalculationKey || 'adjudicationPricePreferenceCalculation';
410
+ const numbers = numberSets.get(priceCalcKey) || [];
411
+ const color = numbers.length === 0 ? submission.adjudicationPricePreferenceCalculationColor : this.#getColorString(submission[priceCalcKey], numbers);
412
+ const backgroundShades = numbers.length === 0 ? submission['backgroundShades'] : { [priceCalcKey]: this.#getScoreShade(color ?? '') };
413
+ // Use the passed `reviewValues` argument
414
+ // T-A widened upstream `adj_microFlow_review_notes` to `unknown` (consumer-side narrowing).
415
+ // The local consumer expects an OutputData payload; narrow with a cast at the boundary.
416
+ const submissionReview = (reviewValues?.find((v) => v.id === submission.id) ?? {});
417
+ return {
418
+ ...submission,
419
+ submissionIsReviewed: !!submissionReview,
420
+ adj_microFlow_review_notes: submissionReview?.adj_microFlow_review_notes || '',
421
+ submissionHasReviewNotes: !!submissionReview?.adj_microFlow_review_notes,
422
+ adj_microFlow_review: submissionReview?.['adj_microFlow_review'],
423
+ adjudicationPricePreferenceCalculationColor: color,
424
+ adjudicationPricePreferenceCalculation: submission[cols.responsePreferenceCalculationKey || 'adjudicationPricePreferenceCalculation'],
425
+ backgroundShades
426
+ };
427
+ });
428
+ }
429
+ #getColorString(target, numbers) {
430
+ return getColorForNumber(target, numbers, true);
431
+ }
432
+ #getScoreShade(rgbColor) {
433
+ if (!rgbColor)
434
+ return '';
435
+ return rgbColor.replace('rgb', 'rgba').replace(')', `, 0.1)`);
436
+ }
437
+ viewMicroSubmission(res) {
438
+ const alreadyInValue = this.#value?.find((v) => v.id === res.id);
439
+ if (!alreadyInValue) {
440
+ const newReview = this.#buildReviewRecord(res, undefined);
441
+ const newValue = [...(this.#value || []), newReview];
442
+ this.reviewChange.emit(newValue);
443
+ }
444
+ this.#config.formBuilder['previewWorkflowDocument'](res.id);
445
+ }
446
+ /**
447
+ * Builds a review record from a submission row. Status, createdAt and updatedAt are
448
+ * all optional on the shared `WorkflowAdjudicationMicroFlowReviewValue` type, so
449
+ * `undefined` flows through naturally without any casts.
450
+ */
451
+ #buildReviewRecord(res, status) {
452
+ const cm = this.columnMapping;
453
+ const createdAt = res[cm?.responseDateKey || 'createdAt'] || undefined;
454
+ const updatedAt = res[cm?.responseDateKey || 'updatedAt'] || undefined;
455
+ return {
456
+ id: res.id,
457
+ adj_microFlow_review: status,
458
+ nameOfBidder: res[cm?.respondentNameKey || ''] || '',
459
+ supplierNumber: res[cm?.respondentCodeKey || ''] || '',
460
+ createdAt,
461
+ updatedAt,
462
+ adjudicationPricePreferenceCalculation: res[cm?.responsePreferenceCalculationKey || 'adjudicationPricePreferenceCalculation'] || 0,
463
+ reference: res['reference'] || '',
464
+ };
465
+ }
466
+ setResponseStatus(res, status) {
467
+ const alreadyInValue = this.#value?.find((v) => v.id === res.id);
468
+ const statusValue = status.value;
469
+ if (!alreadyInValue) {
470
+ const newReview = this.#buildReviewRecord(res, statusValue);
471
+ this.reviewChange.emit([...(this.#value || []), newReview]);
472
+ this.reachedMax([...(this.#value || []), newReview]);
473
+ return;
474
+ }
475
+ const newValue = (this.#value || []).map((v) => {
476
+ if (v.id === res.id) {
477
+ return { ...v, adj_microFlow_review: statusValue };
478
+ }
479
+ return v;
480
+ });
481
+ this.reviewChange.emit(newValue);
482
+ this.reachedMax(newValue);
483
+ }
484
+ reachedMax(newValue) {
485
+ const hasMax = this.inputConfig()?.maxLength;
486
+ if (!hasMax || isNaN(Number(hasMax))) {
487
+ return; // No limit set, do nothin
488
+ }
489
+ const allCompetentSubmissions = newValue?.filter((v) => v.adj_microFlow_review === 'compliant').length || 0;
490
+ if (!(allCompetentSubmissions >= (Number(hasMax) || 1))) {
491
+ return;
492
+ }
493
+ const missingSubmission = this.submissions
494
+ .filter((s) => !newValue?.find((v) => v.id === s.id))
495
+ .map((s) => this.#buildReviewRecord(s, 'non-compliant'));
496
+ const unique = new Set([...(newValue || []), ...missingSubmission]);
497
+ this.reviewChange.emit(Array.from(unique));
498
+ }
499
+ // FIX: A Map to hold notes being edited, keyed by row ID. This prevents shared state.
500
+ /**
501
+ * REFACTORED: Saves the notes from the `notesBeingEdited` map.
502
+ */
503
+ notesBlur(notesToSave) {
504
+ if (!this.notesOpenForRowId || !notesToSave) {
505
+ this.notesOpenForRowId = null;
506
+ return;
507
+ }
508
+ const alreadyInValue = this.#value?.find((v) => v.id === this.notesOpenForRowId);
509
+ if (!alreadyInValue) {
510
+ const newReview = {
511
+ id: this.notesOpenForRowId,
512
+ adj_microFlow_review_notes: notesToSave,
513
+ adj_microFlow_review: undefined,
514
+ nameOfBidder: '',
515
+ supplierNumber: '',
516
+ createdAt: undefined,
517
+ updatedAt: undefined,
518
+ adjudicationPricePreferenceCalculation: 0,
519
+ reference: '',
520
+ };
521
+ this.reviewChange.emit([...(this.#value || []), newReview]);
522
+ }
523
+ else {
524
+ const newValue = (this.#value ?? []).map((v) => {
525
+ if (v.id === this.notesOpenForRowId) {
526
+ return { ...v, adj_microFlow_review_notes: notesToSave };
527
+ }
528
+ return v;
529
+ });
530
+ this.reviewChange.emit(newValue);
531
+ }
532
+ // Clean up and close the overlay
533
+ }
534
+ bulkStatusChanged(event) {
535
+ const newStatus = event.value;
536
+ if (newStatus === 'reset') {
537
+ this.reviewChange.emit([]);
538
+ return;
539
+ }
540
+ // Create a Map of existing values for efficient O(1) lookup
541
+ const existingValueMap = new Map((this.#value ?? []).map(v => [v.id, v]));
542
+ // Destructure column keys with fallbacks for cleaner access
543
+ const { respondentNameKey = '', respondentCodeKey = '', responseDateKey = 'createdAt', responsePreferenceCalculationKey = 'adjudicationPricePreferenceCalculation', } = this.columnMapping || {};
544
+ let allItems = this.submissions.map(s => {
545
+ // Efficiently look up the existing value using the Map
546
+ const existingValue = existingValueMap.get(s.id);
547
+ // If it exists, spread its properties to preserve them and update the status
548
+ if (existingValue) {
549
+ return { ...existingValue, adj_microFlow_review: newStatus };
550
+ }
551
+ // Otherwise, create a new object from the submission data
552
+ return {
553
+ id: s.id,
554
+ adj_microFlow_review: newStatus,
555
+ nameOfBidder: s[respondentNameKey] || '',
556
+ supplierNumber: s[respondentCodeKey] || '',
557
+ createdAt: s[responseDateKey] || undefined,
558
+ updatedAt: s[this.columnMapping?.responseDateKey || 'updatedAt'] || undefined,
559
+ adjudicationPricePreferenceCalculation: s[responsePreferenceCalculationKey] || 0,
560
+ reference: s['reference'] || '',
561
+ };
562
+ });
563
+ // Apply the compliance limit if necessary
564
+ const limit = this.maxLimit();
565
+ if (limit !== undefined && newStatus === 'compliant') {
566
+ // Sort by preference calculation, highest first
567
+ allItems.sort((a, b) => (b.adjudicationPricePreferenceCalculation ?? 0) - (a.adjudicationPricePreferenceCalculation ?? 0));
568
+ // Mark items beyond the limit as 'non-compliant'
569
+ allItems = allItems.map((item, index) => {
570
+ if (index >= limit) {
571
+ return { ...item, adj_microFlow_review: 'non-compliant' };
572
+ }
573
+ return item;
574
+ });
575
+ }
576
+ this.reviewChange.emit(allItems);
577
+ }
578
+ openNotes(row) {
579
+ this.notesOpenForRowId = row.id;
580
+ const stringfiedTemplate = JSON.stringify(this.inputConfig()?.adjudicationReviewFeedbackTemplate);
581
+ const template = stringfiedTemplate?.replace(/\$bidderName/g, row[this.columnMapping?.respondentNameKey || ''])
582
+ ?.replace(/\$bidderCode/g, row[this.columnMapping?.respondentCodeKey || ''])
583
+ ?.replace(/\$responseDate/g, row[this.columnMapping?.responseDateKey || ''])
584
+ ?.replace(/\$responsePreferenceCalculation/g, row[this.columnMapping?.responsePreferenceCalculationKey || ''] || '0');
585
+ this.dialog.open(SubmissionReviewDialogComponent, {
586
+ height: 'fit-content',
587
+ data: {
588
+ row,
589
+ respondentNameKey: this.columnMapping?.respondentNameKey || '',
590
+ respondentCodeKey: this.columnMapping?.respondentCodeKey || '',
591
+ readonly: this.inputConfig()?.readonly || false,
592
+ activeRowNotes: JSON.parse(template || '{}')
593
+ },
594
+ autoFocus: false,
595
+ disableClose: true
596
+ }).afterClosed().pipe(take(1), tap((result) => {
597
+ this.notesBlur(result);
598
+ }), takeUntilDestroyed(this.#destroyRef)).subscribe();
599
+ }
600
+ canChangeStatus(row) {
601
+ if (row['adj_microFlow_review'] === 'compliant')
602
+ return true;
603
+ const maxLength = this.inputConfig()?.maxLength;
604
+ // check if max exists and is a number
605
+ if (!maxLength || isNaN(Number(maxLength))) {
606
+ return true; // No limit set, allow status change
607
+ }
608
+ const allCompetentSubmissions = this.#value?.filter((v) => v.adj_microFlow_review === 'compliant').length || 0;
609
+ return allCompetentSubmissions < (Number(maxLength) || 1);
610
+ }
611
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SubmissionReviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
612
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: SubmissionReviewComponent, isStandalone: true, selector: "lib-submission-review", inputs: { inputConfig: { classPropertyName: "inputConfig", publicName: "inputConfig", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { reviewChange: "reviewChange" }, ngImport: i0, template: "@if (vm$ | async; as vm) {\r\n\r\n @if (vm.loading) {\r\n <div style=\"display: flex; justify-content: center; align-items: center; padding:24px;width: 100%;\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n } @else if (vm.error) {\r\n <div class=\"info-card error-card\">\r\n <h5 class=\"info-title\">\u274C Failed to load data</h5>\r\n <p>{{ vm.error }}</p>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\">\r\n Try Again\r\n </button>\r\n </div>\r\n } @else {\r\n @if (!vm.displayCols.length) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u2699\uFE0F This step needs to be configured to show data.</h5>\r\n <ol>\r\n <li>\r\n <strong>Why you're seeing this:</strong>\r\n <ul>\r\n <li>This component is designed for a <strong>\"Submission Review\"</strong> workflow step.</li>\r\n <li>It's currently missing the required column setup.</li>\r\n </ul>\r\n </li>\r\n <li>\r\n <strong>What to do (for Administrators):</strong>\r\n To fix this, go to the <strong>Workflow Builder</strong> and for this specific step, <strong>map the display\r\n columns</strong> to the desired submission data (e.g., form fields or document keys).\r\n </li>\r\n </ol>\r\n </div>\r\n } @else if (!vm.submissions.length) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u2139\uFE0F There are no submissions to display.</h5>\r\n <ul>\r\n <li>This can happen if <strong>no one has responded</strong> yet, or if the <strong>submission closing date has\r\n passed</strong>.</li>\r\n <li>If you are expecting to see submissions, try clicking the <strong>Refresh</strong> button.</li>\r\n </ul>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\" class=\"load-button\">\r\n Refresh\r\n </button>\r\n </div>\r\n } @else {\r\n @if (!value() || (value()?.length ?? 0) === 0) {\r\n <!-- value() typed `any` per parent's mixed union; length check survives null -->\r\n\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\uD83D\uDCDD How to Review Submissions</h5>\r\n <ol>\r\n <li>\r\n <strong>View Response:</strong> Click the <strong>Preview</strong> (<mat-icon\r\n class=\"smallIcon\">preview</mat-icon>) icon to see the full submission.\r\n </li>\r\n <li>\r\n <strong>Set Status:</strong> Use the dropdown to select <strong>Compliant</strong> or\r\n <strong>Non-compliant</strong>.\r\n </li>\r\n <li>\r\n <strong>Add Note (If Non-compliant):</strong>\r\n <ul>\r\n <li>If you select <strong>Non-compliant</strong>, you <strong>must</strong> add a note.</li>\r\n <li>Click the <strong>Add Note</strong> (<mat-icon class=\"smallIcon\">add_comment</mat-icon>) icon, write your\r\n feedback, and hit <strong>Save Feedback</strong>. Your note will be visible to the applicant.</li>\r\n </ul>\r\n </li>\r\n </ol>\r\n </div>\r\n }\r\n <table mat-table [dataSource]=\"vm.submissions\">\r\n @for (col of vm.displayColumns; track col.formControlName) {\r\n <ng-container [matColumnDef]=\"col.formControlName\">\r\n <th mat-header-cell *matHeaderCellDef style=\"padding:4px\">\r\n\r\n @if (col.formControlName === 'status' && !inputConfig()?.readonly) {\r\n <mat-form-field floatLabel=\"always\" subscriptSizing=\"dynamic\" style=\"width: 100%;\r\n margin-bottom: 8px;\r\n margin-top: 8px;\r\n border-radius:16px;\" appearance=\"outline\">\r\n <mat-label>\r\n Bulk Status Selection\r\n </mat-label>\r\n <mat-select\r\n [style.color]=\"bulkStatus() === 'non-compliant' ? 'var(--mat-sys-error)' : bulkStatus()==='compliant'? 'var(--mat-sys-primary)' : ''\"\r\n (selectionChange)=\"bulkStatusChanged($event)\" ngDefaultControl [value]=\"bulkStatus()\"\r\n placeholder=\" Select Status\">\r\n @for (type of bulkOperations || []; track type) {\r\n <mat-option [value]=\"type.value\">{{type.label}}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n } @else {\r\n {{ col.label }}\r\n }\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" mat-ripple class=\"table-cell\" [class.selected-row]=\"selected === row.id\"\r\n (click)=\"$event.stopPropagation()\" [class.price-preference-cell]=\"true\"\r\n [style.border-bottom-color]=\"row.adjudicationPricePreferenceCalculationColor\"\r\n [style.background]=\"row.backgroundShades?.[col.formControlName]\">\r\n\r\n @if (col.formControlName === 'actions') {\r\n <span style=\"display: flex; align-items: center;\">\r\n <button matTooltipPosition=\"before\"\r\n [matTooltip]=\"`View and review **` + row[vm.columnMapping.respondentNameKey]+ `** 's' Submission`\"\r\n (click)=\"viewMicroSubmission(row)\" mat-icon-button>\r\n <mat-icon>preview</mat-icon>\r\n </button>\r\n @if (!(inputConfig()?.readonly) || (inputConfig()?.readonly) && !!row['submissionHasReviewNotes']) {\r\n <button (click)=\"openNotes(row)\" cdkOverlayOrigin #trigger=\"cdkOverlayOrigin\" matTooltipPosition=\"after\"\r\n [matTooltip]=\"\r\n `Add a review note for **` + row[vm.columnMapping.respondentNameKey] + `**`\" mat-icon-button>\r\n <mat-icon [style.color]=\"\r\n notesOpenForRowId === row.id ? 'var(--mat-sys-primary)' : (row['adj_microFlow_review']==='non-compliant' && !row['adj_microFlow_review_notes']?'var(--mat-sys-error)':'')\r\n \">\r\n {{ !row['submissionHasReviewNotes']? 'add_comment' : 'comment' }}\r\n </mat-icon>\r\n </button>\r\n }\r\n </span>\r\n } @else if (col.formControlName === 'status') {\r\n <mat-form-field floatLabel=\"always\" subscriptSizing=\"dynamic\" style=\"width: 100%;\r\n margin-bottom: 8px;\r\n margin-top: 8px;\r\n border-radius:16px;\" appearance=\"outline\">\r\n <mat-select [disabled]=\"!canChangeStatus(row) || inputConfig()?.readonly === true\"\r\n [style.color]=\"row['adj_microFlow_review'] === 'non-compliant' ? 'var(--mat-sys-error)' : row['adj_microFlow_review']==='compliant'? 'var(--mat-sys-primary)' : ''\"\r\n (selectionChange)=\"setResponseStatus(row,$event)\" ngDefaultControl [value]=\"row.adj_microFlow_review\"\r\n placeholder=\" Select Status\">\r\n @for (type of reviewStatuses || []; track type) {\r\n <mat-option [value]=\"type.value\">{{type.label}}</mat-option>\r\n }\r\n </mat-select>\r\n @if (row['adj_microFlow_review']==='non-compliant' && !row['adj_microFlow_review_notes']) {\r\n <mat-hint style=\"color:var(--mat-sys-error)\">\r\n Rejection reason is required. Add a review note.\r\n </mat-hint>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n @switch (col?.type) {\r\n @case ('daysAgo') {\r\n {{ row[col.formControlName] | daysAgo }}\r\n }\r\n @case ('number') {\r\n <div class=\"number-cell\">\r\n <span>\r\n {{ (row[col.formControlName] | number:'0.0-0' ) || 'N/A' }}\r\n </span>\r\n @if (col.showColor) {\r\n <mat-icon [style.color]=\"row.adjudicationPricePreferenceCalculationColor\">\r\n fiber_manual_record\r\n </mat-icon>\r\n }\r\n </div>\r\n }\r\n @default {\r\n @if ($index === 0) {\r\n <span class=\"main-submission-text\">\r\n <mat-icon style=\"font-size: 1em;height:16px;width:16px\">domain</mat-icon>\r\n <strong>\r\n {{ row[col.formControlName] }}\r\n </strong>\r\n </span>\r\n <span class=\"secondary-text\">\r\n <mat-icon>fingerprint</mat-icon>\r\n {{row[vm.columnMapping.respondentCodeKey]}}\r\n <mat-icon>more_time</mat-icon>\r\n {{ row[vm.columnMapping.responseDateKey ||''] | daysAgo}}\r\n </span>\r\n } @else {\r\n {{ row[col.formControlName] }}\r\n }\r\n }\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n <tr mat-header-row *matHeaderRowDef=\"vm.displayCols\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: vm.displayCols\"></tr>\r\n </table>\r\n }\r\n }\r\n\r\n}\r\n", styles: [".number{display:flex;justify-content:space-between;align-items:center}li{font-size:.75em}ol,ul{margin-top:8px}.number-cell{display:flex;justify-content:space-between;align-items:center;width:100%}.secondary-text{display:flex;align-items:center;white-space:pre;font-size:.75em;gap:8px;opacity:.8}.secondary-text mat-icon{font-size:.875em;width:14px;height:14px}.mat-column-adjudicationPricePreferenceCalculation{max-width:70px}.mat-column-actions{padding:0;max-width:80px}.mat-column-status{padding-left:8px;padding-right:8px;max-width:fit-content!important}.main-submission-text{display:flex;align-items:center;gap:8px;font-size:.875em}:host .notes-card{width:100%;max-width:480px;resize:both;height:fit-content}.smallIcon{font-size:.875em;width:14px;height:14px}.table-cell{padding-left:4px;padding-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i2$3.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i7.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: DaysAgoPipe, name: "daysAgo" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
613
+ }
614
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SubmissionReviewComponent, decorators: [{
615
+ type: Component,
616
+ args: [{ selector: 'lib-submission-review', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [
617
+ AsyncPipe,
618
+ DecimalPipe,
619
+ FormsModule,
620
+ OverlayModule,
621
+ MatButtonModule,
622
+ MatFormFieldModule,
623
+ MatIconModule,
624
+ MatInputModule,
625
+ MatProgressSpinnerModule,
626
+ MatRippleModule,
627
+ MatSelectModule,
628
+ MatTableModule,
629
+ MatTooltipModule,
630
+ DaysAgoPipe,
631
+ ], template: "@if (vm$ | async; as vm) {\r\n\r\n @if (vm.loading) {\r\n <div style=\"display: flex; justify-content: center; align-items: center; padding:24px;width: 100%;\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n } @else if (vm.error) {\r\n <div class=\"info-card error-card\">\r\n <h5 class=\"info-title\">\u274C Failed to load data</h5>\r\n <p>{{ vm.error }}</p>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\">\r\n Try Again\r\n </button>\r\n </div>\r\n } @else {\r\n @if (!vm.displayCols.length) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u2699\uFE0F This step needs to be configured to show data.</h5>\r\n <ol>\r\n <li>\r\n <strong>Why you're seeing this:</strong>\r\n <ul>\r\n <li>This component is designed for a <strong>\"Submission Review\"</strong> workflow step.</li>\r\n <li>It's currently missing the required column setup.</li>\r\n </ul>\r\n </li>\r\n <li>\r\n <strong>What to do (for Administrators):</strong>\r\n To fix this, go to the <strong>Workflow Builder</strong> and for this specific step, <strong>map the display\r\n columns</strong> to the desired submission data (e.g., form fields or document keys).\r\n </li>\r\n </ol>\r\n </div>\r\n } @else if (!vm.submissions.length) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u2139\uFE0F There are no submissions to display.</h5>\r\n <ul>\r\n <li>This can happen if <strong>no one has responded</strong> yet, or if the <strong>submission closing date has\r\n passed</strong>.</li>\r\n <li>If you are expecting to see submissions, try clicking the <strong>Refresh</strong> button.</li>\r\n </ul>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\" class=\"load-button\">\r\n Refresh\r\n </button>\r\n </div>\r\n } @else {\r\n @if (!value() || (value()?.length ?? 0) === 0) {\r\n <!-- value() typed `any` per parent's mixed union; length check survives null -->\r\n\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\uD83D\uDCDD How to Review Submissions</h5>\r\n <ol>\r\n <li>\r\n <strong>View Response:</strong> Click the <strong>Preview</strong> (<mat-icon\r\n class=\"smallIcon\">preview</mat-icon>) icon to see the full submission.\r\n </li>\r\n <li>\r\n <strong>Set Status:</strong> Use the dropdown to select <strong>Compliant</strong> or\r\n <strong>Non-compliant</strong>.\r\n </li>\r\n <li>\r\n <strong>Add Note (If Non-compliant):</strong>\r\n <ul>\r\n <li>If you select <strong>Non-compliant</strong>, you <strong>must</strong> add a note.</li>\r\n <li>Click the <strong>Add Note</strong> (<mat-icon class=\"smallIcon\">add_comment</mat-icon>) icon, write your\r\n feedback, and hit <strong>Save Feedback</strong>. Your note will be visible to the applicant.</li>\r\n </ul>\r\n </li>\r\n </ol>\r\n </div>\r\n }\r\n <table mat-table [dataSource]=\"vm.submissions\">\r\n @for (col of vm.displayColumns; track col.formControlName) {\r\n <ng-container [matColumnDef]=\"col.formControlName\">\r\n <th mat-header-cell *matHeaderCellDef style=\"padding:4px\">\r\n\r\n @if (col.formControlName === 'status' && !inputConfig()?.readonly) {\r\n <mat-form-field floatLabel=\"always\" subscriptSizing=\"dynamic\" style=\"width: 100%;\r\n margin-bottom: 8px;\r\n margin-top: 8px;\r\n border-radius:16px;\" appearance=\"outline\">\r\n <mat-label>\r\n Bulk Status Selection\r\n </mat-label>\r\n <mat-select\r\n [style.color]=\"bulkStatus() === 'non-compliant' ? 'var(--mat-sys-error)' : bulkStatus()==='compliant'? 'var(--mat-sys-primary)' : ''\"\r\n (selectionChange)=\"bulkStatusChanged($event)\" ngDefaultControl [value]=\"bulkStatus()\"\r\n placeholder=\" Select Status\">\r\n @for (type of bulkOperations || []; track type) {\r\n <mat-option [value]=\"type.value\">{{type.label}}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n } @else {\r\n {{ col.label }}\r\n }\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" mat-ripple class=\"table-cell\" [class.selected-row]=\"selected === row.id\"\r\n (click)=\"$event.stopPropagation()\" [class.price-preference-cell]=\"true\"\r\n [style.border-bottom-color]=\"row.adjudicationPricePreferenceCalculationColor\"\r\n [style.background]=\"row.backgroundShades?.[col.formControlName]\">\r\n\r\n @if (col.formControlName === 'actions') {\r\n <span style=\"display: flex; align-items: center;\">\r\n <button matTooltipPosition=\"before\"\r\n [matTooltip]=\"`View and review **` + row[vm.columnMapping.respondentNameKey]+ `** 's' Submission`\"\r\n (click)=\"viewMicroSubmission(row)\" mat-icon-button>\r\n <mat-icon>preview</mat-icon>\r\n </button>\r\n @if (!(inputConfig()?.readonly) || (inputConfig()?.readonly) && !!row['submissionHasReviewNotes']) {\r\n <button (click)=\"openNotes(row)\" cdkOverlayOrigin #trigger=\"cdkOverlayOrigin\" matTooltipPosition=\"after\"\r\n [matTooltip]=\"\r\n `Add a review note for **` + row[vm.columnMapping.respondentNameKey] + `**`\" mat-icon-button>\r\n <mat-icon [style.color]=\"\r\n notesOpenForRowId === row.id ? 'var(--mat-sys-primary)' : (row['adj_microFlow_review']==='non-compliant' && !row['adj_microFlow_review_notes']?'var(--mat-sys-error)':'')\r\n \">\r\n {{ !row['submissionHasReviewNotes']? 'add_comment' : 'comment' }}\r\n </mat-icon>\r\n </button>\r\n }\r\n </span>\r\n } @else if (col.formControlName === 'status') {\r\n <mat-form-field floatLabel=\"always\" subscriptSizing=\"dynamic\" style=\"width: 100%;\r\n margin-bottom: 8px;\r\n margin-top: 8px;\r\n border-radius:16px;\" appearance=\"outline\">\r\n <mat-select [disabled]=\"!canChangeStatus(row) || inputConfig()?.readonly === true\"\r\n [style.color]=\"row['adj_microFlow_review'] === 'non-compliant' ? 'var(--mat-sys-error)' : row['adj_microFlow_review']==='compliant'? 'var(--mat-sys-primary)' : ''\"\r\n (selectionChange)=\"setResponseStatus(row,$event)\" ngDefaultControl [value]=\"row.adj_microFlow_review\"\r\n placeholder=\" Select Status\">\r\n @for (type of reviewStatuses || []; track type) {\r\n <mat-option [value]=\"type.value\">{{type.label}}</mat-option>\r\n }\r\n </mat-select>\r\n @if (row['adj_microFlow_review']==='non-compliant' && !row['adj_microFlow_review_notes']) {\r\n <mat-hint style=\"color:var(--mat-sys-error)\">\r\n Rejection reason is required. Add a review note.\r\n </mat-hint>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n @switch (col?.type) {\r\n @case ('daysAgo') {\r\n {{ row[col.formControlName] | daysAgo }}\r\n }\r\n @case ('number') {\r\n <div class=\"number-cell\">\r\n <span>\r\n {{ (row[col.formControlName] | number:'0.0-0' ) || 'N/A' }}\r\n </span>\r\n @if (col.showColor) {\r\n <mat-icon [style.color]=\"row.adjudicationPricePreferenceCalculationColor\">\r\n fiber_manual_record\r\n </mat-icon>\r\n }\r\n </div>\r\n }\r\n @default {\r\n @if ($index === 0) {\r\n <span class=\"main-submission-text\">\r\n <mat-icon style=\"font-size: 1em;height:16px;width:16px\">domain</mat-icon>\r\n <strong>\r\n {{ row[col.formControlName] }}\r\n </strong>\r\n </span>\r\n <span class=\"secondary-text\">\r\n <mat-icon>fingerprint</mat-icon>\r\n {{row[vm.columnMapping.respondentCodeKey]}}\r\n <mat-icon>more_time</mat-icon>\r\n {{ row[vm.columnMapping.responseDateKey ||''] | daysAgo}}\r\n </span>\r\n } @else {\r\n {{ row[col.formControlName] }}\r\n }\r\n }\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n <tr mat-header-row *matHeaderRowDef=\"vm.displayCols\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: vm.displayCols\"></tr>\r\n </table>\r\n }\r\n }\r\n\r\n}\r\n", styles: [".number{display:flex;justify-content:space-between;align-items:center}li{font-size:.75em}ol,ul{margin-top:8px}.number-cell{display:flex;justify-content:space-between;align-items:center;width:100%}.secondary-text{display:flex;align-items:center;white-space:pre;font-size:.75em;gap:8px;opacity:.8}.secondary-text mat-icon{font-size:.875em;width:14px;height:14px}.mat-column-adjudicationPricePreferenceCalculation{max-width:70px}.mat-column-actions{padding:0;max-width:80px}.mat-column-status{padding-left:8px;padding-right:8px;max-width:fit-content!important}.main-submission-text{display:flex;align-items:center;gap:8px;font-size:.875em}:host .notes-card{width:100%;max-width:480px;resize:both;height:fit-content}.smallIcon{font-size:.875em;width:14px;height:14px}.table-cell{padding-left:4px;padding-right:4px}\n"] }]
632
+ }], propDecorators: { inputConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputConfig", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], reviewChange: [{ type: i0.Output, args: ["reviewChange"] }] } });
633
+
634
+ /** Shows validation errors as soon as the control becomes dirty + invalid. */
635
+ class ShowErrorOnDirtyMatcher {
636
+ isErrorState(control, _form) {
637
+ return !!(control && control.invalid && control.dirty);
638
+ }
639
+ }
640
+ class PointAssignmentDialogComponent {
641
+ constructor() {
642
+ this.dialogRef = inject(MatDialogRef);
643
+ this.data = inject(MAT_DIALOG_DATA);
644
+ this.#fb = inject(FormBuilder);
645
+ this.maxPossiblePoints = 0;
646
+ this.matcher = new ShowErrorOnDirtyMatcher();
647
+ }
648
+ #fb;
649
+ ngOnInit() {
650
+ this.#calculateMaxPoints();
651
+ this.pointsForm = this.#buildForm();
652
+ this.totalPoints$ = this.#createTotalPointsObservable();
653
+ }
654
+ correctMaxValue(event, groupKey, controlId, maxPoints) {
655
+ const inputElement = event.target;
656
+ const currentValue = parseFloat(inputElement.value);
657
+ if (currentValue > maxPoints) {
658
+ this.pointsForm.get([groupKey, controlId])?.patchValue(maxPoints);
659
+ }
660
+ }
661
+ #buildForm() {
662
+ const groupOfGroups = this.data.scoreSheetGroups.reduce((acc, group) => {
663
+ const scoresInGroup = this.data.groupedScoreSheet[group.key] || [];
664
+ const groupControls = scoresInGroup.reduce((ctrls, score) => {
665
+ const existingScore = this.data.row.adj_microFlow_assignedScore?.[score.id] ?? null;
666
+ ctrls[score.id] = new FormControl(existingScore, [
667
+ Validators.required,
668
+ Validators.min(0),
669
+ Validators.max(score.totalPoints),
670
+ ]);
671
+ return ctrls;
672
+ }, {});
673
+ acc[group.key] = this.#fb.group(groupControls);
674
+ return acc;
675
+ }, {});
676
+ return this.#fb.group(groupOfGroups);
677
+ }
678
+ #calculateMaxPoints() {
679
+ this.maxPossiblePoints = Object.values(this.data.groupedScoreSheet)
680
+ .flat()
681
+ .reduce((acc, score) => acc + score.totalPoints, 0);
682
+ }
683
+ #createTotalPointsObservable() {
684
+ return this.pointsForm.valueChanges.pipe(startWith(this.pointsForm.value), map(formValue => {
685
+ let total = 0;
686
+ Object.values(formValue || {}).forEach((group) => {
687
+ if (group && typeof group === 'object') {
688
+ Object.values(group).forEach(score => {
689
+ total += Number(score) || 0;
690
+ });
691
+ }
692
+ });
693
+ return total;
694
+ }));
695
+ }
696
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PointAssignmentDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
697
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: PointAssignmentDialogComponent, isStandalone: true, selector: "lib-point-assignment-dialog", ngImport: i0, template: "<span mat-dialog-title>\r\n Assign Points for <span class=\"bidder-name\">{{ data.row['nameOfBidder'] }}</span>\r\n</span>\r\n @if( !data.readonly){\r\n<div class=\"dialog-instructions\">\r\n @if(!pointsForm.dirty && !pointsForm.touched){\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\uD83D\uDCAF How to Assign Points\r\n\r\n </h5>\r\n <ol>\r\n <li>\r\n <strong>Enter a score</strong> for each item listed below.\r\n </li>\r\n <li>\r\n <strong>Follow the rules:</strong>\r\n <ul>\r\n <li>Your score cannot be higher than the maximum points shown (e.g., / 10).</li>\r\n <li>The <strong>\"Save Points\"</strong> button at the bottom will turn blue when everything is filled\r\n out correctly.</li>\r\n </ul>\r\n </li>\r\n <li>\r\n <strong>Click the blue \"Save Points\" button</strong> to finish.\r\n </li>\r\n </ol>\r\n </div>\r\n }\r\n @else {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u270D\uFE0F Editing Your Scores</h5>\r\n <ol>\r\n <li>\r\n <strong>You are viewing saved scores.</strong> To make a change, simply click on a score and type in a\r\n new number.\r\n </li>\r\n <li>\r\n The <strong>Total Points</strong> bar at the top will update as you make edits.\r\n </li>\r\n <li>\r\n Click <strong>\"Save Points\"</strong> to confirm your changes when you're done.\r\n </li>\r\n </ol>\r\n </div>\r\n }\r\n</div>\r\n\r\n }\r\n\r\n\r\n<mat-dialog-content style=\"padding-bottom: 8px;padding-bottom: 8px\" [formGroup]=\"pointsForm\">\r\n <div class=\"summary-bar\">\r\n <h3>Total Points: {{ totalPoints$ | async }} / {{ maxPossiblePoints }}</h3>\r\n <mat-progress-bar mode=\"determinate\"\r\n [value]=\"((totalPoints$ | async) || 0) / maxPossiblePoints * 100\"></mat-progress-bar>\r\n </div>\r\n\r\n <mat-accordion multi>\r\n @for (group of data.scoreSheetGroups; track group.key) {\r\n <mat-expansion-panel [expanded]=\"true\" [formGroupName]=\"group.key\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>{{ group.name }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n\r\n @for (score of data.groupedScoreSheet[group.key] || []; track score.id) {\r\n <div class=\"score-item\">\r\n <label [for]=\"score.id\" class=\"score-description\">{{ score.description }}</label>\r\n\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"score-input\">\r\n <mat-label>Score</mat-label>\r\n <input [disabled]=\"data.readonly\" matInput type=\"number\" placeholder=\"0\" [id]=\"score.id\"\r\n [formControlName]=\"score.id\" [max]=\"score.totalPoints\" min=\"0\" [errorStateMatcher]=\"matcher\"\r\n (input)=\"correctMaxValue($event, group.key, score.id, score.totalPoints)\" />\r\n <span matSuffix>\r\n <span style=\"min-width: 58px; display:flex\">\r\n / {{ score.totalPoints }}\r\n </span>\r\n </span>\r\n\r\n @if (pointsForm.get(group.key)?.get(score.id)?.hasError('required')) {\r\n <mat-error>A score is required.</mat-error>\r\n }\r\n @if (pointsForm.get(group.key)?.get(score.id)?.hasError('max')) {\r\n <mat-error>Score cannot exceed {{ score.totalPoints }}.</mat-error>\r\n }\r\n @if (pointsForm.get(group.key)?.get(score.id)?.hasError('min')) {\r\n <mat-error>Score cannot be negative.</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n <mat-divider></mat-divider>\r\n }\r\n </mat-expansion-panel>\r\n }\r\n </mat-accordion>\r\n\r\n</mat-dialog-content>\r\n\r\n<mat-dialog-actions align=\"end\">\r\n <button mat-button mat-dialog-close>Cancel</button>\r\n @if( !data.readonly){\r\n <button mat-flat-button color=\"primary\" \r\n [mat-dialog-close]=\"pointsForm.getRawValue()\"\r\n [disabled]=\"pointsForm.invalid\">\r\n Save Points\r\n </button>\r\n }\r\n\r\n</mat-dialog-actions>", styles: [":host{display:block;min-width:500px;max-width:700px}.bidder-name{font-weight:500}.summary-bar{margin-bottom:24px}.summary-bar h3{margin-bottom:8px;font-weight:500}.score-item{display:flex;align-items:center;justify-content:space-between;padding:8px 4px;gap:16px}.score-description{flex:1;margin-right:16px;line-height:1.4}.score-input{flex-shrink:0;width:150px}.mat-expansion-panel-body .score-item:last-child+mat-divider{display:none}mat-dialog-actions{padding:16px 24px}.mat-mdc-form-field-error{font-size:.8125em}.dialog-instructions{margin-top:-8px;margin-bottom:24px;max-width:95%;padding-left:24px;padding-right:24px}li{font-size:.75em}ol,ul{margin-top:8px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$2.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$2.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$2.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$2.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i1$3.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i5$3.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i5$3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5$3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5$3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i4.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
698
+ }
699
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PointAssignmentDialogComponent, decorators: [{
700
+ type: Component,
701
+ args: [{ selector: 'lib-point-assignment-dialog', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [
702
+ AsyncPipe,
703
+ ReactiveFormsModule,
704
+ MatDialogModule,
705
+ MatFormFieldModule,
706
+ MatInputModule,
707
+ MatButtonModule,
708
+ MatProgressBarModule,
709
+ MatExpansionModule,
710
+ MatDividerModule,
711
+ ], template: "<span mat-dialog-title>\r\n Assign Points for <span class=\"bidder-name\">{{ data.row['nameOfBidder'] }}</span>\r\n</span>\r\n @if( !data.readonly){\r\n<div class=\"dialog-instructions\">\r\n @if(!pointsForm.dirty && !pointsForm.touched){\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\uD83D\uDCAF How to Assign Points\r\n\r\n </h5>\r\n <ol>\r\n <li>\r\n <strong>Enter a score</strong> for each item listed below.\r\n </li>\r\n <li>\r\n <strong>Follow the rules:</strong>\r\n <ul>\r\n <li>Your score cannot be higher than the maximum points shown (e.g., / 10).</li>\r\n <li>The <strong>\"Save Points\"</strong> button at the bottom will turn blue when everything is filled\r\n out correctly.</li>\r\n </ul>\r\n </li>\r\n <li>\r\n <strong>Click the blue \"Save Points\" button</strong> to finish.\r\n </li>\r\n </ol>\r\n </div>\r\n }\r\n @else {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u270D\uFE0F Editing Your Scores</h5>\r\n <ol>\r\n <li>\r\n <strong>You are viewing saved scores.</strong> To make a change, simply click on a score and type in a\r\n new number.\r\n </li>\r\n <li>\r\n The <strong>Total Points</strong> bar at the top will update as you make edits.\r\n </li>\r\n <li>\r\n Click <strong>\"Save Points\"</strong> to confirm your changes when you're done.\r\n </li>\r\n </ol>\r\n </div>\r\n }\r\n</div>\r\n\r\n }\r\n\r\n\r\n<mat-dialog-content style=\"padding-bottom: 8px;padding-bottom: 8px\" [formGroup]=\"pointsForm\">\r\n <div class=\"summary-bar\">\r\n <h3>Total Points: {{ totalPoints$ | async }} / {{ maxPossiblePoints }}</h3>\r\n <mat-progress-bar mode=\"determinate\"\r\n [value]=\"((totalPoints$ | async) || 0) / maxPossiblePoints * 100\"></mat-progress-bar>\r\n </div>\r\n\r\n <mat-accordion multi>\r\n @for (group of data.scoreSheetGroups; track group.key) {\r\n <mat-expansion-panel [expanded]=\"true\" [formGroupName]=\"group.key\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>{{ group.name }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n\r\n @for (score of data.groupedScoreSheet[group.key] || []; track score.id) {\r\n <div class=\"score-item\">\r\n <label [for]=\"score.id\" class=\"score-description\">{{ score.description }}</label>\r\n\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"score-input\">\r\n <mat-label>Score</mat-label>\r\n <input [disabled]=\"data.readonly\" matInput type=\"number\" placeholder=\"0\" [id]=\"score.id\"\r\n [formControlName]=\"score.id\" [max]=\"score.totalPoints\" min=\"0\" [errorStateMatcher]=\"matcher\"\r\n (input)=\"correctMaxValue($event, group.key, score.id, score.totalPoints)\" />\r\n <span matSuffix>\r\n <span style=\"min-width: 58px; display:flex\">\r\n / {{ score.totalPoints }}\r\n </span>\r\n </span>\r\n\r\n @if (pointsForm.get(group.key)?.get(score.id)?.hasError('required')) {\r\n <mat-error>A score is required.</mat-error>\r\n }\r\n @if (pointsForm.get(group.key)?.get(score.id)?.hasError('max')) {\r\n <mat-error>Score cannot exceed {{ score.totalPoints }}.</mat-error>\r\n }\r\n @if (pointsForm.get(group.key)?.get(score.id)?.hasError('min')) {\r\n <mat-error>Score cannot be negative.</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n <mat-divider></mat-divider>\r\n }\r\n </mat-expansion-panel>\r\n }\r\n </mat-accordion>\r\n\r\n</mat-dialog-content>\r\n\r\n<mat-dialog-actions align=\"end\">\r\n <button mat-button mat-dialog-close>Cancel</button>\r\n @if( !data.readonly){\r\n <button mat-flat-button color=\"primary\" \r\n [mat-dialog-close]=\"pointsForm.getRawValue()\"\r\n [disabled]=\"pointsForm.invalid\">\r\n Save Points\r\n </button>\r\n }\r\n\r\n</mat-dialog-actions>", styles: [":host{display:block;min-width:500px;max-width:700px}.bidder-name{font-weight:500}.summary-bar{margin-bottom:24px}.summary-bar h3{margin-bottom:8px;font-weight:500}.score-item{display:flex;align-items:center;justify-content:space-between;padding:8px 4px;gap:16px}.score-description{flex:1;margin-right:16px;line-height:1.4}.score-input{flex-shrink:0;width:150px}.mat-expansion-panel-body .score-item:last-child+mat-divider{display:none}mat-dialog-actions{padding:16px 24px}.mat-mdc-form-field-error{font-size:.8125em}.dialog-instructions{margin-top:-8px;margin-bottom:24px;max-width:95%;padding-left:24px;padding-right:24px}li{font-size:.75em}ol,ul{margin-top:8px}\n"] }]
712
+ }] });
713
+
714
+ class PointAssignmentComponent {
715
+ constructor() {
716
+ this.#config = inject(NGX_T_FORMS_CONFIG_TOKEN);
717
+ this.#dialog = inject(MatDialog);
718
+ this.#destroyRef = inject(DestroyRef);
719
+ this.#refreshTrigger = new BehaviorSubject(undefined);
720
+ // A stream to push updates of the review values (`this.value`)
721
+ this.#valueChanges$ = new BehaviorSubject([]);
722
+ this.#initialState = {
723
+ loading: true,
724
+ submissions: [],
725
+ columnMapping: {},
726
+ displayCols: [],
727
+ displayColumns: [],
728
+ error: undefined,
729
+ };
730
+ this.#valueSignal = signal([], ...(ngDevMode ? [{ debugName: "#valueSignal" }] : /* istanbul ignore next */ []));
731
+ this.inputConfig = input(undefined, ...(ngDevMode ? [{ debugName: "inputConfig" }] : /* istanbul ignore next */ []));
732
+ // NOTE(phase-4): Trap 6 — parent (`workflow-adjudication-reactive-input`) binds a
733
+ // `WorkflowAdjudicationValue | null` union of three incompatible shapes. Until the parent
734
+ // template is split per adjudication step type, this input must remain `any`; narrow
735
+ // inside `valueBridge`.
736
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO(phase-4): split parent template by adjudication step, then narrow this input.
737
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
738
+ this.valueBridge = effect(() => {
739
+ const raw = this.value();
740
+ const next = Array.isArray(raw) ? raw : [];
741
+ if (_isEqual(next, this.#valueSignal()))
742
+ return;
743
+ this.#valueSignal.set(next);
744
+ this.#valueChanges$.next(next);
745
+ }, ...(ngDevMode ? [{ debugName: "valueBridge" }] : /* istanbul ignore next */ []));
746
+ // NOTE(phase-4): Trap 6 — emitter forwards enriched `ISubmission[]` rows (with score-sheet
747
+ // augmentation) back to the parent which then writes through the `WorkflowAdjudicationValue`
748
+ // union. D-013: the parent (`workflow-adjudication-reactive-input`) `(pointsChange)="setValue($event)"`
749
+ // binding only typechecks under strictTemplates if `$event` is assignable to that union; the
750
+ // enriched `ISubmission[]` payload matches no single branch (missing formControlName/totalPoints/
751
+ // description), so the emitted type must stay `any[]` to preserve parity. Narrows when the parent
752
+ // template is split per adjudication step type.
753
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO(phase-4): split parent template by adjudication step, then narrow this emitter.
754
+ this.pointsChange = output();
755
+ // Stream for the initial data fetch. Only re-runs when `refreshTrigger` is fired.
756
+ this.#fetchedData$ = this.#refreshTrigger.pipe(switchMap(() => this.#config.formBuilder.getCurrentStepMicroflowSubmissions().pipe(take(1))), shareReplay(1));
757
+ this.vm$ = combineLatest([
758
+ this.#fetchedData$,
759
+ this.#valueChanges$,
760
+ ]).pipe(map(([res, reviewValues]) => {
761
+ const { columnMapping, submissions, scoreSheet } = res;
762
+ // group scoresheet by pointType
763
+ const groupedScoreSheet = (scoreSheet || []).reduce((acc, curr) => {
764
+ const pointType = curr.pointType || 'default';
765
+ if (!acc[pointType]) {
766
+ acc[pointType] = [];
767
+ }
768
+ acc[pointType].push(curr);
769
+ return acc;
770
+ }, {});
771
+ const dataModel = {
772
+ submissions: this.#enrichSubmissionsData(submissions || [], reviewValues),
773
+ loading: false,
774
+ error: undefined,
775
+ groupedScoreSheet,
776
+ scoreSheetGroups: Object.keys(groupedScoreSheet).map((key) => ({
777
+ name: key.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z][a-z])/g, '$1 $2'),
778
+ key,
779
+ })),
780
+ columnMapping,
781
+ displayCols: [
782
+ 'nameOfBidder',
783
+ 'adj_microFlow_review',
784
+ 'adjudicationPricePreferenceCalculation',
785
+ 'adj_microFlow_scoreSheetTotalPoints',
786
+ 'actions',
787
+ ],
788
+ displayColumns: [
789
+ { label: 'name', formControlName: 'nameOfBidder', type: 'string' },
790
+ { label: 'Pref Calc', formControlName: 'adjudicationPricePreferenceCalculation', type: 'number', showColor: true },
791
+ { label: 'status', formControlName: 'adj_microFlow_review', type: 'string' },
792
+ { label: 'Score', formControlName: 'adj_microFlow_scoreSheetTotalPoints', type: 'number', showColor: true },
793
+ { label: '', formControlName: 'actions', type: 'string' },
794
+ ],
795
+ };
796
+ return { ...dataModel };
797
+ }), startWith(this.#initialState), catchError(() => of({ ...this.#initialState, loading: false, error: 'Failed to load submissions.' })), shareReplay(1));
798
+ this.readonly = computed(() => this.inputConfig()?.readonly ?? false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
799
+ }
800
+ #config;
801
+ #dialog;
802
+ #destroyRef;
803
+ #refreshTrigger;
804
+ // A stream to push updates of the review values (`this.value`)
805
+ #valueChanges$;
806
+ #initialState;
807
+ #valueSignal;
808
+ // Stream for the initial data fetch. Only re-runs when `refreshTrigger` is fired.
809
+ #fetchedData$;
810
+ loadMicroSubmissions() {
811
+ this.#refreshTrigger.next();
812
+ }
813
+ viewMicroSubmission(res) {
814
+ this.#config.formBuilder['previewWorkflowDocument'](res.id);
815
+ }
816
+ #enrichSubmissionsData(submissions, reviewValues) {
817
+ const numberSets = new Map();
818
+ numberSets.set('adjudicationPricePreferenceCalculation', submissions.map((s) => Number(s['adjudicationPricePreferenceCalculation'] || 0)));
819
+ const list = submissions.length > 0 ? submissions : (reviewValues || []);
820
+ return list.map((submission) => {
821
+ const numbers = numberSets.get('adjudicationPricePreferenceCalculation') || [];
822
+ const color = numbers.length === 0
823
+ ? submission['adjudicationPricePreferenceCalculationColor']
824
+ : this.#getColorString(submission['adjudicationPricePreferenceCalculation'], numbers);
825
+ const backgroundShades = { ['adjudicationPricePreferenceCalculation']: this.#getScoreShade(color ?? '') };
826
+ const existing = this.#valueSignal()?.find((v) => v.id === submission.id);
827
+ return {
828
+ ...submission,
829
+ adj_microFlow_review: submission['adj_microFlow_review'] === 'compliant' ? '✅' : '❌',
830
+ adjudicationPricePreferenceCalculationColor: color,
831
+ backgroundShades,
832
+ ...existing,
833
+ };
834
+ });
835
+ }
836
+ #getScoreShade(rgbColor) {
837
+ if (!rgbColor)
838
+ return '';
839
+ return rgbColor.replace('rgb', 'rgba').replace(')', `, 0.1)`);
840
+ }
841
+ #getColorString(target, numbers) {
842
+ return getColorForNumber(target, numbers, true);
843
+ }
844
+ openPointAssignmentDialog(row, groupedScoreSheet, scoreSheetGroups) {
845
+ this.#dialog
846
+ .open(PointAssignmentDialogComponent, {
847
+ height: 'fit-content',
848
+ data: {
849
+ row,
850
+ groupedScoreSheet,
851
+ scoreSheetGroups,
852
+ readonly: this.inputConfig()?.readonly || false,
853
+ },
854
+ autoFocus: false,
855
+ disableClose: true,
856
+ })
857
+ .afterClosed()
858
+ .pipe(take(1), switchMap((result) => {
859
+ if (!result)
860
+ return of(null);
861
+ // Update the value with the new point assignments
862
+ const assignment = Object.values(result).reduce((acc, curr) => {
863
+ acc = { ...acc, ...curr };
864
+ return acc;
865
+ }, {});
866
+ row['adj_microFlow_assignedScore'] = assignment;
867
+ const currentList = this.#valueSignal() ?? [];
868
+ const alreadyInValue = currentList.find((v) => v.id === row.id);
869
+ let newValue;
870
+ if (!alreadyInValue) {
871
+ newValue = [
872
+ ...currentList,
873
+ {
874
+ ...row,
875
+ adj_microFlow_assignedScore: assignment,
876
+ adj_microFlow_scoreSheetTotalPoints: Object.values(assignment).reduce((acc, curr) => acc + curr, 0),
877
+ },
878
+ ];
879
+ }
880
+ else {
881
+ newValue = currentList.map((v) => {
882
+ if (v.id === row.id) {
883
+ return {
884
+ ...v,
885
+ adj_microFlow_scoreSheetTotalPoints: Object.values(assignment).reduce((acc, curr) => acc + curr, 0),
886
+ adj_microFlow_assignedScore: assignment,
887
+ };
888
+ }
889
+ return v;
890
+ });
891
+ }
892
+ return of(newValue);
893
+ }), tap((newValue) => {
894
+ if (!newValue || this.inputConfig()?.readonly)
895
+ return;
896
+ this.pointsChange.emit(newValue);
897
+ }), takeUntilDestroyed(this.#destroyRef))
898
+ .subscribe();
899
+ }
900
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PointAssignmentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
901
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: PointAssignmentComponent, isStandalone: true, selector: "lib-point-assignment", inputs: { inputConfig: { classPropertyName: "inputConfig", publicName: "inputConfig", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointsChange: "pointsChange" }, ngImport: i0, template: "@if (vm$ | async; as vm) {\r\n @if (vm.loading) {\r\n <div style=\"display: flex; justify-content: center; align-items: center; padding:24px;width: 100%;\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n } @else if (vm.error) {\r\n <div class=\"info-card error-card\">\r\n <h5 class=\"info-title\">\u274C Failed to load data</h5>\r\n <p>{{ vm.error }}</p>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\">\r\n Try Again\r\n </button>\r\n </div>\r\n } @else {\r\n @if (vm.submissions.length === 0) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u2139\uFE0F There are no submissions to display.</h5>\r\n <ul>\r\n <li>This can happen if <strong>no one has responded</strong> yet, or if <strong>none of the submission were\r\n compliant </strong>.</li>\r\n <li>If you are expecting to see submissions, try clicking the <strong>Refresh</strong> button.</li>\r\n </ul>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\" class=\"load-button\">\r\n Refresh\r\n </button>\r\n </div>\r\n } @else {\r\n @if (!value || value.length === 0) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\uD83D\uDCDD How to Review Submissions</h5>\r\n <ol>\r\n <li>\r\n <strong>View Response:</strong> Click the <strong>Preview</strong> (<mat-icon\r\n class=\"smallIcon\">preview</mat-icon>) icon to see the full submission.\r\n </li>\r\n <li>\r\n <strong>Set Status:</strong> Use the dropdown to select <strong>Compliant</strong> or\r\n <strong>Non-compliant</strong>.\r\n </li>\r\n <li>\r\n <strong>Add Note (If Non-compliant):</strong>\r\n <ul>\r\n <li>If you select <strong>Non-compliant</strong>, you <strong>must</strong> add a note.</li>\r\n <li>Click the <strong>Add Note</strong> (<mat-icon class=\"smallIcon\">add_comment</mat-icon>) icon, write your\r\n feedback, and hit <strong>Save Feedback</strong>. Your note will be visible to the applicant.</li>\r\n </ul>\r\n </li>\r\n </ol>\r\n </div>\r\n }\r\n <table mat-table [dataSource]=\"vm.submissions\">\r\n @for (col of vm.displayColumns; track col.formControlName) {\r\n <ng-container [matColumnDef]=\"col.formControlName\">\r\n <th mat-header-cell *matHeaderCellDef style=\"padding:4px\">\r\n {{ col.label }}\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" mat-ripple class=\"table-cell\" (click)=\"$event.stopPropagation()\"\r\n [class.price-preference-cell]=\"true\"\r\n [style.border-bottom-color]=\"row.adjudicationPricePreferenceCalculationColor\"\r\n [style.background]=\"row.backgroundShades?.[col.formControlName]\">\r\n @if (col.formControlName === 'actions') {\r\n <span style=\"display: flex; align-items: center;\">\r\n <button matTooltipPosition=\"before\"\r\n [matTooltip]=\"'View and review **' + row[vm.columnMapping.respondentNameKey] + '\\'s\\' Submission'\"\r\n (click)=\"viewMicroSubmission(row)\" mat-icon-button>\r\n <mat-icon>preview</mat-icon>\r\n </button>\r\n </span>\r\n } @else if (col.formControlName === 'adj_microFlow_scoreSheetTotalPoints') {\r\n <button\r\n (click)=\"openPointAssignmentDialog(row, vm?.groupedScoreSheet, vm.scoreSheetGroups)\"\r\n [id]=\"row.id\" mat-stroked-button>\r\n {{ (row[col.formControlName] | number:'0.0-0') || 'Assign Points' }}\r\n </button>\r\n } @else {\r\n @switch (col?.type) {\r\n @case ('daysAgo') {\r\n {{ row[col.formControlName] | daysAgo }}\r\n }\r\n @case ('number') {\r\n {{ (row[col.formControlName] | number:'0.0-0') || 'N/A' }}\r\n }\r\n @default {\r\n @if ($index === 0) {\r\n <span class=\"main-submission-text\">\r\n <mat-icon style=\"font-size: 1em;height:16px;width:16px\">domain</mat-icon>\r\n <strong>\r\n {{ row[col.formControlName] }}\r\n </strong>\r\n </span>\r\n <span class=\"secondary-text\">\r\n <mat-icon>fingerprint</mat-icon>\r\n {{ row[vm.columnMapping.respondentCodeKey] }}\r\n <mat-icon>more_time</mat-icon>\r\n {{ row[vm.columnMapping.responseDateKey || ''] | daysAgo }}\r\n </span>\r\n } @else {\r\n {{ row[col.formControlName] }}\r\n }\r\n }\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n <tr mat-header-row *matHeaderRowDef=\"vm.displayCols\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: vm.displayCols\"></tr>\r\n </table>\r\n }\r\n }\r\n}\r\n", styles: [".number{display:flex;justify-content:space-between;align-items:center}li{font-size:.75em}ol,ul{margin-top:8px}.number-cell{display:flex;justify-content:space-between;align-items:center;width:100%}.secondary-text{display:flex;align-items:center;white-space:pre;font-size:.75em;gap:8px;opacity:.8}.secondary-text mat-icon{font-size:.875em;width:14px;height:14px}.mat-column-adj_microFlow_review{max-width:18px;text-align:center}.mat-column-nameOfBidder{max-width:90px}.mat-column-actions{padding:0;max-width:80px}.mat-column-status{padding-left:8px;padding-right:8px;max-width:fit-content!important}.main-submission-text{display:flex;align-items:center;gap:8px;font-size:.875em}:host .notes-card{width:100%;max-width:480px;resize:both;height:fit-content}.smallIcon{font-size:.875em;width:14px;height:14px}.table-cell{padding-left:4px;padding-right:4px}.mat-column-adjudicationPricePreferenceCalculation{max-width:18px;text-align:center}.mat-column-adj_microFlow_scoreSheetTotalPoints{width:148px;text-align:center}.mat-column-actions{max-width:14px;text-align:center}.ngx-t-forms-point-assignment-menu{width:fit-content!important;max-width:400px!important}.point-assignment-menu{padding-left:28px;padding-right:28px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i7.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: DaysAgoPipe, name: "daysAgo" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
902
+ }
903
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PointAssignmentComponent, decorators: [{
904
+ type: Component,
905
+ args: [{ selector: 'lib-point-assignment', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [
906
+ AsyncPipe,
907
+ DecimalPipe,
908
+ FormsModule,
909
+ ReactiveFormsModule,
910
+ MatButtonModule,
911
+ MatIconModule,
912
+ MatProgressSpinnerModule,
913
+ MatRippleModule,
914
+ MatTableModule,
915
+ MatTooltipModule,
916
+ DaysAgoPipe,
917
+ ], template: "@if (vm$ | async; as vm) {\r\n @if (vm.loading) {\r\n <div style=\"display: flex; justify-content: center; align-items: center; padding:24px;width: 100%;\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n } @else if (vm.error) {\r\n <div class=\"info-card error-card\">\r\n <h5 class=\"info-title\">\u274C Failed to load data</h5>\r\n <p>{{ vm.error }}</p>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\">\r\n Try Again\r\n </button>\r\n </div>\r\n } @else {\r\n @if (vm.submissions.length === 0) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u2139\uFE0F There are no submissions to display.</h5>\r\n <ul>\r\n <li>This can happen if <strong>no one has responded</strong> yet, or if <strong>none of the submission were\r\n compliant </strong>.</li>\r\n <li>If you are expecting to see submissions, try clicking the <strong>Refresh</strong> button.</li>\r\n </ul>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\" class=\"load-button\">\r\n Refresh\r\n </button>\r\n </div>\r\n } @else {\r\n @if (!value || value.length === 0) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\uD83D\uDCDD How to Review Submissions</h5>\r\n <ol>\r\n <li>\r\n <strong>View Response:</strong> Click the <strong>Preview</strong> (<mat-icon\r\n class=\"smallIcon\">preview</mat-icon>) icon to see the full submission.\r\n </li>\r\n <li>\r\n <strong>Set Status:</strong> Use the dropdown to select <strong>Compliant</strong> or\r\n <strong>Non-compliant</strong>.\r\n </li>\r\n <li>\r\n <strong>Add Note (If Non-compliant):</strong>\r\n <ul>\r\n <li>If you select <strong>Non-compliant</strong>, you <strong>must</strong> add a note.</li>\r\n <li>Click the <strong>Add Note</strong> (<mat-icon class=\"smallIcon\">add_comment</mat-icon>) icon, write your\r\n feedback, and hit <strong>Save Feedback</strong>. Your note will be visible to the applicant.</li>\r\n </ul>\r\n </li>\r\n </ol>\r\n </div>\r\n }\r\n <table mat-table [dataSource]=\"vm.submissions\">\r\n @for (col of vm.displayColumns; track col.formControlName) {\r\n <ng-container [matColumnDef]=\"col.formControlName\">\r\n <th mat-header-cell *matHeaderCellDef style=\"padding:4px\">\r\n {{ col.label }}\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" mat-ripple class=\"table-cell\" (click)=\"$event.stopPropagation()\"\r\n [class.price-preference-cell]=\"true\"\r\n [style.border-bottom-color]=\"row.adjudicationPricePreferenceCalculationColor\"\r\n [style.background]=\"row.backgroundShades?.[col.formControlName]\">\r\n @if (col.formControlName === 'actions') {\r\n <span style=\"display: flex; align-items: center;\">\r\n <button matTooltipPosition=\"before\"\r\n [matTooltip]=\"'View and review **' + row[vm.columnMapping.respondentNameKey] + '\\'s\\' Submission'\"\r\n (click)=\"viewMicroSubmission(row)\" mat-icon-button>\r\n <mat-icon>preview</mat-icon>\r\n </button>\r\n </span>\r\n } @else if (col.formControlName === 'adj_microFlow_scoreSheetTotalPoints') {\r\n <button\r\n (click)=\"openPointAssignmentDialog(row, vm?.groupedScoreSheet, vm.scoreSheetGroups)\"\r\n [id]=\"row.id\" mat-stroked-button>\r\n {{ (row[col.formControlName] | number:'0.0-0') || 'Assign Points' }}\r\n </button>\r\n } @else {\r\n @switch (col?.type) {\r\n @case ('daysAgo') {\r\n {{ row[col.formControlName] | daysAgo }}\r\n }\r\n @case ('number') {\r\n {{ (row[col.formControlName] | number:'0.0-0') || 'N/A' }}\r\n }\r\n @default {\r\n @if ($index === 0) {\r\n <span class=\"main-submission-text\">\r\n <mat-icon style=\"font-size: 1em;height:16px;width:16px\">domain</mat-icon>\r\n <strong>\r\n {{ row[col.formControlName] }}\r\n </strong>\r\n </span>\r\n <span class=\"secondary-text\">\r\n <mat-icon>fingerprint</mat-icon>\r\n {{ row[vm.columnMapping.respondentCodeKey] }}\r\n <mat-icon>more_time</mat-icon>\r\n {{ row[vm.columnMapping.responseDateKey || ''] | daysAgo }}\r\n </span>\r\n } @else {\r\n {{ row[col.formControlName] }}\r\n }\r\n }\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n <tr mat-header-row *matHeaderRowDef=\"vm.displayCols\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: vm.displayCols\"></tr>\r\n </table>\r\n }\r\n }\r\n}\r\n", styles: [".number{display:flex;justify-content:space-between;align-items:center}li{font-size:.75em}ol,ul{margin-top:8px}.number-cell{display:flex;justify-content:space-between;align-items:center;width:100%}.secondary-text{display:flex;align-items:center;white-space:pre;font-size:.75em;gap:8px;opacity:.8}.secondary-text mat-icon{font-size:.875em;width:14px;height:14px}.mat-column-adj_microFlow_review{max-width:18px;text-align:center}.mat-column-nameOfBidder{max-width:90px}.mat-column-actions{padding:0;max-width:80px}.mat-column-status{padding-left:8px;padding-right:8px;max-width:fit-content!important}.main-submission-text{display:flex;align-items:center;gap:8px;font-size:.875em}:host .notes-card{width:100%;max-width:480px;resize:both;height:fit-content}.smallIcon{font-size:.875em;width:14px;height:14px}.table-cell{padding-left:4px;padding-right:4px}.mat-column-adjudicationPricePreferenceCalculation{max-width:18px;text-align:center}.mat-column-adj_microFlow_scoreSheetTotalPoints{width:148px;text-align:center}.mat-column-actions{max-width:14px;text-align:center}.ngx-t-forms-point-assignment-menu{width:fit-content!important;max-width:400px!important}.point-assignment-menu{padding-left:28px;padding-right:28px}\n"] }]
918
+ }], propDecorators: { inputConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputConfig", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], pointsChange: [{ type: i0.Output, args: ["pointsChange"] }] } });
919
+
920
+ function toSupplierValue(input) {
921
+ // Runtime guard — the parent supplies a union of array / object payloads.
922
+ return Array.isArray(input) ? input : [];
923
+ }
924
+ class SupplierSelectionComponent {
925
+ constructor() {
926
+ this.inputConfig = input(undefined, ...(ngDevMode ? [{ debugName: "inputConfig" }] : /* istanbul ignore next */ []));
927
+ // Phase-4 TODO: parent (workflow-adjudication-reactive-input) accepts broader `WorkflowAdjudicationValue` union.
928
+ // Emitting narrower `SupplierRow[]` is structurally compatible at runtime; output widened to `any` for template typecheck (Trap 6).
929
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
930
+ this.supplierSelection = output();
931
+ this.#config = inject(NGX_T_FORMS_CONFIG_TOKEN);
932
+ this.#destroyRef = inject(DestroyRef);
933
+ this.#dialog = inject(MatDialog);
934
+ this.#refreshTrigger = new BehaviorSubject(undefined);
935
+ // A stream to push updates of the review values (`this.value`)
936
+ this.#valueChanges$ = new BehaviorSubject([]);
937
+ this.#initialState = {
938
+ loading: true,
939
+ submissions: [],
940
+ columnMapping: {},
941
+ displayCols: [],
942
+ displayColumns: [],
943
+ error: undefined,
944
+ };
945
+ this.#value = [];
946
+ // NOTE(phase-4): Trap 6 — parent (`workflow-adjudication-reactive-input`) binds a
947
+ // `WorkflowAdjudicationValue | null` union of three incompatible shapes. The template
948
+ // also reads `value.length` directly. Until parent template is split per adjudication step
949
+ // the input must remain `any`; narrow inside the `valueBridge` via `toSupplierValue`.
950
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO(phase-4): split parent template by adjudication step, then narrow this input.
951
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
952
+ this.valueBridge = effect(() => {
953
+ const next = toSupplierValue(this.value());
954
+ if (_isEqual(next, this.#value))
955
+ return;
956
+ this.#value = next;
957
+ this.#valueChanges$.next(this.#value);
958
+ }, ...(ngDevMode ? [{ debugName: "valueBridge" }] : /* istanbul ignore next */ []));
959
+ this.#fetchedData$ = this.#refreshTrigger.pipe(switchMap(() => this.#config.formBuilder.getCurrentStepMicroflowSubmissions().pipe(take(1))), shareReplay(1));
960
+ this.vm$ = combineLatest([
961
+ this.#fetchedData$,
962
+ this.#valueChanges$,
963
+ ]).pipe(map(([res, reviewValues]) => {
964
+ const { columnMapping, submissions, scoreSheet, adjudicationPricePreferenceCalculationFormula, } = res;
965
+ //group scoreSheet by pointType
966
+ const groupedScoreSheet = (scoreSheet || []).reduce((acc, curr) => {
967
+ const pointType = curr.pointType || 'default';
968
+ if (!acc[pointType]) {
969
+ acc[pointType] = [];
970
+ }
971
+ acc[pointType].push(curr);
972
+ return acc;
973
+ }, {});
974
+ const totalPossibleScore = (scoreSheet || []).reduce((acc, curr) => acc + curr.totalPoints, 0);
975
+ const maxPointsAwarded = adjudicationPricePreferenceCalculationFormula ===
976
+ '80(1-((totalBidPrice-Pmin)/(Pmin)))'
977
+ ? 20
978
+ : adjudicationPricePreferenceCalculationFormula ===
979
+ '90(1-((totalBidPrice-Pmin)/(Pmin)))'
980
+ ? 10
981
+ : 100;
982
+ const dataModel = {
983
+ submissions: this.#enrichSubmissionsData(submissions || [], reviewValues, totalPossibleScore, maxPointsAwarded),
984
+ loading: false,
985
+ error: undefined,
986
+ groupedScoreSheet,
987
+ scoreSheetGroups: Object.keys(groupedScoreSheet).map((key) => ({
988
+ name: key
989
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
990
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2'),
991
+ key: key,
992
+ })),
993
+ maxPointsAwarded,
994
+ totalPossibleScore,
995
+ columnMapping: columnMapping,
996
+ displayCols: [
997
+ 'nameOfBidder',
998
+ 'adjudicationPricePreferenceCalculation',
999
+ 'adj_microFlow_pointsAwarded',
1000
+ 'adj_microFlow_pricePointCalculation',
1001
+ 'actions',
1002
+ 'adj_microFlow_supplierAppointed',
1003
+ ],
1004
+ displayColumns: [
1005
+ { label: 'name', formControlName: 'nameOfBidder', type: InputDataTypes.String },
1006
+ {
1007
+ label: `${100 - maxPointsAwarded}`,
1008
+ formControlName: 'adjudicationPricePreferenceCalculation',
1009
+ type: InputDataTypes.Number,
1010
+ },
1011
+ {
1012
+ label: `${maxPointsAwarded}`,
1013
+ formControlName: 'adj_microFlow_pointsAwarded',
1014
+ type: InputDataTypes.Number,
1015
+ },
1016
+ {
1017
+ label: 'Total',
1018
+ formControlName: 'adj_microFlow_pricePointCalculation',
1019
+ type: InputDataTypes.Number,
1020
+ },
1021
+ // `actions` is an internal cell type used by the template `@switch`; widened locally in `tableCols`.
1022
+ { label: '', formControlName: 'actions', type: 'actions' },
1023
+ {
1024
+ label: 'Appointed',
1025
+ formControlName: 'adj_microFlow_supplierAppointed',
1026
+ type: InputDataTypes.Boolean,
1027
+ showColor: true,
1028
+ },
1029
+ ],
1030
+ };
1031
+ return {
1032
+ ...dataModel,
1033
+ };
1034
+ }), startWith(this.#initialState), catchError(() => {
1035
+ return of({
1036
+ ...this.#initialState,
1037
+ loading: false,
1038
+ error: 'Failed to load submissions.',
1039
+ });
1040
+ }), shareReplay(1));
1041
+ }
1042
+ #config;
1043
+ #destroyRef;
1044
+ #dialog;
1045
+ #refreshTrigger;
1046
+ // A stream to push updates of the review values (`this.value`)
1047
+ #valueChanges$;
1048
+ #initialState;
1049
+ #value;
1050
+ #fetchedData$;
1051
+ loadMicroSubmissions() {
1052
+ this.#refreshTrigger.next();
1053
+ }
1054
+ viewMicroSubmission(res) {
1055
+ this.#config.formBuilder['previewWorkflowDocument'](res.id);
1056
+ }
1057
+ #enrichSubmissionsData(submissions, value, totalPossibleScore, maxPointsAwarded) {
1058
+ // 1. Handle edge cases first for clarity.
1059
+ // 2. Improve performance by creating a lookup map to avoid O(n^2) complexity.
1060
+ // This is much faster than using .find() inside a loop.
1061
+ const valueMap = new Map(this.#value?.map((v) => [v.id, v]) || []);
1062
+ const list = submissions.length > 0 ? submissions : (value || []);
1063
+ // 3. First pass: Enrich submissions with calculated scores.
1064
+ const enrichedSubmissions = list.map((submission) => {
1065
+ const v = valueMap.get(submission.id);
1066
+ // Use nullish coalescing (??) for cleaner fallback logic.
1067
+ const totalPoints = v?.['adj_microFlow_scoreSheetTotalPoints'] ??
1068
+ submission?.['adj_microFlow_scoreSheetTotalPoints'] ??
1069
+ 0;
1070
+ // Simplify calculation: remove unnecessary try-catch. Division by zero in JS results
1071
+ // in Infinity, not an error. The check for totalPossibleScore > 0 handles this.
1072
+ const pointsAwarded = totalPossibleScore > 0
1073
+ ? (totalPoints / totalPossibleScore) * maxPointsAwarded
1074
+ : 0;
1075
+ const pricePointCalculation = (Number(submission['adjudicationPricePreferenceCalculation'] ?? 0)) + pointsAwarded;
1076
+ return {
1077
+ ...submission,
1078
+ ...v,
1079
+ adj_microFlow_pointsAwarded: pointsAwarded,
1080
+ adj_microFlow_pricePointCalculation: pricePointCalculation,
1081
+ };
1082
+ });
1083
+ // 4. Define keys to process to avoid repetition (DRY principle).
1084
+ const keysToColor = ['adj_microFlow_pricePointCalculation'];
1085
+ // 5. Correctly build the number sets from the *newly enriched* data.
1086
+ const numberSets = new Map();
1087
+ for (const key of keysToColor) {
1088
+ const values = enrichedSubmissions.map((s) => Number(s[key] ?? 0));
1089
+ numberSets.set(key, values);
1090
+ }
1091
+ // 6. Second pass: Add colors and shades based on the complete dataset.
1092
+ return enrichedSubmissions.map((submission) => {
1093
+ const colColors = {};
1094
+ const backgroundShades = {
1095
+ ...(submission.backgroundShades ?? {}),
1096
+ };
1097
+ for (const key of keysToColor) {
1098
+ const scoreValue = Number(submission[key] ?? 0);
1099
+ const allScores = numberSets.get(key) || [];
1100
+ const color = this.#getColorString(scoreValue, allScores);
1101
+ colColors[key] = color;
1102
+ backgroundShades[key] = this.#getScoreShade(color);
1103
+ }
1104
+ backgroundShades['adjudicationPricePreferenceCalculation'] = '';
1105
+ return {
1106
+ ...submission,
1107
+ colColors,
1108
+ backgroundShades,
1109
+ };
1110
+ });
1111
+ }
1112
+ #getScoreShade(rgbColor) {
1113
+ if (!rgbColor)
1114
+ return '';
1115
+ return rgbColor.replace('rgb', 'rgba').replace(')', `, 0.1)`);
1116
+ }
1117
+ #getColorString(target, numbers) {
1118
+ return getColorForNumber(target, numbers, true);
1119
+ }
1120
+ openPointAssignmentDialog(row, groupedScoreSheet, scoreSheetGroups) {
1121
+ this.#dialog
1122
+ .open(PointAssignmentDialogComponent, {
1123
+ height: 'fit-content',
1124
+ data: {
1125
+ row,
1126
+ groupedScoreSheet,
1127
+ scoreSheetGroups,
1128
+ },
1129
+ autoFocus: false,
1130
+ disableClose: true,
1131
+ })
1132
+ .afterClosed()
1133
+ .pipe(take(1), switchMap((result) => {
1134
+ if (!result)
1135
+ return of(null);
1136
+ // Update the value with the new point assignments
1137
+ const assignment = Object.values(result).reduce((acc, curr) => {
1138
+ acc = {
1139
+ ...acc,
1140
+ ...curr,
1141
+ };
1142
+ return acc;
1143
+ }, {});
1144
+ row['adj_microFlow_assignedScore'] = assignment;
1145
+ const alreadyInValue = this.#value?.find((v) => v.id === row.id);
1146
+ let newValue;
1147
+ if (!alreadyInValue) {
1148
+ newValue = [
1149
+ ...(this.#value || []),
1150
+ {
1151
+ ...row,
1152
+ adj_microFlow_assignedScore: assignment,
1153
+ adj_microFlow_scoreSheetTotalPoints: Object.values(assignment).reduce((acc, curr) => acc + curr, 0),
1154
+ },
1155
+ ];
1156
+ }
1157
+ else {
1158
+ newValue =
1159
+ this.#value?.map((v) => {
1160
+ if (v.id === row.id) {
1161
+ return {
1162
+ ...v,
1163
+ adj_microFlow_scoreSheetTotalPoints: Object.values(assignment).reduce((acc, curr) => acc + curr, 0),
1164
+ adj_microFlow_assignedScore: assignment,
1165
+ };
1166
+ }
1167
+ return v;
1168
+ }) || [];
1169
+ }
1170
+ return of(newValue);
1171
+ }), tap((newValue) => {
1172
+ if (!newValue)
1173
+ return;
1174
+ this.supplierSelection.emit(newValue);
1175
+ }), takeUntilDestroyed(this.#destroyRef))
1176
+ .subscribe();
1177
+ }
1178
+ appointASupplier(row) {
1179
+ if (this.inputConfig()?.readonly) {
1180
+ return;
1181
+ }
1182
+ if (row['adj_microFlow_supplierAppointed']) {
1183
+ row['adj_microFlow_supplierAppointed'] = false;
1184
+ }
1185
+ else {
1186
+ row['adj_microFlow_supplierAppointed'] = true;
1187
+ }
1188
+ const alreadyInValue = this.#value?.find((v) => v.id === row.id);
1189
+ let newValue;
1190
+ if (!alreadyInValue) {
1191
+ const otherRows = this.#value?.map((v) => ({
1192
+ ...v,
1193
+ adj_microFlow_supplierAppointed: false,
1194
+ }));
1195
+ newValue = [...(otherRows || []), row];
1196
+ }
1197
+ else {
1198
+ newValue =
1199
+ this.#value?.map((v) => {
1200
+ if (v.id === row.id) {
1201
+ return {
1202
+ ...v,
1203
+ adj_microFlow_supplierAppointed: row['adj_microFlow_supplierAppointed'],
1204
+ };
1205
+ }
1206
+ return {
1207
+ ...v,
1208
+ adj_microFlow_supplierAppointed: false,
1209
+ };
1210
+ }) || [];
1211
+ }
1212
+ if (newValue) {
1213
+ this.supplierSelection.emit(newValue);
1214
+ }
1215
+ }
1216
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SupplierSelectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1217
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: SupplierSelectionComponent, isStandalone: true, selector: "lib-supplier-selection", inputs: { inputConfig: { classPropertyName: "inputConfig", publicName: "inputConfig", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { supplierSelection: "supplierSelection" }, ngImport: i0, template: "@if (vm$ | async; as vm) {\r\n @if (vm.loading) {\r\n <div style=\"display: flex; justify-content: center; align-items: center; padding:24px;width: 100%;\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n } @else if (vm.error) {\r\n <div class=\"info-card error-card\">\r\n <h5 class=\"info-title\">\u274C Failed to load data</h5>\r\n <p>{{ vm.error }}</p>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\">\r\n Try Again\r\n </button>\r\n </div>\r\n } @else {\r\n\r\n @if (vm.submissions.length === 0) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u2139\uFE0F There are no submissions to display.</h5>\r\n <ul>\r\n <li>This can happen if <strong>no one has responded</strong> yet, or if <strong>none of the submission were\r\n compliant </strong>.</li>\r\n <li>If you are expecting to see submissions, try clicking the <strong>Refresh</strong> button.</li>\r\n </ul>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\" class=\"load-button\">\r\n Refresh\r\n </button>\r\n </div>\r\n } @else {\r\n\r\n @if (!value || value.length === 0) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\uD83D\uDCDD How to Appoint</h5>\r\n <ol>\r\n <li>\r\n <strong>View Response:</strong> Click the <strong>Preview</strong> (<mat-icon\r\n class=\"smallIcon\">preview</mat-icon>) icon to see the full submission.\r\n </li>\r\n <li>\r\n <strong>Appoint:</strong> Click the <strong>Appoint</strong> button to appoint the supplier.\r\n </li>\r\n\r\n </ol>\r\n </div>\r\n }\r\n <table mat-table [dataSource]=\"vm.submissions\">\r\n @for (col of vm.displayColumns; track col.formControlName) {\r\n <ng-container [matColumnDef]=\"col.formControlName\">\r\n <th mat-header-cell *matHeaderCellDef style=\"padding:4px\">\r\n {{ col.label }}\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" mat-ripple class=\"table-cell\" (click)=\"$event.stopPropagation()\"\r\n [class.price-preference-cell]=\"true\" [style.border-bottom-color]=\"row.colColors[col.formControlName]\"\r\n [style.background]=\"row.backgroundShades?.[col.formControlName]\">\r\n @if (col.formControlName === 'actions') {\r\n <span style=\"display: flex; align-items: center;\">\r\n <button matTooltipPosition=\"before\"\r\n [matTooltip]=\"`View and review **` + row[vm.columnMapping.respondentNameKey]+ `** 's' Submission`\"\r\n (click)=\"viewMicroSubmission(row)\" mat-icon-button>\r\n <mat-icon>preview</mat-icon>\r\n </button>\r\n </span>\r\n } @else if (col.formControlName === 'adj_microFlow_supplierAppointed') {\r\n @if (row[col.formControlName] === true) {\r\n <button (click)=\"appointASupplier(row)\" mat-flat-button color=\"primary\">\r\n <mat-icon>\r\n check_circle\r\n </mat-icon>\r\n Appointed\r\n </button>\r\n } @else {\r\n <button (click)=\"appointASupplier(row)\" mat-stroked-button>\r\n Appoint\r\n </button>\r\n }\r\n } @else {\r\n @switch (col?.type) {\r\n @case ('daysAgo') {\r\n {{ row[col.formControlName] | daysAgo }}\r\n }\r\n @case ('number') {\r\n {{ (row[col.formControlName] | number:'0.0-0' ) || 'N/A' }}\r\n }\r\n @default {\r\n @if ($index === 0) {\r\n <span class=\"main-submission-text\">\r\n <mat-icon style=\"font-size: 1em;height:16px;width:16px\">domain</mat-icon>\r\n <strong>\r\n {{ row[col.formControlName] }}\r\n </strong>\r\n </span>\r\n <span class=\"secondary-text\">\r\n <mat-icon>fingerprint</mat-icon>\r\n {{ row[vm.columnMapping.respondentCodeKey] }}\r\n <mat-icon>more_time</mat-icon>\r\n {{ row[vm.columnMapping.responseDateKey ||''] | daysAgo }}\r\n </span>\r\n } @else {\r\n {{ row[col.formControlName] }}\r\n }\r\n }\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n <tr mat-header-row *matHeaderRowDef=\"vm.displayCols\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: vm.displayCols\"></tr>\r\n </table>\r\n }\r\n }\r\n}\r\n", styles: [".number{display:flex;justify-content:space-between;align-items:center}li{font-size:.75em}ol,ul{margin-top:8px}.number-cell{display:flex;justify-content:space-between;align-items:center;width:100%}.secondary-text{display:flex;align-items:center;white-space:pre;font-size:.75em;gap:8px;opacity:.8}.secondary-text mat-icon{font-size:.875em;width:14px;height:14px}.mat-column-adj_microFlow_review{max-width:18px;text-align:center}.mat-column-nameOfBidder{max-width:90px}.mat-column-actions{padding:0;max-width:80px}.mat-column-status{padding-left:8px;padding-right:8px;max-width:fit-content!important}.main-submission-text{display:flex;align-items:center;gap:8px;font-size:.875em}:host .notes-card{width:100%;max-width:480px;resize:both;height:fit-content}.smallIcon{font-size:.875em;width:14px;height:14px}.table-cell{padding-left:4px;padding-right:4px}.mat-column-adjudicationPricePreferenceCalculation,.mat-column-adj_microFlow_scoreSheetTotalPoints,.mat-column-adj_microFlow_pointsAwarded,.mat-column-adj_microFlow_pricePointCalculation{max-width:18px;text-align:center}.mat-column-adj_microFlow_supplierAppointed{max-width:50px;text-align:center}.mat-column-actions{max-width:14px;text-align:center}.ngx-t-forms-point-assignment-menu{width:fit-content!important;max-width:400px!important}.point-assignment-menu{padding-left:28px;padding-right:28px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i7.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: DaysAgoPipe, name: "daysAgo" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1218
+ }
1219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SupplierSelectionComponent, decorators: [{
1220
+ type: Component,
1221
+ args: [{ selector: 'lib-supplier-selection', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
1222
+ AsyncPipe,
1223
+ DecimalPipe,
1224
+ FormsModule,
1225
+ MatButtonModule,
1226
+ MatDialogModule,
1227
+ MatIconModule,
1228
+ MatProgressSpinnerModule,
1229
+ MatRippleModule,
1230
+ MatTableModule,
1231
+ MatTooltipModule,
1232
+ DaysAgoPipe,
1233
+ ], template: "@if (vm$ | async; as vm) {\r\n @if (vm.loading) {\r\n <div style=\"display: flex; justify-content: center; align-items: center; padding:24px;width: 100%;\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n } @else if (vm.error) {\r\n <div class=\"info-card error-card\">\r\n <h5 class=\"info-title\">\u274C Failed to load data</h5>\r\n <p>{{ vm.error }}</p>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\">\r\n Try Again\r\n </button>\r\n </div>\r\n } @else {\r\n\r\n @if (vm.submissions.length === 0) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\u2139\uFE0F There are no submissions to display.</h5>\r\n <ul>\r\n <li>This can happen if <strong>no one has responded</strong> yet, or if <strong>none of the submission were\r\n compliant </strong>.</li>\r\n <li>If you are expecting to see submissions, try clicking the <strong>Refresh</strong> button.</li>\r\n </ul>\r\n <button mat-raised-button color=\"primary\" (click)=\"loadMicroSubmissions()\" class=\"load-button\">\r\n Refresh\r\n </button>\r\n </div>\r\n } @else {\r\n\r\n @if (!value || value.length === 0) {\r\n <div class=\"info-card\">\r\n <h5 class=\"info-title\">\uD83D\uDCDD How to Appoint</h5>\r\n <ol>\r\n <li>\r\n <strong>View Response:</strong> Click the <strong>Preview</strong> (<mat-icon\r\n class=\"smallIcon\">preview</mat-icon>) icon to see the full submission.\r\n </li>\r\n <li>\r\n <strong>Appoint:</strong> Click the <strong>Appoint</strong> button to appoint the supplier.\r\n </li>\r\n\r\n </ol>\r\n </div>\r\n }\r\n <table mat-table [dataSource]=\"vm.submissions\">\r\n @for (col of vm.displayColumns; track col.formControlName) {\r\n <ng-container [matColumnDef]=\"col.formControlName\">\r\n <th mat-header-cell *matHeaderCellDef style=\"padding:4px\">\r\n {{ col.label }}\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" mat-ripple class=\"table-cell\" (click)=\"$event.stopPropagation()\"\r\n [class.price-preference-cell]=\"true\" [style.border-bottom-color]=\"row.colColors[col.formControlName]\"\r\n [style.background]=\"row.backgroundShades?.[col.formControlName]\">\r\n @if (col.formControlName === 'actions') {\r\n <span style=\"display: flex; align-items: center;\">\r\n <button matTooltipPosition=\"before\"\r\n [matTooltip]=\"`View and review **` + row[vm.columnMapping.respondentNameKey]+ `** 's' Submission`\"\r\n (click)=\"viewMicroSubmission(row)\" mat-icon-button>\r\n <mat-icon>preview</mat-icon>\r\n </button>\r\n </span>\r\n } @else if (col.formControlName === 'adj_microFlow_supplierAppointed') {\r\n @if (row[col.formControlName] === true) {\r\n <button (click)=\"appointASupplier(row)\" mat-flat-button color=\"primary\">\r\n <mat-icon>\r\n check_circle\r\n </mat-icon>\r\n Appointed\r\n </button>\r\n } @else {\r\n <button (click)=\"appointASupplier(row)\" mat-stroked-button>\r\n Appoint\r\n </button>\r\n }\r\n } @else {\r\n @switch (col?.type) {\r\n @case ('daysAgo') {\r\n {{ row[col.formControlName] | daysAgo }}\r\n }\r\n @case ('number') {\r\n {{ (row[col.formControlName] | number:'0.0-0' ) || 'N/A' }}\r\n }\r\n @default {\r\n @if ($index === 0) {\r\n <span class=\"main-submission-text\">\r\n <mat-icon style=\"font-size: 1em;height:16px;width:16px\">domain</mat-icon>\r\n <strong>\r\n {{ row[col.formControlName] }}\r\n </strong>\r\n </span>\r\n <span class=\"secondary-text\">\r\n <mat-icon>fingerprint</mat-icon>\r\n {{ row[vm.columnMapping.respondentCodeKey] }}\r\n <mat-icon>more_time</mat-icon>\r\n {{ row[vm.columnMapping.responseDateKey ||''] | daysAgo }}\r\n </span>\r\n } @else {\r\n {{ row[col.formControlName] }}\r\n }\r\n }\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n <tr mat-header-row *matHeaderRowDef=\"vm.displayCols\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: vm.displayCols\"></tr>\r\n </table>\r\n }\r\n }\r\n}\r\n", styles: [".number{display:flex;justify-content:space-between;align-items:center}li{font-size:.75em}ol,ul{margin-top:8px}.number-cell{display:flex;justify-content:space-between;align-items:center;width:100%}.secondary-text{display:flex;align-items:center;white-space:pre;font-size:.75em;gap:8px;opacity:.8}.secondary-text mat-icon{font-size:.875em;width:14px;height:14px}.mat-column-adj_microFlow_review{max-width:18px;text-align:center}.mat-column-nameOfBidder{max-width:90px}.mat-column-actions{padding:0;max-width:80px}.mat-column-status{padding-left:8px;padding-right:8px;max-width:fit-content!important}.main-submission-text{display:flex;align-items:center;gap:8px;font-size:.875em}:host .notes-card{width:100%;max-width:480px;resize:both;height:fit-content}.smallIcon{font-size:.875em;width:14px;height:14px}.table-cell{padding-left:4px;padding-right:4px}.mat-column-adjudicationPricePreferenceCalculation,.mat-column-adj_microFlow_scoreSheetTotalPoints,.mat-column-adj_microFlow_pointsAwarded,.mat-column-adj_microFlow_pricePointCalculation{max-width:18px;text-align:center}.mat-column-adj_microFlow_supplierAppointed{max-width:50px;text-align:center}.mat-column-actions{max-width:14px;text-align:center}.ngx-t-forms-point-assignment-menu{width:fit-content!important;max-width:400px!important}.point-assignment-menu{padding-left:28px;padding-right:28px}\n"] }]
1234
+ }], propDecorators: { inputConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputConfig", required: false }] }], supplierSelection: [{ type: i0.Output, args: ["supplierSelection"] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }] } });
1235
+
1236
+ const customInputConfig = {
1237
+ controlType: 'lib-workflow-adjudication-reactive-input',
1238
+ nextId: 0,
1239
+ };
1240
+ class WorkflowAdjudicationReactiveInputComponent extends BaseCustomInput {
1241
+ get empty() {
1242
+ return this.value === null || this.value === undefined || Object.keys(this.value).length === 0;
1243
+ }
1244
+ get shouldLabelFloat() {
1245
+ return this.focused || !this.empty;
1246
+ }
1247
+ constructor() {
1248
+ super(inject(NgControl, { self: true, optional: true }), inject(ElementRef), customInputConfig);
1249
+ this.inputConfig = input.required(...(ngDevMode ? [{ debugName: "inputConfig" }] : /* istanbul ignore next */ []));
1250
+ this.editorMode = input(false, ...(ngDevMode ? [{ debugName: "editorMode" }] : /* istanbul ignore next */ []));
1251
+ this.formBuilderFunctions = input(undefined, ...(ngDevMode ? [{ debugName: "formBuilderFunctions" }] : /* istanbul ignore next */ []));
1252
+ this.adjudicationSteps = AdjudicationSteps;
1253
+ }
1254
+ setValue(value) {
1255
+ this.value = value;
1256
+ this.onChange(value);
1257
+ this.onTouched();
1258
+ }
1259
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: WorkflowAdjudicationReactiveInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1260
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: WorkflowAdjudicationReactiveInputComponent, isStandalone: true, selector: "lib-workflow-adjudication-reactive-input", inputs: { inputConfig: { classPropertyName: "inputConfig", publicName: "inputConfig", isSignal: true, isRequired: true, transformFunction: null }, editorMode: { classPropertyName: "editorMode", publicName: "editorMode", isSignal: true, isRequired: false, transformFunction: null }, formBuilderFunctions: { classPropertyName: "formBuilderFunctions", publicName: "formBuilderFunctions", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.floating": "shouldLabelFloat", "id": "id" } }, providers: [{ provide: MatFormFieldControl, useExisting: WorkflowAdjudicationReactiveInputComponent }], usesInheritance: true, ngImport: i0, template: "@switch (inputConfig().adjudicationStep) {\r\n@case (adjudicationSteps.ScoreCreation) {\r\n<lib-points-creation [inputConfig]=\"inputConfig()\" [value]=\"value\" (pointsChange)=\"setValue($event)\">\r\n</lib-points-creation>\r\n}\r\n@case (adjudicationSteps.SubMissionReview) {\r\n<lib-submission-review [inputConfig]=\"inputConfig()\" [value]=\"value\"\r\n (reviewChange)=\"setValue($event)\"></lib-submission-review>\r\n}\r\n@case (adjudicationSteps.SupplierSelection) {\r\n<lib-supplier-selection [inputConfig]=\"inputConfig()\" [value]=\"value\"\r\n (supplierSelection)=\"setValue($event)\"></lib-supplier-selection>\r\n}\r\n@case (adjudicationSteps.PointAssignment) {\r\n<lib-point-assignment [inputConfig]=\"inputConfig()\" [value]=\"value\"\r\n (pointsChange)=\"setValue($event)\"></lib-point-assignment>\r\n}\r\n@default {\r\n<h6>\r\n {{ inputConfig().adjudicationStep | titlecase }} is not implemented yet.\r\n</h6>\r\n}\r\n}\r\n", styles: [".input-add{width:100%}th,td{padding:4px}mat-hint{text-align:start}.glass{background:#ffffff40!important;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.mat-column-totalPoints{width:100px!important;max-width:100px!important}.mat-column-pointType{width:180px!important;max-width:180px!important}.mat-column-actions{width:78px!important;max-width:78px!important}li{font-size:.75em}ol{margin-top:8px}\n"], dependencies: [{ kind: "component", type: PointsCreationComponent, selector: "lib-points-creation", inputs: ["inputConfig", "value"], outputs: ["pointsChange"] }, { kind: "component", type: SubmissionReviewComponent, selector: "lib-submission-review", inputs: ["inputConfig", "value"], outputs: ["reviewChange"] }, { kind: "component", type: SupplierSelectionComponent, selector: "lib-supplier-selection", inputs: ["inputConfig", "value"], outputs: ["supplierSelection"] }, { kind: "component", type: PointAssignmentComponent, selector: "lib-point-assignment", inputs: ["inputConfig", "value"], outputs: ["pointsChange"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1261
+ }
1262
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: WorkflowAdjudicationReactiveInputComponent, decorators: [{
1263
+ type: Component,
1264
+ args: [{ selector: 'lib-workflow-adjudication-reactive-input', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
1265
+ '[class.floating]': 'shouldLabelFloat',
1266
+ '[id]': 'id',
1267
+ }, imports: [
1268
+ TitleCasePipe,
1269
+ PointsCreationComponent,
1270
+ SubmissionReviewComponent,
1271
+ SupplierSelectionComponent,
1272
+ PointAssignmentComponent,
1273
+ ], providers: [{ provide: MatFormFieldControl, useExisting: WorkflowAdjudicationReactiveInputComponent }], template: "@switch (inputConfig().adjudicationStep) {\r\n@case (adjudicationSteps.ScoreCreation) {\r\n<lib-points-creation [inputConfig]=\"inputConfig()\" [value]=\"value\" (pointsChange)=\"setValue($event)\">\r\n</lib-points-creation>\r\n}\r\n@case (adjudicationSteps.SubMissionReview) {\r\n<lib-submission-review [inputConfig]=\"inputConfig()\" [value]=\"value\"\r\n (reviewChange)=\"setValue($event)\"></lib-submission-review>\r\n}\r\n@case (adjudicationSteps.SupplierSelection) {\r\n<lib-supplier-selection [inputConfig]=\"inputConfig()\" [value]=\"value\"\r\n (supplierSelection)=\"setValue($event)\"></lib-supplier-selection>\r\n}\r\n@case (adjudicationSteps.PointAssignment) {\r\n<lib-point-assignment [inputConfig]=\"inputConfig()\" [value]=\"value\"\r\n (pointsChange)=\"setValue($event)\"></lib-point-assignment>\r\n}\r\n@default {\r\n<h6>\r\n {{ inputConfig().adjudicationStep | titlecase }} is not implemented yet.\r\n</h6>\r\n}\r\n}\r\n", styles: [".input-add{width:100%}th,td{padding:4px}mat-hint{text-align:start}.glass{background:#ffffff40!important;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.mat-column-totalPoints{width:100px!important;max-width:100px!important}.mat-column-pointType{width:180px!important;max-width:180px!important}.mat-column-actions{width:78px!important;max-width:78px!important}li{font-size:.75em}ol{margin-top:8px}\n"] }]
1274
+ }], ctorParameters: () => [], propDecorators: { inputConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputConfig", required: true }] }], editorMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editorMode", required: false }] }], formBuilderFunctions: [{ type: i0.Input, args: [{ isSignal: true, alias: "formBuilderFunctions", required: false }] }] } });
1275
+
1276
+ /** Wraps a workflow-adjudication reactive control inside a `mat-form-field`. */
1277
+ class WorkflowAdjudicationComponent {
1278
+ constructor() {
1279
+ this.inputConfig = input.required(...(ngDevMode ? [{ debugName: "inputConfig" }] : /* istanbul ignore next */ []));
1280
+ this.formGroup = input.required(...(ngDevMode ? [{ debugName: "formGroup" }] : /* istanbul ignore next */ []));
1281
+ this.editorMode = input(false, ...(ngDevMode ? [{ debugName: "editorMode" }] : /* istanbul ignore next */ []));
1282
+ this.formBuilderFunctions = input(undefined, ...(ngDevMode ? [{ debugName: "formBuilderFunctions" }] : /* istanbul ignore next */ []));
1283
+ this.reload = output();
1284
+ this.errorMessage = computed(() => getInputErrorMessage(this.inputConfig(), this.formGroup()), ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
1285
+ }
1286
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: WorkflowAdjudicationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1287
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: WorkflowAdjudicationComponent, isStandalone: true, selector: "lib-workflow-adjudication", inputs: { inputConfig: { classPropertyName: "inputConfig", publicName: "inputConfig", isSignal: true, isRequired: true, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, editorMode: { classPropertyName: "editorMode", publicName: "editorMode", isSignal: true, isRequired: false, transformFunction: null }, formBuilderFunctions: { classPropertyName: "formBuilderFunctions", publicName: "formBuilderFunctions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { reload: "reload" }, ngImport: i0, template: "<form [formGroup]=\"formGroup()\">\r\n @if (inputConfig(); as inputConfig) {\r\n <mat-form-field\r\n [appearance]=\"inputConfig.appearance || 'fill'\"\r\n [floatLabel]=\"'always'\"\r\n subscriptSizing=\"dynamic\"\r\n >\r\n <mat-label>\r\n {{ inputConfig.label }}\r\n <lib-t-form-input-status [inputConfig]=\"inputConfig\"></lib-t-form-input-status>\r\n </mat-label>\r\n\r\n <lib-workflow-adjudication-reactive-input\r\n [inputConfig]=\"inputConfig\"\r\n [formControlName]=\"inputConfig.id\"\r\n [required]=\"inputConfig.required\"\r\n [formGroup]=\"formGroup()\"\r\n [editorMode]=\"editorMode()\"\r\n [formBuilderFunctions]=\"formBuilderFunctions()\"\r\n ></lib-workflow-adjudication-reactive-input>\r\n\r\n @if (inputConfig.hintLabel || inputConfig.temporaryHint) {\r\n <mat-hint class=\"inputHint\">\r\n {{ inputConfig.temporaryHint || inputConfig.hintLabel }}\r\n </mat-hint>\r\n }\r\n\r\n @if (!!errorMessage()) {\r\n <mat-error class=\"oneLineTextEllipsis\" matTooltipClass=\"errorToolTip\">{{ errorMessage() }}</mat-error>\r\n }\r\n\r\n @if (inputConfig.canReload?.canReload) {\r\n <button\r\n mat-icon-button\r\n style=\"margin-right: 4px;\"\r\n matTooltip=\"Click to refresh this field.\"\r\n class=\"input-sync-button\"\r\n (click)=\"inputConfig.canReload.emit()\"\r\n matSuffix\r\n >\r\n <mat-icon style=\"color: var(--mat-sys-primary)\">sync</mat-icon>\r\n </button>\r\n }\r\n\r\n @if (inputConfig.prefixIcon) {\r\n <mat-icon matPrefix>{{ inputConfig.prefixIcon }}</mat-icon>\r\n }\r\n\r\n @if (inputConfig.suffixIcon && inputConfig.id !== 'password') {\r\n <mat-icon matSuffix>{{ inputConfig.suffixIcon }}</mat-icon>\r\n }\r\n\r\n @if (inputConfig.prefixText) {\r\n <span matPrefix style=\"top: 0\">{{ inputConfig.prefixText }}</span>\r\n }\r\n @if (inputConfig.suffixText) {\r\n <span matSuffix style=\"padding-left: 5px\">{{ inputConfig.suffixText }}</span>\r\n }\r\n\r\n @if (inputConfig.maxLength && formGroup()) {\r\n <mat-hint align=\"end\">\r\n {{ (formGroup().controls[inputConfig.id]?.value?.length || 0) + '/' + inputConfig.maxLength }}\r\n </mat-hint>\r\n }\r\n </mat-form-field>\r\n }\r\n</form>\r\n", styles: ["mat-form-field{width:100%}mat-form-field .input-sync-button{display:none}mat-form-field:hover .input-sync-button{display:inline-block}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: TFormInputStatusComponent, selector: "lib-t-form-input-status", inputs: ["inputConfig"] }, { kind: "component", type: WorkflowAdjudicationReactiveInputComponent, selector: "lib-workflow-adjudication-reactive-input", inputs: ["inputConfig", "editorMode", "formBuilderFunctions"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1288
+ }
1289
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: WorkflowAdjudicationComponent, decorators: [{
1290
+ type: Component,
1291
+ args: [{ selector: 'lib-workflow-adjudication', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
1292
+ ReactiveFormsModule,
1293
+ MatFormFieldModule,
1294
+ MatIconModule,
1295
+ MatButtonModule,
1296
+ MatTooltipModule,
1297
+ TFormInputStatusComponent,
1298
+ WorkflowAdjudicationReactiveInputComponent,
1299
+ ], template: "<form [formGroup]=\"formGroup()\">\r\n @if (inputConfig(); as inputConfig) {\r\n <mat-form-field\r\n [appearance]=\"inputConfig.appearance || 'fill'\"\r\n [floatLabel]=\"'always'\"\r\n subscriptSizing=\"dynamic\"\r\n >\r\n <mat-label>\r\n {{ inputConfig.label }}\r\n <lib-t-form-input-status [inputConfig]=\"inputConfig\"></lib-t-form-input-status>\r\n </mat-label>\r\n\r\n <lib-workflow-adjudication-reactive-input\r\n [inputConfig]=\"inputConfig\"\r\n [formControlName]=\"inputConfig.id\"\r\n [required]=\"inputConfig.required\"\r\n [formGroup]=\"formGroup()\"\r\n [editorMode]=\"editorMode()\"\r\n [formBuilderFunctions]=\"formBuilderFunctions()\"\r\n ></lib-workflow-adjudication-reactive-input>\r\n\r\n @if (inputConfig.hintLabel || inputConfig.temporaryHint) {\r\n <mat-hint class=\"inputHint\">\r\n {{ inputConfig.temporaryHint || inputConfig.hintLabel }}\r\n </mat-hint>\r\n }\r\n\r\n @if (!!errorMessage()) {\r\n <mat-error class=\"oneLineTextEllipsis\" matTooltipClass=\"errorToolTip\">{{ errorMessage() }}</mat-error>\r\n }\r\n\r\n @if (inputConfig.canReload?.canReload) {\r\n <button\r\n mat-icon-button\r\n style=\"margin-right: 4px;\"\r\n matTooltip=\"Click to refresh this field.\"\r\n class=\"input-sync-button\"\r\n (click)=\"inputConfig.canReload.emit()\"\r\n matSuffix\r\n >\r\n <mat-icon style=\"color: var(--mat-sys-primary)\">sync</mat-icon>\r\n </button>\r\n }\r\n\r\n @if (inputConfig.prefixIcon) {\r\n <mat-icon matPrefix>{{ inputConfig.prefixIcon }}</mat-icon>\r\n }\r\n\r\n @if (inputConfig.suffixIcon && inputConfig.id !== 'password') {\r\n <mat-icon matSuffix>{{ inputConfig.suffixIcon }}</mat-icon>\r\n }\r\n\r\n @if (inputConfig.prefixText) {\r\n <span matPrefix style=\"top: 0\">{{ inputConfig.prefixText }}</span>\r\n }\r\n @if (inputConfig.suffixText) {\r\n <span matSuffix style=\"padding-left: 5px\">{{ inputConfig.suffixText }}</span>\r\n }\r\n\r\n @if (inputConfig.maxLength && formGroup()) {\r\n <mat-hint align=\"end\">\r\n {{ (formGroup().controls[inputConfig.id]?.value?.length || 0) + '/' + inputConfig.maxLength }}\r\n </mat-hint>\r\n }\r\n </mat-form-field>\r\n }\r\n</form>\r\n", styles: ["mat-form-field{width:100%}mat-form-field .input-sync-button{display:none}mat-form-field:hover .input-sync-button{display:inline-block}\n"] }]
1300
+ }], propDecorators: { inputConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputConfig", required: true }] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: true }] }], editorMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editorMode", required: false }] }], formBuilderFunctions: [{ type: i0.Input, args: [{ isSignal: true, alias: "formBuilderFunctions", required: false }] }], reload: [{ type: i0.Output, args: ["reload"] }] } });
1301
+
1302
+ export { WorkflowAdjudicationComponent };
1303
+ //# sourceMappingURL=ngx-t-forms-workflow-adjudication.component-CPvwm7f4.mjs.map