@piserve-tech/form-submission 1.3.314 → 1.3.316

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 (443) hide show
  1. package/README.md +24 -24
  2. package/ng-package.json +30 -0
  3. package/package.json +33 -46
  4. package/{assets → src/assets}/icons/close-sidebar-mark-icon.svg +3 -3
  5. package/{assets → src/assets}/icons/copy-icon.svg +5 -5
  6. package/{assets → src/assets}/icons/delete-icon.svg +7 -7
  7. package/{assets → src/assets}/icons/delete-no-outline-icon.svg +7 -7
  8. package/src/environment/environment.ts +6 -0
  9. package/src/form-fields/aggregate-function/aggregate-function.component.html +63 -0
  10. package/src/form-fields/aggregate-function/aggregate-function.component.scss +1 -0
  11. package/src/form-fields/aggregate-function/aggregate-function.component.spec.ts +21 -0
  12. package/src/form-fields/aggregate-function/aggregate-function.component.ts +148 -0
  13. package/src/form-fields/check-box-fields/check-box-fields.component.html +81 -0
  14. package/src/form-fields/check-box-fields/check-box-fields.component.scss +28 -0
  15. package/src/form-fields/check-box-fields/check-box-fields.component.spec.ts +21 -0
  16. package/src/form-fields/check-box-fields/check-box-fields.component.ts +538 -0
  17. package/src/form-fields/currency-fields/currency-fields.component.html +85 -0
  18. package/src/form-fields/currency-fields/currency-fields.component.scss +48 -0
  19. package/src/form-fields/currency-fields/currency-fields.component.spec.ts +21 -0
  20. package/src/form-fields/currency-fields/currency-fields.component.ts +589 -0
  21. package/src/form-fields/date-time-fields/date-time-fields.component.html +268 -0
  22. package/src/form-fields/date-time-fields/date-time-fields.component.scss +88 -0
  23. package/src/form-fields/date-time-fields/date-time-fields.component.spec.ts +21 -0
  24. package/src/form-fields/date-time-fields/date-time-fields.component.ts +693 -0
  25. package/src/form-fields/drop-down-fields/drop-down-fields.component.html +77 -0
  26. package/src/form-fields/drop-down-fields/drop-down-fields.component.scss +12 -0
  27. package/src/form-fields/drop-down-fields/drop-down-fields.component.spec.ts +21 -0
  28. package/src/form-fields/drop-down-fields/drop-down-fields.component.ts +698 -0
  29. package/src/form-fields/file-picker-fields/file-picker-fields.component.html +72 -0
  30. package/src/form-fields/file-picker-fields/file-picker-fields.component.scss +33 -0
  31. package/src/form-fields/file-picker-fields/file-picker-fields.component.spec.ts +21 -0
  32. package/src/form-fields/file-picker-fields/file-picker-fields.component.ts +256 -0
  33. package/src/form-fields/form-fields.module.ts +110 -0
  34. package/src/form-fields/hidden-field/hidden-field.component.scss +0 -0
  35. package/src/form-fields/hidden-field/hidden-field.component.spec.ts +21 -0
  36. package/src/form-fields/hidden-field/hidden-field.component.ts +156 -0
  37. package/src/form-fields/iframe-fields/iframe-fields.component.html +31 -0
  38. package/src/form-fields/iframe-fields/iframe-fields.component.scss +0 -0
  39. package/src/form-fields/iframe-fields/iframe-fields.component.spec.ts +21 -0
  40. package/src/form-fields/iframe-fields/iframe-fields.component.ts +225 -0
  41. package/src/form-fields/location-fields/location-fields.component.html +54 -0
  42. package/src/form-fields/location-fields/location-fields.component.scss +22 -0
  43. package/src/form-fields/location-fields/location-fields.component.spec.ts +21 -0
  44. package/src/form-fields/location-fields/location-fields.component.ts +83 -0
  45. package/src/form-fields/mail-fields/mail-fields.component.html +66 -0
  46. package/src/form-fields/mail-fields/mail-fields.component.scss +10 -0
  47. package/src/form-fields/mail-fields/mail-fields.component.spec.ts +21 -0
  48. package/src/form-fields/mail-fields/mail-fields.component.ts +127 -0
  49. package/src/form-fields/mobile-fields/mobile-fields.component.html +97 -0
  50. package/src/form-fields/mobile-fields/mobile-fields.component.scss +52 -0
  51. package/src/form-fields/mobile-fields/mobile-fields.component.spec.ts +21 -0
  52. package/src/form-fields/mobile-fields/mobile-fields.component.ts +310 -0
  53. package/src/form-fields/number-fields/number-fields.component.html +70 -0
  54. package/src/form-fields/number-fields/number-fields.component.scss +17 -0
  55. package/src/form-fields/number-fields/number-fields.component.spec.ts +21 -0
  56. package/src/form-fields/number-fields/number-fields.component.ts +256 -0
  57. package/src/form-fields/password-fields/password-fields.component.html +113 -0
  58. package/src/form-fields/password-fields/password-fields.component.scss +61 -0
  59. package/src/form-fields/password-fields/password-fields.component.spec.ts +21 -0
  60. package/src/form-fields/password-fields/password-fields.component.ts +273 -0
  61. package/src/form-fields/radio-button-fields/radio-button-fields.component.html +82 -0
  62. package/src/form-fields/radio-button-fields/radio-button-fields.component.scss +28 -0
  63. package/src/form-fields/radio-button-fields/radio-button-fields.component.spec.ts +21 -0
  64. package/src/form-fields/radio-button-fields/radio-button-fields.component.ts +391 -0
  65. package/src/form-fields/rich-text-editor-fields/rich-text-editor-fields.component.html +67 -0
  66. package/src/form-fields/rich-text-editor-fields/rich-text-editor-fields.component.scss +23 -0
  67. package/src/form-fields/rich-text-editor-fields/rich-text-editor-fields.component.spec.ts +21 -0
  68. package/src/form-fields/rich-text-editor-fields/rich-text-editor-fields.component.ts +145 -0
  69. package/src/form-fields/section-fields/section-fields.component.html +4 -0
  70. package/src/form-fields/section-fields/section-fields.component.scss +37 -0
  71. package/src/form-fields/section-fields/section-fields.component.spec.ts +21 -0
  72. package/src/form-fields/section-fields/section-fields.component.ts +25 -0
  73. package/src/form-fields/selection-matrix-fields/selection-matrix-fields.component.html +93 -0
  74. package/src/form-fields/selection-matrix-fields/selection-matrix-fields.component.scss +52 -0
  75. package/src/form-fields/selection-matrix-fields/selection-matrix-fields.component.spec.ts +21 -0
  76. package/src/form-fields/selection-matrix-fields/selection-matrix-fields.component.ts +155 -0
  77. package/src/form-fields/signature-fields/signature-fields.component.html +43 -0
  78. package/src/form-fields/signature-fields/signature-fields.component.scss +10 -0
  79. package/src/form-fields/signature-fields/signature-fields.component.spec.ts +21 -0
  80. package/src/form-fields/signature-fields/signature-fields.component.ts +25 -0
  81. package/src/form-fields/slider-fields/slider-fields.component.html +67 -0
  82. package/src/form-fields/slider-fields/slider-fields.component.scss +55 -0
  83. package/src/form-fields/slider-fields/slider-fields.component.spec.ts +21 -0
  84. package/src/form-fields/slider-fields/slider-fields.component.ts +63 -0
  85. package/src/form-fields/terms-and-condition-fields/terms-and-condition-fields.component.html +52 -0
  86. package/src/form-fields/terms-and-condition-fields/terms-and-condition-fields.component.scss +85 -0
  87. package/src/form-fields/terms-and-condition-fields/terms-and-condition-fields.component.spec.ts +21 -0
  88. package/src/form-fields/terms-and-condition-fields/terms-and-condition-fields.component.ts +62 -0
  89. package/src/form-fields/text-area-fields/text-area-fields.component.html +68 -0
  90. package/src/form-fields/text-area-fields/text-area-fields.component.scss +19 -0
  91. package/src/form-fields/text-area-fields/text-area-fields.component.spec.ts +21 -0
  92. package/src/form-fields/text-area-fields/text-area-fields.component.ts +166 -0
  93. package/src/form-fields/text-fields/text-fields.component.html +67 -0
  94. package/src/form-fields/text-fields/text-fields.component.scss +26 -0
  95. package/src/form-fields/text-fields/text-fields.component.spec.ts +21 -0
  96. package/src/form-fields/text-fields/text-fields.component.ts +248 -0
  97. package/src/form-fields/toggle-switch-fields/toggle-switch-fields.component.html +70 -0
  98. package/src/form-fields/toggle-switch-fields/toggle-switch-fields.component.scss +112 -0
  99. package/src/form-fields/toggle-switch-fields/toggle-switch-fields.component.spec.ts +21 -0
  100. package/src/form-fields/toggle-switch-fields/toggle-switch-fields.component.ts +74 -0
  101. package/src/form-fields/url-fields/url-fields.component.html +109 -0
  102. package/src/form-fields/url-fields/url-fields.component.scss +22 -0
  103. package/src/form-fields/url-fields/url-fields.component.spec.ts +21 -0
  104. package/src/form-fields/url-fields/url-fields.component.ts +123 -0
  105. package/src/form-submission/form-submission.module.ts +40 -0
  106. package/src/form-submission/header/header.component.html +6 -0
  107. package/src/form-submission/header/header.component.scss +26 -0
  108. package/src/form-submission/header/header.component.spec.ts +21 -0
  109. package/src/form-submission/header/header.component.ts +12 -0
  110. package/src/form-submission/navigation-tabs/navigation-tabs.component.html +134 -0
  111. package/src/form-submission/navigation-tabs/navigation-tabs.component.scss +200 -0
  112. package/src/form-submission/navigation-tabs/navigation-tabs.component.spec.ts +21 -0
  113. package/src/form-submission/navigation-tabs/navigation-tabs.component.ts +134 -0
  114. package/src/form-submission/next-prev-navigation/next-prev-navigation.component.html +207 -0
  115. package/src/form-submission/next-prev-navigation/next-prev-navigation.component.scss +208 -0
  116. package/src/form-submission/next-prev-navigation/next-prev-navigation.component.spec.ts +21 -0
  117. package/src/form-submission/next-prev-navigation/next-prev-navigation.component.ts +74 -0
  118. package/src/form-submission/save-as-draft/saveDraftModalComponent .ts +60 -0
  119. package/src/form-submission/save-as-draft/saveDraftModalComponent.html +33 -0
  120. package/src/form-submission/save-as-draft/saveDraftModalComponent.scss +195 -0
  121. package/src/form-submission/submission-container/submission-container.component.html +30 -0
  122. package/src/form-submission/submission-container/submission-container.component.scss +67 -0
  123. package/src/form-submission/submission-container/submission-container.component.spec.ts +21 -0
  124. package/src/form-submission/submission-container/submission-container.component.ts +14 -0
  125. package/src/form-submission/submit-form/submit-form.component.html +76 -0
  126. package/src/form-submission/submit-form/submit-form.component.scss +88 -0
  127. package/src/form-submission/submit-form/submit-form.component.spec.ts +21 -0
  128. package/src/form-submission/submit-form/submit-form.component.ts +1953 -0
  129. package/src/lib/form-submission.component.html +15 -0
  130. package/src/lib/form-submission.component.scss +1 -0
  131. package/src/lib/form-submission.component.ts +120 -0
  132. package/src/lib/form-submission.module.ts +31 -0
  133. package/src/lib/form-submission.service.spec.ts +16 -0
  134. package/src/lib/form-submission.service.ts +9 -0
  135. package/src/models/aggregate.model.ts +4 -0
  136. package/{models/answer.model.d.ts → src/models/answer.model.ts} +25 -23
  137. package/src/models/appearance.model.ts +64 -0
  138. package/{models/attachment.model.d.ts → src/models/attachment.model.ts} +6 -6
  139. package/{models/defaultAnswerEntry.model.d.ts → src/models/defaultAnswerEntry.model.ts} +7 -5
  140. package/{models/defaultAnswersModel.model.d.ts → src/models/defaultAnswersModel.model.ts} +5 -4
  141. package/{models/defaultQuestionAnswer.model.d.ts → src/models/defaultQuestionAnswer.model.ts} +6 -6
  142. package/src/models/elementOptionAPI.model.ts +13 -0
  143. package/src/models/elementOptionAPIData.model.ts +5 -0
  144. package/src/models/elementOptionDB.model.ts +9 -0
  145. package/src/models/elementOptionDBCriteria.model.ts +7 -0
  146. package/{models/emitters.model.d.ts → src/models/emitters.model.ts} +8 -8
  147. package/{models/enum/condition.enum.d.ts → src/models/enum/condition.enum.ts} +2 -2
  148. package/src/models/enum/elementType .enum.ts +22 -0
  149. package/src/models/enum/entityType.enum.ts +10 -0
  150. package/{models/formConfiguration.model.d.ts → src/models/formConfiguration.model.ts} +2 -2
  151. package/{models/formElement.model.d.ts → src/models/formElement.model.ts} +22 -22
  152. package/{models/formElementType.model.d.ts → src/models/formElementType.model.ts} +3 -2
  153. package/{models/grid.model.d.ts → src/models/grid.model.ts} +14 -13
  154. package/{models/hiddenField.model.d.ts → src/models/hiddenField.model.ts} +5 -5
  155. package/{models/iFrameProperties.model.d.ts → src/models/iFrameProperties.model.ts} +13 -13
  156. package/src/models/logic.model.ts +11 -0
  157. package/src/models/multifields.model.ts +12 -0
  158. package/{models/option.model.d.ts → src/models/option.model.ts} +5 -5
  159. package/src/models/page.model.ts +7 -0
  160. package/src/models/publicForm.model.ts +19 -0
  161. package/src/models/question.model.ts +27 -0
  162. package/src/models/questionAnswer.model.ts +8 -0
  163. package/src/models/questionGroup.model.ts +12 -0
  164. package/src/models/response.model.ts +9 -0
  165. package/src/models/result.model.ts +5 -0
  166. package/{models/row.model.d.ts → src/models/row.model.ts} +2 -1
  167. package/src/models/scoring.model.ts +10 -0
  168. package/src/models/section.model.ts +6 -0
  169. package/src/models/subForm.model.ts +25 -0
  170. package/src/models/subformConfiguration.model.ts +4 -0
  171. package/src/models/subformPropery.model.ts +8 -0
  172. package/{models/submission.model.d.ts → src/models/submission.model.ts} +16 -14
  173. package/{models/submissionCopyConfig.mopdel.d.ts → src/models/submissionCopyConfig.mopdel.ts} +4 -4
  174. package/{models/terms&condition.model.d.ts → src/models/terms&condition.model.ts} +1 -1
  175. package/src/models/validation.model.ts +48 -0
  176. package/src/models/whenClause.model.ts +7 -0
  177. package/{models/whenClauseCondition.model.d.ts → src/models/whenClauseCondition.model.ts} +6 -6
  178. package/{models/whenClauseElements.model.d.ts → src/models/whenClauseElements.model.ts} +6 -5
  179. package/{public-api.d.ts → src/public-api.ts} +4 -0
  180. package/src/question/card-subform/card-subform.component.html +282 -0
  181. package/src/question/card-subform/card-subform.component.scss +49 -0
  182. package/src/question/card-subform/card-subform.component.spec.ts +21 -0
  183. package/src/question/card-subform/card-subform.component.ts +407 -0
  184. package/src/question/confirm-dialog/confirm-dialog.component.html +32 -0
  185. package/src/question/confirm-dialog/confirm-dialog.component.scss +109 -0
  186. package/src/question/confirm-dialog/confirm-dialog.component.spec.ts +21 -0
  187. package/src/question/confirm-dialog/confirm-dialog.component.ts +24 -0
  188. package/src/question/form-elements/form-elements.component.html +28 -0
  189. package/src/question/form-elements/form-elements.component.scss +0 -0
  190. package/src/question/form-elements/form-elements.component.spec.ts +21 -0
  191. package/src/question/form-elements/form-elements.component.ts +41 -0
  192. package/src/question/inline-multiple-subform/inline-multiple-subform.component.html +321 -0
  193. package/src/question/inline-multiple-subform/inline-multiple-subform.component.scss +78 -0
  194. package/src/question/inline-multiple-subform/inline-multiple-subform.component.spec.ts +21 -0
  195. package/src/question/inline-multiple-subform/inline-multiple-subform.component.ts +425 -0
  196. package/src/question/multifields/multifields.component.html +78 -0
  197. package/src/question/multifields/multifields.component.scss +6 -0
  198. package/src/question/multifields/multifields.component.spec.ts +21 -0
  199. package/src/question/multifields/multifields.component.ts +27 -0
  200. package/src/question/multiple-subform/multiple-subform.component.html +185 -0
  201. package/src/question/multiple-subform/multiple-subform.component.scss +52 -0
  202. package/src/question/multiple-subform/multiple-subform.component.spec.ts +21 -0
  203. package/src/question/multiple-subform/multiple-subform.component.ts +481 -0
  204. package/src/question/question/question.component.html +51 -0
  205. package/src/question/question/question.component.scss +0 -0
  206. package/src/question/question/question.component.spec.ts +21 -0
  207. package/src/question/question/question.component.ts +129 -0
  208. package/src/question/question-group/question-group.component.html +147 -0
  209. package/src/question/question-group/question-group.component.scss +36 -0
  210. package/src/question/question-group/question-group.component.spec.ts +21 -0
  211. package/src/question/question-group/question-group.component.ts +94 -0
  212. package/src/question/question.module.ts +22 -0
  213. package/src/question/recursive-logic/recursive-logic.component.html +33 -0
  214. package/src/question/recursive-logic/recursive-logic.component.scss +0 -0
  215. package/src/question/recursive-logic/recursive-logic.component.spec.ts +21 -0
  216. package/src/question/recursive-logic/recursive-logic.component.ts +29 -0
  217. package/src/question/sub-form/sub-form.component.html +254 -0
  218. package/src/question/sub-form/sub-form.component.scss +83 -0
  219. package/src/question/sub-form/sub-form.component.spec.ts +21 -0
  220. package/src/question/sub-form/sub-form.component.ts +421 -0
  221. package/src/services/aggregate.service.ts +148 -0
  222. package/src/services/countryService.ts +92 -0
  223. package/src/services/data.service.ts +36 -0
  224. package/src/services/element-tracker.service.spec.ts +16 -0
  225. package/src/services/element-tracker.service.ts +24 -0
  226. package/src/services/form-validation.service.ts +151 -0
  227. package/src/services/form.service.spec.ts +16 -0
  228. package/src/services/form.service.ts +407 -0
  229. package/src/services/mapper.service.ts +625 -0
  230. package/src/services/recaptcha.service.spec.ts +16 -0
  231. package/src/services/recaptcha.service.ts +179 -0
  232. package/src/services/score.service.ts +352 -0
  233. package/src/services/subform-copy.service.ts +148 -0
  234. package/src/services/validation.service.spec.ts +16 -0
  235. package/src/services/validation.service.ts +33 -0
  236. package/src/services/whenClause.service.ts +502 -0
  237. package/src/shared/shared.module.ts +58 -0
  238. package/src/styles/shared-style.scss +86 -0
  239. package/src/sub-form/sub-form.module.ts +24 -0
  240. package/src/sub-form/submission-modal/submission-modal.component.html +160 -0
  241. package/src/sub-form/submission-modal/submission-modal.component.scss +54 -0
  242. package/src/sub-form/submission-modal/submission-modal.component.spec.ts +21 -0
  243. package/src/sub-form/submission-modal/submission-modal.component.ts +127 -0
  244. package/tsconfig.lib.json +14 -0
  245. package/tsconfig.lib.prod.json +10 -0
  246. package/tsconfig.spec.json +14 -0
  247. package/environment/environment.d.ts +0 -5
  248. package/esm2022/environment/environment.mjs +0 -6
  249. package/esm2022/form-fields/aggregate-function/aggregate-function.component.mjs +0 -146
  250. package/esm2022/form-fields/check-box-fields/check-box-fields.component.mjs +0 -473
  251. package/esm2022/form-fields/currency-fields/currency-fields.component.mjs +0 -502
  252. package/esm2022/form-fields/date-time-fields/date-time-fields.component.mjs +0 -608
  253. package/esm2022/form-fields/drop-down-fields/drop-down-fields.component.mjs +0 -585
  254. package/esm2022/form-fields/file-picker-fields/file-picker-fields.component.mjs +0 -224
  255. package/esm2022/form-fields/form-fields.module.mjs +0 -174
  256. package/esm2022/form-fields/hidden-field/hidden-field.component.mjs +0 -137
  257. package/esm2022/form-fields/iframe-fields/iframe-fields.component.mjs +0 -198
  258. package/esm2022/form-fields/location-fields/location-fields.component.mjs +0 -85
  259. package/esm2022/form-fields/mail-fields/mail-fields.component.mjs +0 -131
  260. package/esm2022/form-fields/mobile-fields/mobile-fields.component.mjs +0 -270
  261. package/esm2022/form-fields/number-fields/number-fields.component.mjs +0 -242
  262. package/esm2022/form-fields/password-fields/password-fields.component.mjs +0 -257
  263. package/esm2022/form-fields/radio-button-fields/radio-button-fields.component.mjs +0 -346
  264. package/esm2022/form-fields/rich-text-editor-fields/rich-text-editor-fields.component.mjs +0 -148
  265. package/esm2022/form-fields/section-fields/section-fields.component.mjs +0 -29
  266. package/esm2022/form-fields/selection-matrix-fields/selection-matrix-fields.component.mjs +0 -158
  267. package/esm2022/form-fields/signature-fields/signature-fields.component.mjs +0 -32
  268. package/esm2022/form-fields/slider-fields/slider-fields.component.mjs +0 -73
  269. package/esm2022/form-fields/terms-and-condition-fields/terms-and-condition-fields.component.mjs +0 -66
  270. package/esm2022/form-fields/text-area-fields/text-area-fields.component.mjs +0 -159
  271. package/esm2022/form-fields/text-fields/text-fields.component.mjs +0 -245
  272. package/esm2022/form-fields/toggle-switch-fields/toggle-switch-fields.component.mjs +0 -85
  273. package/esm2022/form-fields/url-fields/url-fields.component.mjs +0 -124
  274. package/esm2022/form-submission/form-submission.module.mjs +0 -64
  275. package/esm2022/form-submission/header/header.component.mjs +0 -20
  276. package/esm2022/form-submission/navigation-tabs/navigation-tabs.component.mjs +0 -171
  277. package/esm2022/form-submission/next-prev-navigation/next-prev-navigation.component.mjs +0 -124
  278. package/esm2022/form-submission/save-as-draft/saveDraftModalComponent .mjs +0 -50
  279. package/esm2022/form-submission/submission-container/submission-container.component.mjs +0 -17
  280. package/esm2022/form-submission/submit-form/submit-form.component.mjs +0 -1610
  281. package/esm2022/lib/form-submission.component.mjs +0 -113
  282. package/esm2022/lib/form-submission.module.mjs +0 -56
  283. package/esm2022/lib/form-submission.service.mjs +0 -14
  284. package/esm2022/models/aggregate.model.mjs +0 -2
  285. package/esm2022/models/answer.model.mjs +0 -2
  286. package/esm2022/models/appearance.model.mjs +0 -2
  287. package/esm2022/models/attachment.model.mjs +0 -2
  288. package/esm2022/models/defaultAnswerEntry.model.mjs +0 -2
  289. package/esm2022/models/defaultAnswersModel.model.mjs +0 -2
  290. package/esm2022/models/defaultQuestionAnswer.model.mjs +0 -2
  291. package/esm2022/models/elementOptionAPI.model.mjs +0 -2
  292. package/esm2022/models/elementOptionAPIData.model.mjs +0 -2
  293. package/esm2022/models/elementOptionDB.model.mjs +0 -2
  294. package/esm2022/models/elementOptionDBCriteria.model.mjs +0 -2
  295. package/esm2022/models/emitters.model.mjs +0 -2
  296. package/esm2022/models/enum/condition.enum.mjs +0 -18
  297. package/esm2022/models/enum/elementType .enum.mjs +0 -24
  298. package/esm2022/models/enum/entityType.enum.mjs +0 -12
  299. package/esm2022/models/formConfiguration.model.mjs +0 -2
  300. package/esm2022/models/formElement.model.mjs +0 -2
  301. package/esm2022/models/formElementType.model.mjs +0 -2
  302. package/esm2022/models/grid.model.mjs +0 -2
  303. package/esm2022/models/hiddenField.model.mjs +0 -2
  304. package/esm2022/models/iFrameProperties.model.mjs +0 -2
  305. package/esm2022/models/logic.model.mjs +0 -2
  306. package/esm2022/models/multifields.model.mjs +0 -2
  307. package/esm2022/models/option.model.mjs +0 -2
  308. package/esm2022/models/page.model.mjs +0 -2
  309. package/esm2022/models/publicForm.model.mjs +0 -2
  310. package/esm2022/models/question.model.mjs +0 -2
  311. package/esm2022/models/questionAnswer.model.mjs +0 -2
  312. package/esm2022/models/questionGroup.model.mjs +0 -2
  313. package/esm2022/models/row.model.mjs +0 -2
  314. package/esm2022/models/scoring.model.mjs +0 -2
  315. package/esm2022/models/section.model.mjs +0 -2
  316. package/esm2022/models/subForm.model.mjs +0 -2
  317. package/esm2022/models/subformConfiguration.model.mjs +0 -2
  318. package/esm2022/models/subformPropery.model.mjs +0 -2
  319. package/esm2022/models/submission.model.mjs +0 -2
  320. package/esm2022/models/submissionCopyConfig.mopdel.mjs +0 -2
  321. package/esm2022/models/terms&condition.model.mjs +0 -2
  322. package/esm2022/models/validation.model.mjs +0 -2
  323. package/esm2022/models/whenClause.model.mjs +0 -2
  324. package/esm2022/models/whenClauseCondition.model.mjs +0 -2
  325. package/esm2022/models/whenClauseElements.model.mjs +0 -2
  326. package/esm2022/piserve-tech-form-submission.mjs +0 -5
  327. package/esm2022/public-api.mjs +0 -7
  328. package/esm2022/question/card-subform/card-subform.component.mjs +0 -354
  329. package/esm2022/question/confirm-dialog/confirm-dialog.component.mjs +0 -27
  330. package/esm2022/question/form-elements/form-elements.component.mjs +0 -62
  331. package/esm2022/question/inline-multiple-subform/inline-multiple-subform.component.mjs +0 -384
  332. package/esm2022/question/multifields/multifields.component.mjs +0 -54
  333. package/esm2022/question/multiple-subform/multiple-subform.component.mjs +0 -413
  334. package/esm2022/question/question/question.component.mjs +0 -145
  335. package/esm2022/question/question-group/question-group.component.mjs +0 -102
  336. package/esm2022/question/recursive-logic/recursive-logic.component.mjs +0 -49
  337. package/esm2022/question/sub-form/sub-form.component.mjs +0 -351
  338. package/esm2022/services/aggregate.service.mjs +0 -139
  339. package/esm2022/services/countryService.mjs +0 -89
  340. package/esm2022/services/data.service.mjs +0 -36
  341. package/esm2022/services/element-tracker.service.mjs +0 -27
  342. package/esm2022/services/form-validation.service.mjs +0 -128
  343. package/esm2022/services/form.service.mjs +0 -333
  344. package/esm2022/services/mapper.service.mjs +0 -519
  345. package/esm2022/services/recaptcha.service.mjs +0 -136
  346. package/esm2022/services/score.service.mjs +0 -268
  347. package/esm2022/services/subform-copy.service.mjs +0 -122
  348. package/esm2022/services/validation.service.mjs +0 -35
  349. package/esm2022/services/whenClause.service.mjs +0 -423
  350. package/esm2022/shared/shared.module.mjs +0 -97
  351. package/esm2022/sub-form/submission-modal/submission-modal.component.mjs +0 -133
  352. package/fesm2022/piserve-tech-form-submission.mjs +0 -11658
  353. package/fesm2022/piserve-tech-form-submission.mjs.map +0 -1
  354. package/form-fields/aggregate-function/aggregate-function.component.d.ts +0 -43
  355. package/form-fields/check-box-fields/check-box-fields.component.d.ts +0 -73
  356. package/form-fields/currency-fields/currency-fields.component.d.ts +0 -62
  357. package/form-fields/date-time-fields/date-time-fields.component.d.ts +0 -96
  358. package/form-fields/drop-down-fields/drop-down-fields.component.d.ts +0 -88
  359. package/form-fields/file-picker-fields/file-picker-fields.component.d.ts +0 -72
  360. package/form-fields/form-fields.module.d.ts +0 -40
  361. package/form-fields/hidden-field/hidden-field.component.d.ts +0 -38
  362. package/form-fields/iframe-fields/iframe-fields.component.d.ts +0 -35
  363. package/form-fields/location-fields/location-fields.component.d.ts +0 -29
  364. package/form-fields/mail-fields/mail-fields.component.d.ts +0 -41
  365. package/form-fields/mobile-fields/mobile-fields.component.d.ts +0 -71
  366. package/form-fields/number-fields/number-fields.component.d.ts +0 -53
  367. package/form-fields/password-fields/password-fields.component.d.ts +0 -57
  368. package/form-fields/radio-button-fields/radio-button-fields.component.d.ts +0 -70
  369. package/form-fields/rich-text-editor-fields/rich-text-editor-fields.component.d.ts +0 -46
  370. package/form-fields/section-fields/section-fields.component.d.ts +0 -10
  371. package/form-fields/selection-matrix-fields/selection-matrix-fields.component.d.ts +0 -45
  372. package/form-fields/signature-fields/signature-fields.component.d.ts +0 -13
  373. package/form-fields/slider-fields/slider-fields.component.d.ts +0 -32
  374. package/form-fields/terms-and-condition-fields/terms-and-condition-fields.component.d.ts +0 -26
  375. package/form-fields/text-area-fields/text-area-fields.component.d.ts +0 -49
  376. package/form-fields/text-fields/text-fields.component.d.ts +0 -49
  377. package/form-fields/toggle-switch-fields/toggle-switch-fields.component.d.ts +0 -29
  378. package/form-fields/url-fields/url-fields.component.d.ts +0 -41
  379. package/form-submission/form-submission.module.d.ts +0 -20
  380. package/form-submission/header/header.component.d.ts +0 -7
  381. package/form-submission/navigation-tabs/navigation-tabs.component.d.ts +0 -57
  382. package/form-submission/next-prev-navigation/next-prev-navigation.component.d.ts +0 -46
  383. package/form-submission/save-as-draft/saveDraftModalComponent .d.ts +0 -17
  384. package/form-submission/submission-container/submission-container.component.d.ts +0 -7
  385. package/form-submission/submit-form/submit-form.component.d.ts +0 -237
  386. package/index.d.ts +0 -5
  387. package/lib/form-submission.component.d.ts +0 -30
  388. package/lib/form-submission.module.d.ts +0 -16
  389. package/lib/form-submission.service.d.ts +0 -6
  390. package/models/aggregate.model.d.ts +0 -4
  391. package/models/appearance.model.d.ts +0 -64
  392. package/models/elementOptionAPI.model.d.ts +0 -12
  393. package/models/elementOptionAPIData.model.d.ts +0 -5
  394. package/models/elementOptionDB.model.d.ts +0 -8
  395. package/models/elementOptionDBCriteria.model.d.ts +0 -7
  396. package/models/enum/elementType .enum.d.ts +0 -22
  397. package/models/enum/entityType.enum.d.ts +0 -10
  398. package/models/logic.model.d.ts +0 -9
  399. package/models/multifields.model.d.ts +0 -11
  400. package/models/page.model.d.ts +0 -6
  401. package/models/publicForm.model.d.ts +0 -18
  402. package/models/question.model.d.ts +0 -26
  403. package/models/questionAnswer.model.d.ts +0 -7
  404. package/models/questionGroup.model.d.ts +0 -11
  405. package/models/scoring.model.d.ts +0 -10
  406. package/models/section.model.d.ts +0 -6
  407. package/models/subForm.model.d.ts +0 -24
  408. package/models/subformConfiguration.model.d.ts +0 -4
  409. package/models/subformPropery.model.d.ts +0 -7
  410. package/models/validation.model.d.ts +0 -48
  411. package/models/whenClause.model.d.ts +0 -6
  412. package/question/card-subform/card-subform.component.d.ts +0 -44
  413. package/question/confirm-dialog/confirm-dialog.component.d.ts +0 -12
  414. package/question/form-elements/form-elements.component.d.ts +0 -20
  415. package/question/inline-multiple-subform/inline-multiple-subform.component.d.ts +0 -47
  416. package/question/multifields/multifields.component.d.ts +0 -11
  417. package/question/multiple-subform/multiple-subform.component.d.ts +0 -39
  418. package/question/question/question.component.d.ts +0 -30
  419. package/question/question-group/question-group.component.d.ts +0 -29
  420. package/question/recursive-logic/recursive-logic.component.d.ts +0 -19
  421. package/question/sub-form/sub-form.component.d.ts +0 -70
  422. package/services/aggregate.service.d.ts +0 -29
  423. package/services/countryService.d.ts +0 -21
  424. package/services/data.service.d.ts +0 -19
  425. package/services/element-tracker.service.d.ts +0 -10
  426. package/services/form-validation.service.d.ts +0 -16
  427. package/services/form.service.d.ts +0 -27
  428. package/services/mapper.service.d.ts +0 -31
  429. package/services/recaptcha.service.d.ts +0 -46
  430. package/services/score.service.d.ts +0 -27
  431. package/services/subform-copy.service.d.ts +0 -10
  432. package/services/validation.service.d.ts +0 -14
  433. package/services/whenClause.service.d.ts +0 -45
  434. package/shared/shared.module.d.ts +0 -25
  435. package/sub-form/submission-modal/submission-modal.component.d.ts +0 -31
  436. /package/{assets → src/assets}/fonts/Garamond.woff2 +0 -0
  437. /package/{assets → src/assets}/fonts/Georgia.woff2 +0 -0
  438. /package/{assets → src/assets}/fonts/Poppins.woff2 +0 -0
  439. /package/{assets → src/assets}/fonts/Tahoma.woff2 +0 -0
  440. /package/{assets → src/assets}/fonts/Tajawal.woff2 +0 -0
  441. /package/{assets → src/assets}/fonts/Verdana.woff2 +0 -0
  442. /package/{assets → src/assets}/icons/left_arrow.svg +0 -0
  443. /package/{assets → src/assets}/icons/right_arrow.svg +0 -0
@@ -0,0 +1,1953 @@
1
+ import {
2
+ ChangeDetectorRef,
3
+ Component,
4
+ EventEmitter,
5
+ Input,
6
+ OnDestroy,
7
+ OnInit,
8
+ Output,
9
+ SimpleChanges,
10
+ ViewChild,
11
+ } from '@angular/core';
12
+ import { FormService } from '../../services/form.service';
13
+ import * as mapper from '../../services/mapper.service';
14
+ import { PublicForm } from '../../models/publicForm.model';
15
+ import { ToastrService } from 'ngx-toastr';
16
+ import { Router } from '@angular/router';
17
+ import { DataService } from '../../services/data.service';
18
+ import { RecaptchaService } from '../../services/recaptcha.service';
19
+ import { ValidationService } from '../../services/validation.service';
20
+ import { ElementTrackerService } from '../../services/element-tracker.service';
21
+ import { NavigationTabsComponent } from '../navigation-tabs/navigation-tabs.component';
22
+ import { FormValidationService } from '../../services/form-validation.service';
23
+ import { AggregationFunctionService } from '../../services/aggregate.service';
24
+ import { EmitterDTO } from '../../models/emitters.model';
25
+ import { ScoreCalculationService } from '../../services/score.service';
26
+ import { MatDialog } from '@angular/material/dialog';
27
+ import { SaveDraftModalComponent } from '../save-as-draft/saveDraftModalComponent ';
28
+ import { WhenClauseService } from '../../services/whenClause.service';
29
+ import { HttpEventType } from '@angular/common/http';
30
+
31
+ interface Answer {
32
+ questionId: string;
33
+ answer: any;
34
+ answerId?: string;
35
+ deleteFiles?: string[];
36
+ fields?: any;
37
+ }
38
+
39
+ interface Submission {
40
+ submissionId?: string;
41
+ answers: Answer[];
42
+ }
43
+
44
+ interface Form {
45
+ formId: string;
46
+ deleted?: boolean;
47
+ deletedSubmissions: any[];
48
+ submissions: Submission[];
49
+ copiedForms: string[];
50
+ scores?:{
51
+ scoreType: string;
52
+ score: number;
53
+ }[];
54
+ maximumPossibleScore?:number;
55
+ show?:boolean;
56
+ }
57
+
58
+ interface CopyForms{
59
+ id: string,
60
+ title: string,
61
+ description: string,
62
+ parentSubform: string,
63
+ questionRelation: Record<string, string>;
64
+ }
65
+
66
+ interface WhenClauseElements{
67
+ elementId:string;
68
+ entityType:string;
69
+ show:boolean;
70
+ }
71
+
72
+ interface QuestionAnswersWrapper {
73
+ parentId: string;
74
+ answers: Answer[];
75
+ forms: Form[];
76
+ copySubForms:CopyForms[];
77
+ scores?:{
78
+ scoreType: string;
79
+ score: number;
80
+ }[];
81
+ maximumPossibleScore?:number;
82
+ whenClauseElements: WhenClauseElements[]
83
+ }
84
+
85
+ interface SubmissionScore {
86
+ scoreType: string;
87
+ score: number;
88
+ }
89
+
90
+ @Component({
91
+ selector: 'lib-submit-form',
92
+ templateUrl: './submit-form.component.html',
93
+ styleUrls: ['./submit-form.component.scss'],
94
+ })
95
+ export class SubmitFormComponent implements OnInit, OnDestroy {
96
+ question: PublicForm | null = null;
97
+ @Input() moduleName: string = '';
98
+ @Input() edit: boolean = false;
99
+ @Input() submissionId: string = '';
100
+ @Input() acceptedLanguage: string = '';
101
+ @Input() skipMargin: boolean = false;
102
+ @Input() skipValidation: boolean = false;
103
+ @Input() navigateTo: string = '';
104
+ @Input() country: string = '';
105
+ @Output() submit: EventEmitter<any> = new EventEmitter<any>();
106
+ @Output() apiCalled: EventEmitter<boolean> = new EventEmitter<boolean>();
107
+ @Output() currentPageIndexChange: EventEmitter<number> =
108
+ new EventEmitter<number>();
109
+ @Input() showCancelButton: boolean = false;
110
+ @Output() cancel: EventEmitter<string> = new EventEmitter<string>();
111
+ @ViewChild(NavigationTabsComponent, { static: false })
112
+ child!: NavigationTabsComponent;
113
+ @Input() isActionTriggered: boolean = false;
114
+ isLoading: boolean = false;
115
+ name: string = '';
116
+ saveAutomatically: boolean = false;
117
+ saveInterval: number = 0;
118
+ customizeFont: boolean = false;
119
+ fontFamily: string = '';
120
+ fontSize: number = 0;
121
+ validationSkip: boolean = false;
122
+ enableDataExport: boolean = false;
123
+ fileFormats: string[] = [];
124
+ enableQuestionNumbering: boolean = false;
125
+ questionNumbering: string = '';
126
+ enableGoogleRecaptcha: boolean = false;
127
+ googleRecaptchaKey: string = '';
128
+ googleRecaptchaSecret: string = '';
129
+ navigation: string = '';
130
+ markAllQuestionsAsRequired: boolean = false;
131
+ pages: any = [];
132
+ currentPageIndex: number = 0;
133
+ previousPage: any = {};
134
+ currentPage: any = {};
135
+ nextPage: any = {};
136
+ lastTab: boolean = false;
137
+ questionAnswers: QuestionAnswersWrapper = {
138
+ parentId: '',
139
+ answers: [],
140
+ forms: [],
141
+ copySubForms: [],
142
+ whenClauseElements: []
143
+ };
144
+ webhook: any;
145
+ files: { questionId: string; file: any ; show?:boolean}[] = [];
146
+ parentQuestions: any;
147
+ submission: any = {};
148
+ parentQuestionAnswers: any;
149
+ parentSubmissionId: string = '';
150
+ submissionInProgress: boolean = false;
151
+ submissionInDraft: boolean = false;
152
+ recaptchaToken: string = '';
153
+ private isRefreshing: boolean = false;
154
+ private intervalId: ReturnType<typeof setInterval> | null = null;
155
+ pageDetails: any = [];
156
+ pageNumber: number = 1;
157
+ rowSet: number = 0;
158
+ rowSetCount: [number] = [0];
159
+ retryCount: number = 0;
160
+ maxRetries: number = 5;
161
+
162
+ eligibleForSubmission: { status: boolean; message: string } = {
163
+ status: true,
164
+ message: '',
165
+ };
166
+ webhookPayloadArray: any[] = [];
167
+ inValidQuestions: Map<string, string> = new Map<string, string>();
168
+ visitedRows: Map<number, number> = new Map<number, number>();
169
+ allElementIds: string[] = [];
170
+ private debounceTimer: any;
171
+ primaryColor: string = '#084fff';
172
+ secondaryColor: string = '';
173
+ isLoadingPages: boolean = false;
174
+ copySubforms: CopyForms[] = [];
175
+ emitters: EmitterDTO[] = [];
176
+ maxPossibleScore!: number;
177
+ maxPossibleGoodScore!: number;
178
+ maxPossibleBadScore!: number;
179
+ sumOfFindings!: number;
180
+ sumOfGoodFindings!: number;
181
+ sumOfBadFindings!: number;
182
+ draft: boolean = false;
183
+ submissionProgress = 0;
184
+ showSubmissionLoader = false;
185
+ progressInterval: any;
186
+ currentStep = 'Submitting form';
187
+ animatedDots = '';
188
+ private dotInterval: any;
189
+
190
+ constructor(
191
+ private formService: FormService,
192
+ private toastr: ToastrService,
193
+ private router: Router,
194
+ private dataService: DataService,
195
+ private recaptchaService: RecaptchaService,
196
+ private validationService: ValidationService,
197
+ private changeDetectorRef: ChangeDetectorRef,
198
+ private elementTracker: ElementTrackerService,
199
+ private formValidationService: FormValidationService,
200
+ private aggregateService: AggregationFunctionService,
201
+ private scoreCalcService: ScoreCalculationService,
202
+ public dialog: MatDialog,
203
+ private whenClauseService:WhenClauseService
204
+ ) {
205
+ const navigation = this.router.getCurrentNavigation();
206
+ this.submission = navigation?.extras.state?.['submission'];
207
+ this.parentSubmissionId = this.submission?.id;
208
+ this.parentQuestionAnswers = this.submission?.questionAnswers;
209
+ this.parentQuestions = navigation?.extras.state?.['parentQuestions'];
210
+ this.dataService.setParentQuestions(this.parentQuestions);
211
+ }
212
+ ngOnDestroy(): void {
213
+ this.visitedRows.clear();
214
+ this.recaptchaService.removeRecaptchaScript();
215
+ if (this.intervalId) {
216
+ clearInterval(this.intervalId);
217
+ this.intervalId = null;
218
+ this.isRefreshing = false;
219
+ }
220
+ this.aggregateService.clearAll();
221
+ this.recaptchaService.removeRecaptchaScript();
222
+ this.whenClauseService.resetService();
223
+ }
224
+
225
+ ngOnChanges(changes: SimpleChanges): void {
226
+ if (changes['acceptedLanguage']) {
227
+ this.currentPageIndex = 0;
228
+ this.acceptedLanguage = changes['acceptedLanguage'].currentValue;
229
+ if (!this.edit) this.getFormData();
230
+ }
231
+ if(changes['submissionId']){
232
+ this.submissionId=changes['submissionId'].currentValue;
233
+ if (this.edit && this.submissionId) {
234
+ this.getSubmittedFormData();
235
+ }
236
+ }
237
+ }
238
+
239
+ ngOnInit() {
240
+ if (this.edit && this.submissionId) {
241
+ // this.getSubmittedFormData();
242
+ } else {
243
+ // this.getFormData();
244
+ }
245
+
246
+ if (this.enableGoogleRecaptcha) {
247
+ this.recaptchaInitialized();
248
+ }
249
+ }
250
+
251
+ async recaptchaInitialized() {
252
+ await this.recaptchaService.initialize();
253
+ this.recaptchaService.render('captcha-container', (token) => {});
254
+ }
255
+
256
+ async getToken(): Promise<string> {
257
+ return new Promise(async (resolve) => {
258
+ try {
259
+ await this.recaptchaService.initialize();
260
+
261
+ // Check if CAPTCHA was rendered
262
+ if (!this.recaptchaService.isRendered()) {
263
+ console.warn('reCAPTCHA not rendered yet.');
264
+ return resolve('');
265
+ }
266
+
267
+ // If solved, return the token
268
+ if (this.recaptchaService.isResolved()) {
269
+ const token = this.recaptchaService.getResponse();
270
+
271
+ return resolve(token);
272
+ }
273
+
274
+ // If expired or unsolved, return empty string
275
+ console.warn('reCAPTCHA not resolved or expired.');
276
+ return resolve('');
277
+ } catch (err) {
278
+ console.error('reCAPTCHA initialization failed:', err);
279
+ return resolve('');
280
+ }
281
+ });
282
+ }
283
+
284
+ loadFont(fontName: string): void {
285
+ const fontPath = `assets/fonts/${fontName}.woff2`; // Adjust extension as per your font file
286
+ const style = document.createElement('style');
287
+ style.innerHTML = `
288
+ @font-face {
289
+ font-family: '${fontName}';
290
+ src: url('${fontPath}') format('woff2');
291
+ font-weight: normal;
292
+ font-style: normal;
293
+ }
294
+ body {
295
+ font-family: '${fontName}', sans-serif;
296
+ }
297
+ `;
298
+ document.head.appendChild(style);
299
+ }
300
+
301
+ getFormData() {
302
+ this.isLoadingPages = true;
303
+ this.formService
304
+ .getQuestions(this.moduleName, 1, 0, true,this.country)
305
+ .subscribe((response: any) => {
306
+ this.isLoadingPages = false;
307
+ this.enableGoogleRecaptcha =
308
+ response.result.moduleConfiguration?.enableGoogleRecaptcha;
309
+ this.question = mapper.mapPublicFormToModel(response);
310
+ this.emitters = this.question.emitters || [];
311
+ this.assignQuestionProperties(this.question);
312
+ this.setUpFontFamily(response);
313
+ this.primaryColor = response.result.primaryColor || '#084fff';
314
+ this.secondaryColor = response.result.secondaryColor;
315
+ if (this.enableGoogleRecaptcha) this.recaptchaInitialized();
316
+ if (response?.result?.pageDetails) {
317
+ this.pageDetails = response.result.pageDetails;
318
+ this.nextPage = this.pageDetails[1];
319
+ }
320
+ this.webhook = response?.result?.webhooks;
321
+ this.webhookPayloadArray = response?.result?.webhooks?.map(
322
+ (w: any) => ({
323
+ id: w.id,
324
+ ...this.extractGroupedWebhookData([w]),
325
+ })
326
+ );
327
+
328
+ this.rowSetCount[0] = response.result.totalNumberRow;
329
+ for (let i = 0; i < this.pageDetails.length; i++) {
330
+ if (!this.pages[i]) {
331
+ this.pages[i] = [];
332
+ }
333
+ }
334
+ this.checkSubmissionEligibility();
335
+ });
336
+ }
337
+
338
+ getSubmittedFormData() {
339
+ this.formService
340
+ .getSubmittedForm(
341
+ this.moduleName,
342
+ this.submissionId,
343
+ 1,
344
+ 0,
345
+ this.navigateTo
346
+ )
347
+ .subscribe((response) => {
348
+ this.enableGoogleRecaptcha =
349
+ response.result.moduleConfiguration.enableGoogleRecaptcha;
350
+ this.question = mapper.mapPublicFormToModel(response);
351
+ this.assignQuestionProperties(this.question);
352
+ this.primaryColor = response.result.primaryColor || '#084fff';
353
+ this.secondaryColor = response.result.secondaryColor;
354
+ this.emitters = this.question.emitters || [];
355
+ if (response?.result?.pageDetails) {
356
+ this.pageDetails = response.result.pageDetails;
357
+ this.nextPage = this.pageDetails[1];
358
+ }
359
+ this.webhookPayloadArray;
360
+ this.webhookPayloadArray = response?.result?.webhooks?.map(
361
+ (w: any) => ({
362
+ id: w.id,
363
+ ...this.extractGroupedWebhookData([w]),
364
+ })
365
+ );
366
+ this.rowSetCount[0] = response.result.totalNumberRow;
367
+ for (let i = 0; i < this.pageDetails.length; i++) {
368
+ if (!this.pages[i]) {
369
+ this.pages[i] = [];
370
+ }
371
+ }
372
+ this.setUpFontFamily(response);
373
+ if (this.navigateTo) {
374
+ setTimeout(() => {
375
+ this.navigateToFormElement();
376
+ });
377
+ }
378
+ if (this.enableGoogleRecaptcha) {
379
+ this.recaptchaInitialized();
380
+ }
381
+ this.checkSubmissionEligibility();
382
+ });
383
+ }
384
+
385
+ setUpFontFamily(response: any) {
386
+ //set font family and font size
387
+ if (response.result?.moduleConfiguration?.customizeFont) {
388
+ this.fontFamily = response.result.moduleConfiguration?.fontFamily;
389
+ this.fontSize = response.result.moduleConfiguration?.fontSize || 18;
390
+ } else {
391
+ this.fontFamily = 'Poppins';
392
+ this.fontSize = 18;
393
+ }
394
+ this.loadFont(this.fontFamily);
395
+ document.documentElement.style.setProperty(
396
+ '--library-font-family',
397
+ this.fontFamily
398
+ );
399
+ document.documentElement.style.setProperty(
400
+ '--library-font-size',
401
+ this.fontSize + 'px'
402
+ );
403
+ }
404
+
405
+ navigateToFormElement() {
406
+ if (!this.pages) return;
407
+
408
+ let foundPageIndex: number | null = null;
409
+
410
+ // Step 1: find the page index
411
+ this.pages.forEach((page: any, pageIndex: number) => {
412
+ if (!page?.rows) return;
413
+
414
+ page.rows.forEach((row: any) => {
415
+ if (!row?.grid) return;
416
+
417
+ row.grid.forEach((gridItem: any) => {
418
+ const element = gridItem?.element;
419
+ if (!element) return;
420
+
421
+ // Case 1: Direct question match
422
+ if (
423
+ gridItem.entityType === 'QUESTION' &&
424
+ element.id === this.navigateTo
425
+ ) {
426
+ foundPageIndex = pageIndex;
427
+ }
428
+
429
+ // Case 2: Subform match
430
+ if (gridItem.entityType === 'SUBFORM') {
431
+ if (element.id === this.navigateTo) {
432
+ foundPageIndex = pageIndex;
433
+ }
434
+ if (
435
+ element.submissions?.some(
436
+ (s: any) => s.submissionId === this.navigateTo
437
+ )
438
+ ) {
439
+ foundPageIndex = pageIndex;
440
+ }
441
+ }
442
+ });
443
+ });
444
+ });
445
+
446
+ if (foundPageIndex !== null) {
447
+ // Step 2: Set current page
448
+ this.currentPageIndex = foundPageIndex;
449
+ this.currentPage = this.pages[this.currentPageIndex];
450
+
451
+ if (this.navigation == 'TAB') {
452
+ this.child.triggerFunction(this.currentPageIndex);
453
+ } else {
454
+
455
+ this.previousPage = this.pageDetails[this.currentPageIndex - 1];
456
+ this.nextPage = this.pageDetails[this.currentPageIndex + 1];
457
+ }
458
+ // Step 3: Wait for DOM render and scroll
459
+ setTimeout(() => {
460
+ const elementId = `question-${this.navigateTo}`;
461
+ const el = document.getElementById(elementId);
462
+ if (el) {
463
+ el.scrollIntoView({ behavior: 'smooth', block: 'center' });
464
+ this.checkSubmissionEligibility();
465
+ }
466
+ }, 0);
467
+ } else {
468
+ console.warn(`navigateTo id ${this.navigateTo} not found in any page`);
469
+ }
470
+ }
471
+
472
+ assignQuestionProperties(question: PublicForm) {
473
+ this.name = question.name;
474
+ this.pages = question.pages;
475
+ if (this.parentQuestions) {
476
+ this.parentQuestionAnswers = question?.questionAnswers;
477
+ this.parentSubmissionId = question?.parentId;
478
+ }
479
+ this.currentPage = this.pages[0];
480
+ this.nextPage = this.pages[1];
481
+ this.saveAutomatically = question.formConfiguration.saveAutomatically;
482
+ this.saveInterval = question.formConfiguration.saveInterval;
483
+ this.customizeFont = question.formConfiguration.customizeFont;
484
+ this.fontFamily = question.formConfiguration.fontFamily;
485
+ this.fontSize = question.formConfiguration.fontSize;
486
+ this.enableDataExport = question.formConfiguration.enableDataExport;
487
+ this.fileFormats = question.formConfiguration.fileFormats;
488
+ this.enableQuestionNumbering =
489
+ question.formConfiguration.enableQuestionNumbering;
490
+ this.questionNumbering = question.formConfiguration.questionNumbering;
491
+ this.enableGoogleRecaptcha =
492
+ question.formConfiguration.enableGoogleRecaptcha;
493
+ this.googleRecaptchaKey = question.formConfiguration.googleRecaptchaKey;
494
+ this.googleRecaptchaSecret =
495
+ question.formConfiguration.googleRecaptchaSecret;
496
+ this.navigation = question.formConfiguration.navigation;
497
+ this.markAllQuestionsAsRequired =
498
+ question.formConfiguration.markAllQuestionsAsRequired;
499
+ this.apiCalled.emit(true);
500
+ }
501
+
502
+ navigateToNextPage() {
503
+ if (!this.isLoading) {
504
+ this.isLoading = true;
505
+ const nextIndex = this.currentPageIndex + 1;
506
+ if (nextIndex < this.pageDetails.length) {
507
+ if (this.markAllQuestionsAsRequired) {
508
+ const currentPageData = this.pages[this.currentPageIndex];
509
+ let validationResponse = true;
510
+ if (!this.validationSkip) {
511
+ validationResponse =
512
+ this.formValidationService.validateRequiredQuestions(
513
+ currentPageData
514
+ ).isValid;
515
+ }
516
+ if (validationResponse) {
517
+ this.previousPage = this.pageDetails[this.currentPageIndex];
518
+ if (this.pages[nextIndex].length != 0) {
519
+ this.currentPage = this.pages[nextIndex];
520
+ this.currentPageIndex = nextIndex;
521
+ } else {
522
+ this.setCurrentPage(nextIndex);
523
+ }
524
+ this.nextPage = this.pageDetails[nextIndex + 1];
525
+ }
526
+ } else {
527
+ this.previousPage = this.pageDetails[this.currentPageIndex];
528
+ if (this.pages[nextIndex].length != 0) {
529
+ this.currentPage = this.pages[nextIndex];
530
+ this.currentPageIndex = nextIndex;
531
+ } else {
532
+ this.setCurrentPage(nextIndex);
533
+ }
534
+ this.nextPage = this.pageDetails[nextIndex + 1];
535
+ }
536
+ }
537
+ this.isLoading = false;
538
+ this.checkSubmissionEligibility();
539
+ }
540
+ }
541
+
542
+ navigateToPreviousPage() {
543
+ if (this.currentPageIndex > 0) {
544
+ if (this.markAllQuestionsAsRequired) {
545
+ const currentPageData = this.pages[this.currentPageIndex];
546
+ let validationResponse = true;
547
+ if (!this.validationSkip) {
548
+ validationResponse =
549
+ this.formValidationService.validateRequiredQuestions(
550
+ currentPageData
551
+ ).isValid;
552
+ }
553
+ if (validationResponse) {
554
+ this.nextPage = this.pageDetails[this.currentPageIndex];
555
+ this.currentPageIndex--;
556
+ if (this.pages[this.currentPageIndex].length != 0) {
557
+ this.currentPage = this.pages[this.currentPageIndex];
558
+ } else {
559
+ this.setCurrentPage(this.currentPageIndex);
560
+ }
561
+ this.previousPage = this.pageDetails[this.currentPageIndex - 1];
562
+ }
563
+ } else {
564
+ this.nextPage = this.pageDetails[this.currentPageIndex];
565
+ this.currentPageIndex--;
566
+ if (this.pages[this.currentPageIndex].length != 0) {
567
+ this.currentPage = this.pages[this.currentPageIndex];
568
+ } else {
569
+ this.setCurrentPage(this.currentPageIndex);
570
+ }
571
+ this.previousPage = this.pageDetails[this.currentPageIndex - 1];
572
+ }
573
+ }
574
+ this.checkSubmissionEligibility();
575
+ }
576
+
577
+ checkSubmissionEligibility(): void {
578
+ this.eligibleForSubmission = this.validateAllElementLoaded();
579
+ }
580
+
581
+ setCurrentPage(index: number) {
582
+ if (index < this.pages.length) {
583
+ // If page already exists AND has content → no need to fetch again
584
+ if (
585
+ this.pages[index] &&
586
+ this.pages[index].rows &&
587
+ this.pages[index].rows.length > 0
588
+ ) {
589
+ this.checkSubmissionEligibility();
590
+ return;
591
+ }
592
+ }
593
+ this.pageNumber = index + 1;
594
+ if (this.edit) {
595
+ this.formService
596
+ .getSubmittedForm(
597
+ this.moduleName,
598
+ this.submissionId,
599
+ this.pageNumber,
600
+ this.rowSet
601
+ )
602
+ .subscribe((response: any) => {
603
+ const mappedResponse = mapper.mapPageToModel(response.result.pages[0],response.result?.currency,response.result?.defaultCurrency);
604
+ if (mappedResponse) {
605
+ this.pages[index] = mappedResponse;
606
+ }
607
+ this.currentPageIndex = index;
608
+ this.currentPage = this.pages[index];
609
+ this.rowSetCount[index] = response.result.totalNumberRow;
610
+ this.checkSubmissionEligibility();
611
+ });
612
+ } else {
613
+ this.formService
614
+ .getQuestions(this.moduleName, this.pageNumber, this.rowSet, true,this.country)
615
+ .subscribe((response: any) => {
616
+ const mappedResponse = mapper.mapPageToModel(response.result.pages[0],response.result?.currency,response.result?.defaultCurrency);
617
+ if (mappedResponse) {
618
+ this.pages[index] = mappedResponse;
619
+ }
620
+ this.currentPageIndex = index;
621
+ this.currentPage = this.pages[index];
622
+ this.rowSetCount[index] = response.result.totalNumberRow;
623
+ this.checkSubmissionEligibility();
624
+ });
625
+ }
626
+
627
+ if (this.markAllQuestionsAsRequired) {
628
+ const currentPageData = this.pages[this.currentPageIndex];
629
+ let validationResponse = true;
630
+ if (!this.validationSkip) {
631
+ validationResponse =
632
+ this.formValidationService.validateRequiredQuestions(
633
+ currentPageData
634
+ ).isValid;
635
+ }
636
+ if (validationResponse) {
637
+ this.currentPageIndex = index;
638
+ } else {
639
+ this.checkSubmissionEligibility();
640
+ return;
641
+ }
642
+ } else {
643
+ this.checkSubmissionEligibility();
644
+ this.currentPageIndex = index;
645
+ }
646
+ }
647
+
648
+ loadMoreData(index: number): void {
649
+ if (!this.isLoading) {
650
+ this.isLoading = true;
651
+ let rowCountLimit = this.rowSetCount[index];
652
+ let i = 0;
653
+ this.pages[index].rows.forEach((row: any) => {
654
+ row.grid.forEach((gridItem: any) => {
655
+ i++;
656
+ });
657
+ });
658
+ let rowCount = i / 20;
659
+ rowCount = Math.ceil(rowCount);
660
+
661
+ if (this.visitedRows.has(index)) {
662
+ if (this.visitedRows.get(index) === rowCount) {
663
+ this.isLoading = false;
664
+ return;
665
+ }
666
+ }
667
+
668
+ if (rowCount + 1 > rowCountLimit && !this.navigateTo) {
669
+ this.checkSubmissionEligibility();
670
+ this.isLoading = false;
671
+ return;
672
+ }
673
+ this.visitedRows.set(index, rowCount);
674
+
675
+ if (this.edit) {
676
+ this.formService
677
+ .getSubmittedForm(
678
+ this.moduleName,
679
+ this.submissionId,
680
+ index + 1,
681
+ rowCount
682
+ )
683
+ .subscribe((response: any) => {
684
+ const newRows = response?.result?.pages[0]?.rows ?? [];
685
+ this.pages[index].rows = [
686
+ ...(this.pages[index]?.rows ?? []),
687
+ ...newRows,
688
+ ];
689
+ this.currentPageIndex = index;
690
+ this.currentPage = this.pages[index];
691
+ this.rowSetCount[index] = response.result.totalNumberRow;
692
+ this.checkSubmissionEligibility();
693
+ this.isLoading = false;
694
+ });
695
+ } else {
696
+ this.formService
697
+ .getQuestions(this.moduleName, index + 1, rowCount)
698
+ .subscribe((response: any) => {
699
+ const newRows = response?.result?.pages[0]?.rows ?? [];
700
+ this.pages[index].rows = [
701
+ ...(this.pages[index]?.rows ?? []),
702
+ ...newRows,
703
+ ];
704
+ this.currentPageIndex = index;
705
+ this.currentPage = this.pages[index];
706
+ this.rowSetCount[index] = response.result.totalNumberRow;
707
+ this.checkSubmissionEligibility();
708
+ this.isLoading = false;
709
+ });
710
+ }
711
+
712
+ this.checkSubmissionEligibility();
713
+ }
714
+ }
715
+
716
+ generateQuestionAnswers() {
717
+ this.maxPossibleScore = 1;
718
+ this.sumOfFindings = 0;
719
+ this.sumOfBadFindings = 0;
720
+ this.sumOfGoodFindings = 0;
721
+ this.maxPossibleBadScore = 0;
722
+ this.maxPossibleGoodScore = 0;
723
+ this.copySubforms = [];
724
+
725
+ // this.maxPossibleGoodScore = 0;
726
+ // this.maxPossibleBadScore = 0;
727
+ const extractQuestions = (
728
+ formElements: any[],
729
+ parentFormId: string | null = null,
730
+ isInSubForm: boolean = false,
731
+ subFormIndex: number = 0
732
+ ): { answers: Answer[]; forms: Form[] } => {
733
+ const questions: Answer[] = [];
734
+ const forms: Form[] = [];
735
+ formElements.forEach((element) => {
736
+ if (element.entityType === 'QUESTION' && element.show) {
737
+ let baseQuestion: any;
738
+ if (element.element.formElement.elementType === 'FILE_PICKER') {
739
+ baseQuestion = {
740
+ questionId: element.element.id,
741
+ answer: '',
742
+ };
743
+ if (Array.isArray(element.element.answer)) {
744
+ const questionId = isInSubForm
745
+ ? `${parentFormId}_${subFormIndex}_${element.element.id}`
746
+ : element.element.id;
747
+ this.files.push({
748
+ questionId: questionId,
749
+ file: element.element.answer,
750
+ show: element.show
751
+ });
752
+ }
753
+ } else {
754
+ baseQuestion = {
755
+ questionId: element.element.id,
756
+ answer: element.element.answer,
757
+ score: element.element.score,
758
+ maximumPossibleScore: element.element.maxPossibleScore,
759
+ currencySymbol: element.element?.currencySymbol,
760
+ amountInWords: element.element?.amountInWords,
761
+ show: element.show
762
+ };
763
+ }
764
+ if (this.edit) {
765
+ questions.push({
766
+ ...baseQuestion,
767
+ answerId: element.element.answerId || '',
768
+ deleteFiles: element.element.deleteFiles || [],
769
+ });
770
+ } else {
771
+ questions.push(baseQuestion);
772
+ }
773
+
774
+ if (
775
+ (element.element.formElement.elementType === 'DROP_DOWN' ||
776
+ element.element.formElement.elementType === 'RADIO_BUTTON' ||
777
+ element.element.formElement.elementType === 'CHECK_BOX') &&
778
+ element.element.formElement.scoring &&
779
+ element.element.formElement.scoring.enableScoring &&
780
+ !isInSubForm
781
+ ) {
782
+ this.maxPossibleScore *=
783
+ this.scoreCalcService.getMaxPossibleScoreForQuestion(
784
+ element.element
785
+ );
786
+ if (
787
+ element.element.score &&
788
+ element.element.score != null &&
789
+ element.element.score != undefined
790
+ ) {
791
+ this.sumOfFindings += element.element.score;
792
+ }
793
+ }
794
+
795
+ if (element.element.childLogics) {
796
+ for (let logic of element.element.childLogics) {
797
+ if (logic.showLogic) {
798
+ for (let row of logic.rows) {
799
+ const result = extractQuestions(
800
+ row.grid,
801
+ parentFormId,
802
+ isInSubForm,
803
+ subFormIndex
804
+ );
805
+ questions.push(...result.answers);
806
+ forms.push(...result.forms);
807
+ }
808
+ }
809
+ }
810
+ }
811
+ } else if (element.entityType === 'SUBFORM' && element.show) {
812
+ const subformId = element.element.id;
813
+ const submissionId = element.element.submissionId;
814
+ if (element.element.copySubformMeta) {
815
+ this.copySubforms.push(element.element.copySubformMeta);
816
+ }
817
+ if (element.element.property.subFormStructure === 'single') {
818
+ let allAnswers: Answer[] = [];
819
+
820
+ element.element.rows.forEach((row: any) => {
821
+ const extracted = extractQuestions(
822
+ row.grid,
823
+ subformId,
824
+ true,
825
+ forms.length
826
+ );
827
+ extracted.answers.forEach((answer) => {
828
+ allAnswers.push({ ...answer });
829
+ });
830
+ });
831
+ let copiedForms;
832
+ if (element.element.copiedForms) {
833
+ copiedForms = element.element.copiedForms;
834
+ }
835
+ const subform: Form = {
836
+ formId: subformId,
837
+ deleted: element.element.deleted,
838
+ deletedSubmissions: [],
839
+ submissions: [
840
+ {
841
+ submissionId: submissionId,
842
+ answers: allAnswers,
843
+ },
844
+ ],
845
+ copiedForms: copiedForms,
846
+ };
847
+
848
+ forms.push(subform);
849
+ } else if (element.element.property.subFormStructure === 'multiple') {
850
+ const subformAnswers: Answer[] = [];
851
+ let subformQuestions: any = { answers: [] };
852
+ element.element.rows.forEach((row: any) => {
853
+ subformQuestions = extractQuestions(
854
+ row.grid,
855
+ subformId,
856
+ true,
857
+ forms.length
858
+ );
859
+ });
860
+ const existingSubform = forms.find(
861
+ (form) => form.formId === subformId
862
+ );
863
+ if (
864
+ (element.element.property.subFormType == 'inline' ||
865
+ element.element.property.subFormType == 'card') &&
866
+ element.element.submissions
867
+ ) {
868
+ element.element.submissions = this.clearEmptySubmissions(
869
+ element.element.submissions
870
+ );
871
+ }
872
+ element.element?.submissions?.forEach(
873
+ (submission: any, index: number) => {
874
+ for (const s of submission.scores ?? []) {
875
+ if (s.scoreType === 'GOOD_SCORE') {
876
+ this.sumOfGoodFindings += s.score;
877
+ }
878
+ if (s.scoreType === 'BAD_SCORE') {
879
+ this.sumOfBadFindings += s.score;
880
+ }
881
+ }
882
+ delete submission.hasAllScoringAnsweredOnce;
883
+ submission.answers.forEach((answer: any) => {
884
+ delete answer.htmlContent;
885
+ if (Array.isArray(answer.files)) {
886
+ const questionId = `${subformId}_${index}_${answer.questionId}`;
887
+ this.files.push({
888
+ questionId: questionId,
889
+ file: answer.files,
890
+ });
891
+ delete answer.files;
892
+ }
893
+ delete answer.attachments;
894
+ });
895
+ }
896
+ );
897
+ let copiedForms;
898
+ if (element.element.copiedForms) {
899
+ copiedForms = element.element.copiedForms;
900
+ }
901
+
902
+ const subform: Form = {
903
+ formId: subformId,
904
+ deleted: element.element.deleted,
905
+ scores: element.element.scores,
906
+ deletedSubmissions: element.element.deletedSubmissions,
907
+ submissions: element.element.submissions,
908
+ copiedForms: copiedForms,
909
+ };
910
+ forms.push(subform);
911
+ this.maxPossibleScore +=
912
+ element.element?.maxPossibleScore *
913
+ element.element?.submissions?.length;
914
+ this.maxPossibleGoodScore +=
915
+ element.element?.maxPossibleScore?.good *
916
+ element.element?.submissions?.length || 0;
917
+ this.maxPossibleBadScore +=
918
+ element.element?.maxPossibleScore?.bad *
919
+ element.element?.submissions?.length || 0;
920
+ }
921
+ } else if (element.entityType === 'QUESTION_GROUP' && element.show) {
922
+ // Handle Question Group by iterating through rows
923
+ element.element.rows.forEach((row: any) => {
924
+ row.grid.forEach((gridItem: any) => {
925
+ if (gridItem.entityType === 'QUESTION' && gridItem.show) {
926
+ let baseQuestion: any;
927
+ if (
928
+ gridItem.element?.formElement.elementType === 'FILE_PICKER'
929
+ ) {
930
+ baseQuestion = {
931
+ questionId: gridItem.element.id,
932
+ answer: '',
933
+ };
934
+ if (Array.isArray(gridItem.element.answer)) {
935
+ const questionId = isInSubForm
936
+ ? `${parentFormId}_${subFormIndex}_${gridItem.element.id}`
937
+ : gridItem.element.id;
938
+ this.files.push({
939
+ questionId: questionId,
940
+ file: gridItem.element.answer,
941
+ });
942
+ }
943
+ } else {
944
+ baseQuestion = {
945
+ questionId: gridItem.element.id,
946
+ answer: gridItem.element.answer,
947
+ amountInWords: gridItem.element.amountInWords
948
+ };
949
+ }
950
+ if (this.edit) {
951
+ questions.push({
952
+ ...baseQuestion,
953
+ answerId: gridItem.element.answerId || '',
954
+ deleteFiles: gridItem.element.deleteFiles || [],
955
+ });
956
+ } else {
957
+ questions.push(baseQuestion);
958
+ }
959
+
960
+ if (gridItem.element.childLogics) {
961
+ for (let logic of gridItem.element.childLogics) {
962
+ if (logic.showLogic) {
963
+ for (let row of logic.rows) {
964
+ const result = extractQuestions(
965
+ row.grid,
966
+ parentFormId,
967
+ isInSubForm,
968
+ subFormIndex
969
+ );
970
+ questions.push(...result.answers);
971
+ forms.push(...result.forms);
972
+ }
973
+ }
974
+ }
975
+ }
976
+ }
977
+ });
978
+ });
979
+ } else if (element.entityType === 'MULTIFIELD' && element.show) {
980
+ element.element.rows.forEach((row: any) => {
981
+ row.grid.forEach((gridItem: any) => {
982
+ if (gridItem.entityType === 'QUESTION' && gridItem.show) {
983
+ questions.push({
984
+ questionId: gridItem.element.id,
985
+ answer: gridItem.element.answer || '',
986
+ });
987
+ }
988
+ });
989
+ });
990
+ }
991
+ });
992
+ return { answers: questions, forms: forms };
993
+ };
994
+
995
+ const processPages = (
996
+ pages: any[]
997
+ ): { answers: Answer[]; forms: Form[] } => {
998
+ const questions: Answer[] = [];
999
+ const forms: Form[] = [];
1000
+
1001
+ pages.forEach((page) => {
1002
+ page.rows.forEach((row: any) => {
1003
+ const result = extractQuestions(row.grid);
1004
+ questions.push(...result.answers);
1005
+ forms.push(...result.forms);
1006
+ });
1007
+ });
1008
+
1009
+ return { answers: questions, forms: forms };
1010
+ };
1011
+ const result = processPages(this.pages);
1012
+ const grids = this.whenClauseService.getActiveWhenClauseGrids();
1013
+ const simplified = grids.map(grid => ({
1014
+ elementId: grid?.element?.id ?? null,
1015
+ entityType: grid?.entityType ?? null,
1016
+ show: grid?.show ?? false
1017
+ }));
1018
+ this.questionAnswers = {
1019
+ parentId: '',
1020
+ answers: result.answers,
1021
+ forms: result.forms,
1022
+ copySubForms: this.copySubforms,
1023
+ whenClauseElements: simplified
1024
+ };
1025
+ }
1026
+
1027
+ clearEmptySubmissions(submissions: any) {
1028
+ const nonEmptySubmissions: Submission[] = [];
1029
+ const emptySubmissionIds: string[] = [];
1030
+
1031
+ submissions.forEach((submission: any) => {
1032
+ const hasValue = submission.answers.some(
1033
+ (answer: any) =>
1034
+ answer.answer !== '' &&
1035
+ answer.answer !== null &&
1036
+ answer.answer !== undefined
1037
+ );
1038
+ if (hasValue) {
1039
+ nonEmptySubmissions.push(submission);
1040
+ } else if (submission.submissionId) {
1041
+ emptySubmissionIds.push(submission.submissionId);
1042
+ }
1043
+ });
1044
+
1045
+ submissions = nonEmptySubmissions;
1046
+ return submissions;
1047
+ }
1048
+
1049
+ validateAllElementLoaded(): { status: boolean; message: string } {
1050
+ if (this.pages.length < this.pageDetails.length) {
1051
+ return {
1052
+ status: false,
1053
+ message:
1054
+ 'Please ensure all pages are fully visible before submitting the form.',
1055
+ };
1056
+ }
1057
+
1058
+ const incompletePages: string[] = [];
1059
+
1060
+ this.pages.forEach((p: any, i: number) => {
1061
+ if (p && Array.isArray(p.rows)) {
1062
+ let j = 0;
1063
+ p.rows.forEach((row: any) => {
1064
+ row.grid.forEach((gridItem: any) => {
1065
+ j++;
1066
+ });
1067
+ });
1068
+ if (
1069
+ !p ||
1070
+ !Array.isArray(p.rows) ||
1071
+ (Math.ceil(j / 20) != this.rowSetCount[i] && !this.navigateTo)
1072
+ ) {
1073
+ incompletePages.push(this.pageDetails[i].name);
1074
+ }
1075
+ } else {
1076
+ incompletePages.push(this.pageDetails[i].name);
1077
+ }
1078
+ });
1079
+
1080
+ if (incompletePages.length > 0) {
1081
+ return {
1082
+ status: false,
1083
+ message: `Some sections are still loading. Please scroll through the following pages to ensure all questions are loaded: ${incompletePages.join(
1084
+ ', '
1085
+ )}.`,
1086
+ };
1087
+ }
1088
+
1089
+ return {
1090
+ status: true,
1091
+ message: 'Great! All sections are complete. You can now submit the form.',
1092
+ };
1093
+ }
1094
+ saveasDraftPopup(): void {
1095
+ if (!this.skipValidation) {
1096
+ return;
1097
+ }
1098
+ const dialogRef = this.dialog.open(SaveDraftModalComponent, {
1099
+ width: '450px',
1100
+ panelClass: 'custom-dialog-container',
1101
+ autoFocus: false,
1102
+ disableClose: true,
1103
+ data: {
1104
+ primaryColor: this.primaryColor,
1105
+ },
1106
+ });
1107
+
1108
+ dialogRef.afterClosed().subscribe((result) => {
1109
+ if (result) {
1110
+ this.validationSkip = true;
1111
+ this.submitForm();
1112
+ } else {
1113
+ this.validationSkip = false;
1114
+ }
1115
+ });
1116
+ }
1117
+
1118
+ saveAsDraft() {
1119
+ this.validationSkip = true;
1120
+ this.draft = true;
1121
+ this.submitForm();
1122
+ }
1123
+
1124
+ async submitForm() {
1125
+ this.startSubmissionLoader();
1126
+ if (this.webhook) {
1127
+ this.webhookPayloadArray = this.webhook.map((w: any) => ({
1128
+ id: w.id,
1129
+ ...this.extractGroupedWebhookData([w]),
1130
+ }));
1131
+ }
1132
+ if (!this.validateAllElementLoaded().status) {
1133
+ this.toastr.warning('Please wait for all elements to load');
1134
+ this.submit.emit({
1135
+ status: 'failed',
1136
+ });
1137
+ return;
1138
+ }
1139
+ if (this.submissionInProgress || this.submissionInDraft) {
1140
+ this.submit.emit({
1141
+ status: 'failed',
1142
+ });
1143
+ return;
1144
+ }
1145
+ let recaptchaToken: string | undefined;
1146
+ this.inValidQuestions.clear();
1147
+ this.validationService.clearAll();
1148
+ if (this.enableGoogleRecaptcha) {
1149
+ try {
1150
+ recaptchaToken = await this.getToken();
1151
+
1152
+ if (!recaptchaToken) {
1153
+ this.toastr.error(
1154
+ 'Please complete the CAPTCHA verification before submitting the form.'
1155
+ );
1156
+ this.submissionInProgress = false;
1157
+ this.submissionInDraft = false;
1158
+ this.resetSubmissionLoader();
1159
+ this.validationSkip = false;
1160
+ this.draft = false;
1161
+ this.submit.emit({
1162
+ status: 'failed',
1163
+ });
1164
+ return;
1165
+ }
1166
+ } catch (error: any) {
1167
+ console.error(
1168
+ 'Error while getting reCAPTCHA token:',
1169
+ error.error?.message
1170
+ );
1171
+ this.toastr.error('Failed to verify reCAPTCHA.');
1172
+ this.submissionInProgress = false;
1173
+ this.submissionInDraft = false;
1174
+ this.resetSubmissionLoader();
1175
+ this.validationSkip = false;
1176
+ this.draft = false;
1177
+ this.submit.emit({
1178
+ status: 'failed',
1179
+ });
1180
+ return;
1181
+ }
1182
+ }
1183
+
1184
+ if (this.draft || this.validationSkip) {
1185
+ this.submissionInDraft = true;
1186
+ } else {
1187
+ this.submissionInProgress = true;
1188
+ }
1189
+
1190
+ let validationResponse = true;
1191
+ if (!this.validationSkip) {
1192
+ validationResponse = this.validateForm(this.pages).isValid;
1193
+ }
1194
+
1195
+ if (!validationResponse) {
1196
+ this.saveasDraftPopup();
1197
+ this.submissionInProgress = false;
1198
+ this.submissionInDraft = false;
1199
+ this.resetSubmissionLoader();
1200
+ this.validationSkip = false;
1201
+ this.draft = false;
1202
+ this.submit.emit({
1203
+ status: 'failed',
1204
+ });
1205
+ return;
1206
+ }
1207
+ const getCircularReplacer = () => {
1208
+ const seen = new WeakSet();
1209
+ return (_key: string, value: any) => {
1210
+ if (typeof value === 'object' && value !== null) {
1211
+ if (seen.has(value)) {
1212
+ return undefined;
1213
+ }
1214
+ seen.add(value);
1215
+ }
1216
+ return value;
1217
+ };
1218
+ };
1219
+
1220
+ this.generateQuestionAnswers();
1221
+ const formFinalScore = this.calculateFormFinalScore();
1222
+ this.questionAnswers.scores = formFinalScore;
1223
+ if (validationResponse) {
1224
+ const formData = new FormData();
1225
+ formData.append('skipValidation', this.validationSkip ? 'true' : 'false');
1226
+ this.questionAnswers.parentId = this.parentSubmissionId;
1227
+ formData.append(
1228
+ 'json',
1229
+ JSON.stringify(this.questionAnswers, getCircularReplacer())
1230
+ );
1231
+ if (this.webhookPayloadArray) {
1232
+ formData.append(
1233
+ 'webhookPayload',
1234
+ JSON.stringify(this.webhookPayloadArray)
1235
+ );
1236
+ } else {
1237
+ formData.append('webhookPayload', JSON.stringify([]));
1238
+ }
1239
+ if (this.files.length > 0) {
1240
+ const seenQuestionIds = new Set<string>();
1241
+
1242
+ const uniqueFiles = this.files.filter((fileItem) => {
1243
+ if (seenQuestionIds.has(fileItem.questionId)) {
1244
+ return false;
1245
+ }
1246
+ seenQuestionIds.add(fileItem.questionId);
1247
+ return true;
1248
+ });
1249
+
1250
+ uniqueFiles.forEach((fileItem) => {
1251
+ fileItem.file.forEach((file: any) => {
1252
+ formData.append(fileItem.questionId, file._file);
1253
+ });
1254
+ });
1255
+ }
1256
+ const formServiceCall =
1257
+ this.edit && this.submissionId
1258
+ ? this.formService.updateForm(
1259
+ this.moduleName,
1260
+ this.submissionId,
1261
+ formData,
1262
+ recaptchaToken ?? ''
1263
+ )
1264
+ : this.formService.submitForm(
1265
+ this.moduleName,
1266
+ formData,
1267
+ recaptchaToken ?? ''
1268
+ );
1269
+
1270
+ formServiceCall.subscribe({
1271
+ next: (event) => {
1272
+ if (event.type === HttpEventType.UploadProgress) {
1273
+ if (event.total) {
1274
+ const percentDone = Math.round((event.loaded / event.total) * 100);
1275
+ this.setProgress(Math.min(95, percentDone));
1276
+ }
1277
+ }
1278
+
1279
+ if (event.type === HttpEventType.Response) {
1280
+ this.completeSubmissionLoader();
1281
+
1282
+ const response = event.body;
1283
+
1284
+ // existing success logic ↓
1285
+ this.toastr.success(
1286
+ this.edit
1287
+ ? 'Form updated successfully'
1288
+ : 'Form submitted successfully'
1289
+ );
1290
+ let emittingValues;
1291
+ if (this.emitters) {
1292
+ emittingValues = this.mapEmittersToValues();
1293
+ }
1294
+
1295
+ if (this.validationSkip || this.draft) {
1296
+ this.submit.emit({
1297
+ status: 'draft',
1298
+ submissionId: response.result,
1299
+ score: formFinalScore,
1300
+ ...(emittingValues || {}),
1301
+ });
1302
+ } else {
1303
+ this.submit.emit({
1304
+ status: 'success',
1305
+ submissionId: response.result,
1306
+ score: formFinalScore,
1307
+ ...(emittingValues || {}),
1308
+ });
1309
+ }
1310
+ this.submissionInProgress = false;
1311
+ this.submissionInDraft = false;
1312
+ this.draft = false;
1313
+ this.validationSkip = false;
1314
+ }
1315
+ },
1316
+ error: (error) => {
1317
+ this.validationService.clearAll();
1318
+ this.findQuestionPageIndex(
1319
+ this.parseErrorsToMap(error.error.message)
1320
+ );
1321
+ this.currentPageIndex = this.pages.length - 1;
1322
+ this.navigateToFirstError();
1323
+ this.resetSubmissionLoader();
1324
+ this.submissionInProgress = false;
1325
+ this.draft = false;
1326
+ this.validationSkip = false;
1327
+ this.submissionInDraft = false;
1328
+ this.saveasDraftPopup();
1329
+ this.submit.emit({
1330
+ status: 'failed',
1331
+ message: error.error.message
1332
+ });
1333
+ },
1334
+ });
1335
+ } else {
1336
+ // this.toastr.error(validationResponse.message);
1337
+ this.submit.emit({
1338
+ status: 'failed',
1339
+ });
1340
+ this.saveasDraftPopup();
1341
+ this.submissionInProgress = false;
1342
+ this.submissionInDraft = false;
1343
+ this.resetSubmissionLoader();
1344
+ this.validationSkip = false;
1345
+ this.draft = false;
1346
+ }
1347
+ }
1348
+
1349
+ setProgress(value: number) {
1350
+ this.submissionProgress = value;
1351
+ this.updateStepText(value);
1352
+ }
1353
+
1354
+
1355
+ startSubmissionLoader() {
1356
+ this.showSubmissionLoader = true;
1357
+ this.setProgress(0);
1358
+
1359
+ // Simulated progress until backend upload updates it
1360
+ this.progressInterval = setInterval(() => {
1361
+ if (this.submissionProgress < 85) {
1362
+ this.setProgress(this.submissionProgress + 2);
1363
+ }
1364
+ }, 300);
1365
+ }
1366
+
1367
+ completeSubmissionLoader() {
1368
+ clearInterval(this.progressInterval);
1369
+
1370
+ this.setProgress(100);
1371
+
1372
+ setTimeout(() => {
1373
+ this.showSubmissionLoader = false;
1374
+ this.setProgress(0);
1375
+ }, 500);
1376
+ }
1377
+
1378
+ resetSubmissionLoader() {
1379
+ clearInterval(this.progressInterval);
1380
+
1381
+ this.showSubmissionLoader = false;
1382
+ clearInterval(this.dotInterval);
1383
+ this.animatedDots = '';
1384
+ this.setProgress(0);
1385
+ }
1386
+
1387
+
1388
+ startTextLoader() {
1389
+ let dotCount = 0;
1390
+
1391
+ this.dotInterval = setInterval(() => {
1392
+ dotCount = (dotCount + 1) % 4;
1393
+ this.animatedDots = '.'.repeat(dotCount);
1394
+ }, 500);
1395
+ }
1396
+
1397
+ updateStepText(progress: number) {
1398
+ if (progress < 25) {
1399
+ this.currentStep = 'Validating form';
1400
+ } else if (progress < 60) {
1401
+ this.currentStep = 'Uploading data';
1402
+ } else if (progress < 90) {
1403
+ this.currentStep = 'Saving submission';
1404
+ } else {
1405
+ this.currentStep = 'Finalizing';
1406
+ }
1407
+ }
1408
+
1409
+ calculateFormFinalScore(): SubmissionScore[] {
1410
+ const goodScore = this.maxPossibleGoodScore
1411
+ ? 100 - (this.sumOfGoodFindings / this.maxPossibleGoodScore) * 100
1412
+ : 0;
1413
+
1414
+ const badScore = this.maxPossibleBadScore
1415
+ ? 100 - (this.sumOfBadFindings / this.maxPossibleBadScore) * 100
1416
+ : 0;
1417
+
1418
+ return [
1419
+ { scoreType: 'GOOD_SCORE', score: Math.round(goodScore) },
1420
+ { scoreType: 'BAD_SCORE', score: Math.round(badScore) },
1421
+ ];
1422
+ }
1423
+
1424
+ mapEmittersToValues(): { [key: string]: any } {
1425
+ const answerMap = new Map(
1426
+ this.questionAnswers.answers.map((qa) => [qa.questionId, qa.answer])
1427
+ );
1428
+
1429
+ const result: { [key: string]: any } = {};
1430
+
1431
+ this.emitters.forEach((emitter) => {
1432
+ if (emitter.valueSource === 'CUSTOM_INPUT') {
1433
+ result[emitter.keyName] = emitter.inputValue;
1434
+ } else if (
1435
+ emitter.valueSource === 'QUESTION' &&
1436
+ emitter.questionId &&
1437
+ answerMap.has(emitter.questionId)
1438
+ ) {
1439
+ result[emitter.keyName] = answerMap.get(emitter.questionId);
1440
+ }
1441
+ });
1442
+
1443
+ return result;
1444
+ }
1445
+
1446
+ parseErrorsToMap(errorString: string): Map<string, string> {
1447
+ const errorMap = new Map<string, string>();
1448
+ // Convert the incorrect format to a valid object structure
1449
+ errorString
1450
+ .replace(/[{}]/g, '') // Remove curly braces
1451
+ .split(', ') // Split key-value pairs
1452
+ .forEach((pair) => {
1453
+ const [key, value] = pair.split('=');
1454
+ if (key && value) {
1455
+ errorMap.set(key.trim(), value.trim()); // Store in Map
1456
+ }
1457
+ });
1458
+
1459
+ return errorMap;
1460
+ }
1461
+ async navigateToFirstError() {
1462
+ const firstError = this.validationService.getFirstInvalid();
1463
+ if (!firstError) return;
1464
+
1465
+ const elementId = `question-${firstError.id}`;
1466
+
1467
+ this.retryCount = 0;
1468
+
1469
+ // Change page if needed
1470
+ if (this.currentPageIndex !== firstError.pageIndex) {
1471
+ this.currentPageIndex = firstError.pageIndex;
1472
+ if (this.currentPageIndex + 1 < this.pageDetails.length) {
1473
+ this.nextPage = this.pageDetails[this.currentPageIndex + 1];
1474
+ }
1475
+ if (this.currentPageIndex - 1 >= 0) {
1476
+ this.previousPage = this.pageDetails[this.currentPageIndex - 1];
1477
+ }
1478
+
1479
+ this.debounce(() => this.changeDetectorRef.detectChanges(), 100);
1480
+ if (this.navigation === 'TAB')
1481
+ this.child.triggerFunction(firstError.pageIndex);
1482
+ }
1483
+
1484
+ await this.scrollToElementWithRetry(elementId);
1485
+ }
1486
+
1487
+ private async scrollToElementWithRetry(elementId: string): Promise<void> {
1488
+ this.retryCount++;
1489
+ const element = document.getElementById(elementId);
1490
+
1491
+ if (element) {
1492
+ requestAnimationFrame(() => this.scrollAndFocus(element));
1493
+ return;
1494
+ }
1495
+
1496
+ if (this.retryCount < this.maxRetries) {
1497
+ await new Promise((resolve) => setTimeout(resolve, 100));
1498
+ return this.scrollToElementWithRetry(elementId);
1499
+ }
1500
+
1501
+ console.error(
1502
+ `Element ${elementId} not found after ${this.maxRetries} attempts`
1503
+ );
1504
+ requestAnimationFrame(() => this.fallbackScroll());
1505
+ }
1506
+
1507
+ private scrollAndFocus(element: HTMLElement) {
1508
+ element.scrollIntoView({
1509
+ behavior: 'smooth',
1510
+ block: 'center',
1511
+ inline: 'nearest',
1512
+ });
1513
+
1514
+ const focusable = element.querySelector(
1515
+ 'input, select, textarea, button, [tabindex]:not([tabindex="-1"])'
1516
+ ) as HTMLElement;
1517
+ focusable?.focus();
1518
+ }
1519
+
1520
+ private fallbackScroll() {
1521
+ const pageElement = document.querySelector(
1522
+ `.page-${this.currentPageIndex}`
1523
+ );
1524
+ pageElement?.scrollIntoView({ behavior: 'auto' });
1525
+ }
1526
+
1527
+ private debounce(func: () => void, delay: number) {
1528
+ clearTimeout(this.debounceTimer);
1529
+ this.debounceTimer = setTimeout(func, delay);
1530
+ }
1531
+ // extractId(message: string): string | null {
1532
+ // const match = message.match(/[0-9a-fA-F-]{36}/);
1533
+ // return match ? match[0] : null;
1534
+ // }
1535
+
1536
+ // removeId(message: string): string {
1537
+ // return message.replace(/[0-9a-fA-F-]{36}$/, "").trim();
1538
+ // }
1539
+
1540
+ findQuestionPageIndex(questionErrors: Map<string, string>): void {
1541
+ for (let pageIndex = 0; pageIndex < this.pages.length; pageIndex++) {
1542
+ const page = this.pages[pageIndex];
1543
+
1544
+ for (const row of page.rows) {
1545
+ for (const element of row.grid) {
1546
+ this.checkAndStoreInvalidQuestions(
1547
+ element,
1548
+ questionErrors,
1549
+ pageIndex
1550
+ );
1551
+ }
1552
+ }
1553
+ }
1554
+ }
1555
+
1556
+ private checkAndStoreInvalidQuestions(
1557
+ element: any,
1558
+ questionErrors: Map<string, string>,
1559
+ pageIndex: number
1560
+ ): void {
1561
+ if (element.element.id && questionErrors.has(element.element.id)) {
1562
+ this.validationService.setInvalid(
1563
+ element.element.id,
1564
+ questionErrors.get(element.element.id)!,
1565
+ pageIndex
1566
+ );
1567
+ }
1568
+
1569
+ // Handle subforms recursively
1570
+ if (element.entityType === 'SUBFORM' && element.element.rows) {
1571
+ for (const row of element.element.rows) {
1572
+ for (const gridItem of row.grid) {
1573
+ this.checkAndStoreInvalidQuestions(
1574
+ gridItem,
1575
+ questionErrors,
1576
+ pageIndex
1577
+ );
1578
+ }
1579
+ }
1580
+ }
1581
+ if (
1582
+ (element.entityType === 'QUESTION_GROUP' ||
1583
+ element.entityType === 'MULTIFIELD') &&
1584
+ element.element.rows
1585
+ ) {
1586
+ for (const row of element.element.rows) {
1587
+ for (const gridItem of row.grid) {
1588
+ this.checkAndStoreInvalidQuestions(
1589
+ gridItem,
1590
+ questionErrors,
1591
+ pageIndex
1592
+ );
1593
+ }
1594
+ }
1595
+ }
1596
+
1597
+ // Handle logic-based conditions recursively
1598
+ if (element.entityType === 'QUESTION' && element.element.childLogics) {
1599
+ for (const logic of element.element.childLogics) {
1600
+ if (logic.showLogic) {
1601
+ for (const row of logic.rows) {
1602
+ for (const gridItem of row.grid) {
1603
+ this.checkAndStoreInvalidQuestions(
1604
+ gridItem,
1605
+ questionErrors,
1606
+ pageIndex
1607
+ );
1608
+ }
1609
+ }
1610
+ }
1611
+ }
1612
+ }
1613
+ }
1614
+
1615
+ focusQuestionElement(container: HTMLElement) {
1616
+ // Find first focusable element within the question
1617
+ const focusable = container.querySelector(
1618
+ 'input, select, textarea, [tabindex]'
1619
+ );
1620
+ if (focusable) {
1621
+ (focusable as HTMLElement).focus();
1622
+ } else {
1623
+ container.focus();
1624
+ }
1625
+ }
1626
+ validateForm(jsonData: any): {
1627
+ isValid: boolean;
1628
+ message: string;
1629
+ pageIndex: number | 0;
1630
+ } {
1631
+ const validateQuestions = (
1632
+ formElements: any[]
1633
+ ): { isValid: boolean; message: string; pageIndex: number | 0 } => {
1634
+ for (let element of formElements) {
1635
+ if (element.entityType === 'QUESTION') {
1636
+ const required = element.element.required;
1637
+ const answer = element.element.answer;
1638
+ const validation = element.element.validation;
1639
+ const show = element.show;
1640
+ if (element.element.formElement.elementType === 'FILE_PICKER') {
1641
+ const attachment = element.element.attachments;
1642
+ if (required && show) {
1643
+ if (
1644
+ (!answer || answer?.length === 0) &&
1645
+ (!attachment || attachment?.length == 0)
1646
+ ) {
1647
+ this.inValidQuestions.set(
1648
+ element.element.id,
1649
+ 'Required file question has no files uploaded.'
1650
+ );
1651
+ return {
1652
+ isValid: false,
1653
+ message: `Form is invalid - Required file question ${element.element.questionNumber} has no files uploaded.`,
1654
+ pageIndex: 0,
1655
+ };
1656
+ }
1657
+ }
1658
+ } else {
1659
+ if (required && show) {
1660
+ if (
1661
+ answer === null ||
1662
+ answer === undefined ||
1663
+ (typeof answer === 'string' && answer.trim() === '') ||
1664
+ (typeof answer === 'object' && Object.keys(answer).length === 0)
1665
+ ) {
1666
+ this.inValidQuestions.set(
1667
+ element.element.id,
1668
+ 'This is a required question'
1669
+ );
1670
+ return {
1671
+ isValid: false,
1672
+ message: `Form is invalid - Question ${element.element.questionNumber} is required but no answer provided.`,
1673
+ pageIndex: 0,
1674
+ };
1675
+ } else if (validation === false) {
1676
+ this.inValidQuestions.set(
1677
+ element.element.id,
1678
+ element.element.validationMessage
1679
+ );
1680
+ return {
1681
+ isValid: false,
1682
+ message: `Form is invalid - Question ${element.element.questionNumber} is required but validation is false.`,
1683
+ pageIndex: 0,
1684
+ };
1685
+ }
1686
+ } else if (!required && answer && Object.keys(answer).length > 0) {
1687
+ if (validation === false) {
1688
+ this.inValidQuestions.set(
1689
+ element.element.id,
1690
+ element.element.validationMessage
1691
+ );
1692
+ return {
1693
+ isValid: false,
1694
+ message: `Form is invalid - Question ${element.element.questionNumber} is not required but answer provided with validation false.`,
1695
+ pageIndex: 0,
1696
+ };
1697
+ }
1698
+ }
1699
+ }
1700
+ if (element.element.childLogics) {
1701
+ for (let logic of element.element.childLogics) {
1702
+ if (logic.showLogic) {
1703
+ const result = validateQuestions(logic.rows);
1704
+ if (!result.isValid) return result;
1705
+ }
1706
+ }
1707
+ }
1708
+ } else if (element.entityType === 'SUBFORM' && !element.element.deleted) {
1709
+ if(element.element.property?.subFormStructure == 'single'){
1710
+ for(let row of element.element.rows){
1711
+ const result = validateQuestions(row.grid);
1712
+ if (!result.isValid) return result;
1713
+ }
1714
+ } else {
1715
+ this.validateSubForm(element);
1716
+ }
1717
+ } else if (
1718
+ (element.entityType === 'QUESTION_GROUP' ||
1719
+ element.entityType === 'MULTIFIELD') &&
1720
+ element.element.rows
1721
+ ) {
1722
+ for (const row of element.element.rows) {
1723
+ const result = validateQuestions(row.grid);
1724
+ if (!result.isValid && element.entityType != 'MULTIFIELD')
1725
+ return result;
1726
+ }
1727
+ }
1728
+ }
1729
+ return { isValid: true, message: '', pageIndex: 0 };
1730
+ };
1731
+
1732
+ for (let pageIndex = 0; pageIndex < jsonData.length; pageIndex++) {
1733
+ const page = jsonData[pageIndex];
1734
+ for (let row of page.rows) {
1735
+ const result = validateQuestions(row.grid);
1736
+ this.inValidQuestions.forEach((message: string, id: string) => {
1737
+ this.validationService.setInvalid(id, message, pageIndex);
1738
+ });
1739
+ this.inValidQuestions = new Map<string, string>();
1740
+ // if (!result.isValid) {
1741
+ // return {
1742
+ // isValid: false,
1743
+ // message: `Page ${pageIndex + 1}: ${result.message}`,
1744
+ // pageIndex: pageIndex,
1745
+ // };
1746
+ // }
1747
+ }
1748
+ }
1749
+ this.currentPageIndex = jsonData.length - 1;
1750
+ if (this.validationService.getFirstInvalid()) {
1751
+ this.navigateToFirstError();
1752
+ return {
1753
+ isValid: false,
1754
+ message: `Page`,
1755
+ pageIndex: 0,
1756
+ };
1757
+ }
1758
+ return {
1759
+ isValid: true,
1760
+ message: 'Form is valid.',
1761
+ pageIndex: 0,
1762
+ };
1763
+ }
1764
+
1765
+ validateSubForm(element: any) {
1766
+ if (element.entityType !== 'SUBFORM') {
1767
+ return;
1768
+ }
1769
+
1770
+ const submissions = element.element?.submissions || [];
1771
+
1772
+ /* ------------------------------
1773
+ * 1️⃣ Subform required but no submission
1774
+ * ------------------------------ */
1775
+ if (element.element?.required && submissions.length === 0) {
1776
+ this.inValidQuestions.set(
1777
+ element.element.id,
1778
+ 'At least one submission is required'
1779
+ );
1780
+ return; // nothing else to validate
1781
+ }
1782
+
1783
+ submissions.forEach((submission: any, submissionIndex: number) => {
1784
+ const answers = submission.answers || [];
1785
+
1786
+ /* ------------------------------
1787
+ * 2️⃣ Submission exists but no answers
1788
+ * ------------------------------ */
1789
+ const hasAnyAnswer = answers.some(
1790
+ (a: any) =>
1791
+ a.answer !== null &&
1792
+ a.answer !== undefined &&
1793
+ a.answer !== '' &&
1794
+ !(Array.isArray(a.answer) && a.answer.length === 0)
1795
+ );
1796
+
1797
+ if (element.element?.required && !hasAnyAnswer) {
1798
+ const key = `${element.element.id}`;
1799
+ this.inValidQuestions.set(
1800
+ key,
1801
+ 'At least one answer is required in the subform'
1802
+ );
1803
+ return;
1804
+ }
1805
+
1806
+ /* ------------------------------
1807
+ * 3️⃣ Existing question-level validations
1808
+ * ------------------------------ */
1809
+ answers.forEach((answerObj: any) => {
1810
+ const question = this.findQuestionById(
1811
+ element.element.rows,
1812
+ answerObj.questionId
1813
+ );
1814
+
1815
+ const ans = answerObj.answer;
1816
+
1817
+ // Required question check
1818
+ if ((question?.required || answerObj.required) &&
1819
+ (ans === '' || ans === null || ans === undefined || ans?.length === 0)) {
1820
+ const key = `${submissionIndex}-${question.id}`;
1821
+ this.inValidQuestions.set(key, 'This is a required question');
1822
+ }
1823
+
1824
+ // Email validation
1825
+ if (question?.formElement?.elementType === 'EMAIL' && ans) {
1826
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1827
+ if (!emailRegex.test(ans)) {
1828
+ const key = `${submissionIndex}-${question.id}`;
1829
+ this.inValidQuestions.set(
1830
+ key,
1831
+ 'Please enter a valid email address'
1832
+ );
1833
+ }
1834
+ }
1835
+ });
1836
+ });
1837
+ }
1838
+
1839
+
1840
+ // 🔍 Helper: find question definition inside rows
1841
+ private findQuestionById(rows: any[], questionId: string): any {
1842
+ for (const row of rows) {
1843
+ for (const gridElement of row.grid) {
1844
+ if (
1845
+ gridElement.entityType === 'QUESTION' &&
1846
+ gridElement.element.id === questionId
1847
+ ) {
1848
+ return gridElement.element;
1849
+ }
1850
+ }
1851
+ }
1852
+ return null;
1853
+ }
1854
+
1855
+ updateSubFormWithSubmissions(event: any) {
1856
+ this.updateSubForm(this.pages, event);
1857
+ }
1858
+
1859
+ updateSubForm = (pages: any[], updatedSubForm: any) => {
1860
+ pages.forEach((page) => {
1861
+ page.rows.forEach((row: any) => {
1862
+ row.grid.forEach((element: any) => {
1863
+ if (
1864
+ element.entityType === 'SUBFORM' &&
1865
+ element.element.id === updatedSubForm.id
1866
+ ) {
1867
+ // Update the existing subform with the new data
1868
+ element.element = updatedSubForm;
1869
+ } else if (
1870
+ element.entityType === 'SUBFORM' &&
1871
+ element.element.id !== updatedSubForm.formId
1872
+ ) {
1873
+ // Handle multiple subforms logic here if needed
1874
+ } else if (
1875
+ element.entityType === 'QUESTION' &&
1876
+ element.element.childLogics
1877
+ ) {
1878
+ // Recursively update child logics
1879
+ // element.element.childLogics.forEach((logic: any) => {
1880
+ // if (logic.showLogic) {
1881
+ // this.updateSubForm(logic.rows, updatedSubForm);
1882
+ // }
1883
+ // });
1884
+ }
1885
+ });
1886
+ });
1887
+ });
1888
+ };
1889
+
1890
+ extractGroupedWebhookData(webhooks: any[]): {
1891
+ headers: Record<string, string>;
1892
+ params: Record<string, string>;
1893
+ pathVariables: Record<string, string>;
1894
+ requestBodies: Record<string, string>;
1895
+ } {
1896
+ const result: {
1897
+ headers: Record<string, string>;
1898
+ params: Record<string, string>;
1899
+ pathVariables: Record<string, string>;
1900
+ requestBodies: Record<string, string>;
1901
+ } = {
1902
+ headers: {},
1903
+ params: {},
1904
+ pathVariables: {},
1905
+ requestBodies: {},
1906
+ };
1907
+
1908
+ for (const webhook of webhooks ?? []) {
1909
+ for (const item of webhook?.headers ?? []) {
1910
+ const value = this.getValueFromStorage(item.valueSource, item.inputValue);
1911
+ if (value !== null) {
1912
+ result.headers[item.keyName] = value;
1913
+ }
1914
+ }
1915
+
1916
+ for (const item of webhook?.params ?? []) {
1917
+ const value = this.getValueFromStorage(item.valueSource, item.inputValue);
1918
+ if (value !== null) {
1919
+ result.params[item.keyName] = value;
1920
+ }
1921
+ }
1922
+
1923
+ for (const item of webhook?.pathVariables ?? []) {
1924
+ const value = this.getValueFromStorage(item.valueSource, item.inputValue);
1925
+ if (value !== null) {
1926
+ result.pathVariables[item.keyName] = value;
1927
+ }
1928
+ }
1929
+
1930
+ for (const item of webhook?.requestBodies ?? []) {
1931
+ const value = this.getValueFromStorage(item.valueSource, item.inputValue);
1932
+ if (value !== null) {
1933
+ result.requestBodies[item.keyName] = value;
1934
+ }
1935
+ }
1936
+ }
1937
+
1938
+ return result;
1939
+ }
1940
+
1941
+ getValueFromStorage(
1942
+ source: 'LOCAL_STORAGE' | 'SESSION_STORAGE',
1943
+ key: string
1944
+ ): string | null {
1945
+ if (source === 'LOCAL_STORAGE') return localStorage.getItem(key);
1946
+ if (source === 'SESSION_STORAGE') return sessionStorage.getItem(key);
1947
+ return null;
1948
+ }
1949
+
1950
+ cancelled(){
1951
+ this.cancel.emit();
1952
+ }
1953
+ }