@piserve-tech/form-submission 1.3.149 → 1.3.151

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.
@@ -3,32 +3,49 @@ import * as i0 from "@angular/core";
3
3
  export class FormValidationService {
4
4
  constructor() { }
5
5
  validateRequiredQuestions(page) {
6
- const validateQuestions = (formElements) => {
6
+ const validateQuestions = (formElements, submissionContext = null) => {
7
7
  for (let element of formElements) {
8
8
  if (element.entityType === 'QUESTION') {
9
- const required = element.element.required;
10
- const answer = element.element.answer;
11
- if (element.element.formElement.elementType === 'FILE_PICKER') {
12
- if (required && (!answer || answer.length === 0)) {
9
+ const question = element.element;
10
+ const required = question.required;
11
+ const questionId = question.id;
12
+ const elementType = question.formElement?.elementType;
13
+ // If inside a subform submission context
14
+ if (submissionContext?.isInMultipleSubform) {
15
+ const { submission, questionMap } = submissionContext;
16
+ const submittedAnswer = submission.answers.find((ans) => ans.questionId === questionId);
17
+ const answer = submittedAnswer?.answer;
18
+ const isEmpty = answer === null ||
19
+ answer === undefined ||
20
+ (typeof answer === 'string' && answer.trim() === '') ||
21
+ (typeof answer === 'object' && Object.keys(answer).length === 0);
22
+ if (required && isEmpty) {
13
23
  return {
14
24
  isValid: false,
15
- message: `Required file question ${element.element.questionNumber} is not answered.`,
25
+ message: `Required question ${question.questionNumber} is not answered in a subform.`,
16
26
  };
17
27
  }
18
28
  }
19
29
  else {
20
- if (required && (!answer || Object.keys(answer).length === 0)) {
30
+ // Normal non-subform validation
31
+ const answer = question.answer;
32
+ const isEmpty = answer === null ||
33
+ answer === undefined ||
34
+ (typeof answer === 'string' && answer.trim() === '') ||
35
+ (typeof answer === 'object' && Object.keys(answer).length === 0);
36
+ if (required && isEmpty) {
21
37
  return {
22
38
  isValid: false,
23
- message: `Required question ${element.element.questionNumber} is not answered.`,
39
+ message: `Required question ${question.questionNumber} is not answered.`,
24
40
  };
25
41
  }
26
42
  }
27
- if (element.element.childLogics) {
28
- for (let logic of element.element.childLogics) {
43
+ // Recursively validate child logics
44
+ if (question.childLogics) {
45
+ for (let logic of question.childLogics) {
29
46
  if (logic.showLogic) {
30
47
  for (let row of logic.rows) {
31
- const result = validateQuestions(row.grid);
48
+ const result = validateQuestions(row.grid, submissionContext);
32
49
  if (!result.isValid)
33
50
  return result;
34
51
  }
@@ -37,8 +54,50 @@ export class FormValidationService {
37
54
  }
38
55
  }
39
56
  else if (element.entityType === 'SUBFORM') {
40
- if (element.element.rows) {
41
- for (let row of element.element.rows) {
57
+ const subform = element.element;
58
+ if (subform.property?.subFormStructure === 'multiple') {
59
+ const submissions = subform.submissions || [];
60
+ // Check if subform has any required questions
61
+ let hasRequiredQuestion = false;
62
+ for (const row of subform.rows) {
63
+ for (const field of row.grid) {
64
+ if (field.entityType === 'QUESTION' && field.element.required) {
65
+ hasRequiredQuestion = true;
66
+ break;
67
+ }
68
+ }
69
+ if (hasRequiredQuestion)
70
+ break;
71
+ }
72
+ if (hasRequiredQuestion && submissions.length === 0) {
73
+ return {
74
+ isValid: false,
75
+ message: `Subform "${subform.title || 'Untitled'}" is missing required submissions.`,
76
+ };
77
+ }
78
+ const questionMap = new Map();
79
+ for (const row of subform.rows) {
80
+ for (const field of row.grid) {
81
+ if (field.entityType === 'QUESTION') {
82
+ questionMap.set(field.element.id, field.element);
83
+ }
84
+ }
85
+ }
86
+ for (const submission of submissions) {
87
+ for (const row of subform.rows) {
88
+ const result = validateQuestions(row.grid, {
89
+ isInMultipleSubform: true,
90
+ submission,
91
+ questionMap
92
+ });
93
+ if (!result.isValid)
94
+ return result;
95
+ }
96
+ }
97
+ }
98
+ else {
99
+ // Single-entry subform
100
+ for (const row of subform.rows || []) {
42
101
  const result = validateQuestions(row.grid);
43
102
  if (!result.isValid)
44
103
  return result;
@@ -48,7 +107,7 @@ export class FormValidationService {
48
107
  else if ((element.entityType === 'QUESTION_GROUP' || element.entityType === 'MULTIFIELD') &&
49
108
  element.element.rows) {
50
109
  for (const row of element.element.rows) {
51
- const result = validateQuestions(row.grid);
110
+ const result = validateQuestions(row.grid, submissionContext);
52
111
  if (!result.isValid)
53
112
  return result;
54
113
  }
@@ -56,11 +115,11 @@ export class FormValidationService {
56
115
  }
57
116
  return { isValid: true, message: 'All required questions are answered.' };
58
117
  };
59
- for (let row of page.rows) {
118
+ // Start validation from top-level rows
119
+ for (const row of page.rows) {
60
120
  const result = validateQuestions(row.grid);
61
- if (!result.isValid) {
121
+ if (!result.isValid)
62
122
  return result;
63
- }
64
123
  }
65
124
  return { isValid: true, message: 'All required questions are answered.' };
66
125
  }
@@ -73,4 +132,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
73
132
  providedIn: 'root'
74
133
  }]
75
134
  }], ctorParameters: function () { return []; } });
76
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9ybS12YWxpZGF0aW9uLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9mb3JtLXN1Ym1pc3Npb24vc3JjL3NlcnZpY2VzL2Zvcm0tdmFsaWRhdGlvbi5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7O0FBSzNDLE1BQU0sT0FBTyxxQkFBcUI7SUFFaEMsZ0JBQWUsQ0FBQztJQUVoQix5QkFBeUIsQ0FBQyxJQUFTO1FBQ2pDLE1BQU0saUJBQWlCLEdBQUcsQ0FDeEIsWUFBbUIsRUFDb0IsRUFBRTtZQUN6QyxLQUFLLElBQUksT0FBTyxJQUFJLFlBQVksRUFBRTtnQkFDaEMsSUFBSSxPQUFPLENBQUMsVUFBVSxLQUFLLFVBQVUsRUFBRTtvQkFDckMsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUM7b0JBQzFDLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO29CQUN0QyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLFdBQVcsS0FBSyxhQUFhLEVBQUU7d0JBQzdELElBQUksUUFBUSxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUMsRUFBRTs0QkFDaEQsT0FBTztnQ0FDTCxPQUFPLEVBQUUsS0FBSztnQ0FDZCxPQUFPLEVBQUUsMEJBQTBCLE9BQU8sQ0FBQyxPQUFPLENBQUMsY0FBYyxtQkFBbUI7NkJBQ3JGLENBQUM7eUJBQ0g7cUJBQ0Y7eUJBQU07d0JBQ0wsSUFBSSxRQUFRLElBQUksQ0FBQyxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUMsRUFBRTs0QkFDN0QsT0FBTztnQ0FDTCxPQUFPLEVBQUUsS0FBSztnQ0FDZCxPQUFPLEVBQUUscUJBQXFCLE9BQU8sQ0FBQyxPQUFPLENBQUMsY0FBYyxtQkFBbUI7NkJBQ2hGLENBQUM7eUJBQ0g7cUJBQ0Y7b0JBQ0QsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRTt3QkFDL0IsS0FBSyxJQUFJLEtBQUssSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRTs0QkFDN0MsSUFBSSxLQUFLLENBQUMsU0FBUyxFQUFFO2dDQUNuQixLQUFLLElBQUksR0FBRyxJQUFJLEtBQUssQ0FBQyxJQUFJLEVBQUU7b0NBQzFCLE1BQU0sTUFBTSxHQUFHLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztvQ0FDM0MsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPO3dDQUFFLE9BQU8sTUFBTSxDQUFDO2lDQUNwQzs2QkFDRjt5QkFDRjtxQkFDRjtpQkFDRjtxQkFBTSxJQUFJLE9BQU8sQ0FBQyxVQUFVLEtBQUssU0FBUyxFQUFFO29CQUMzQyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO3dCQUN4QixLQUFLLElBQUksR0FBRyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFOzRCQUNwQyxNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7NEJBQzNDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTztnQ0FBRSxPQUFPLE1BQU0sQ0FBQzt5QkFDcEM7cUJBQ0Y7aUJBQ0Y7cUJBQU0sSUFDTCxDQUFDLE9BQU8sQ0FBQyxVQUFVLEtBQUssZ0JBQWdCLElBQUksT0FBTyxDQUFDLFVBQVUsS0FBSyxZQUFZLENBQUM7b0JBQ2hGLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUNwQjtvQkFDQSxLQUFLLE1BQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO3dCQUN0QyxNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7d0JBQzNDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTzs0QkFBRSxPQUFPLE1BQU0sQ0FBQztxQkFDcEM7aUJBQ0Y7YUFDRjtZQUVELE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxDQUFDO1FBQzVFLENBQUMsQ0FBQztRQUVGLEtBQUssSUFBSSxHQUFHLElBQUksSUFBSSxDQUFDLElBQUksRUFBRTtZQUN6QixNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDM0MsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUU7Z0JBQ25CLE9BQU8sTUFBTSxDQUFDO2FBQ2Y7U0FDRjtRQUVELE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxDQUFDO0lBQzVFLENBQUM7K0dBbEVVLHFCQUFxQjttSEFBckIscUJBQXFCLGNBRnBCLE1BQU07OzRGQUVQLHFCQUFxQjtrQkFIakMsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIEZvcm1WYWxpZGF0aW9uU2VydmljZSB7XG5cbiAgY29uc3RydWN0b3IoKSB7fVxuXG4gIHZhbGlkYXRlUmVxdWlyZWRRdWVzdGlvbnMocGFnZTogYW55KTogeyBpc1ZhbGlkOiBib29sZWFuOyBtZXNzYWdlOiBzdHJpbmcgfSB7XG4gICAgY29uc3QgdmFsaWRhdGVRdWVzdGlvbnMgPSAoXG4gICAgICBmb3JtRWxlbWVudHM6IGFueVtdXG4gICAgKTogeyBpc1ZhbGlkOiBib29sZWFuOyBtZXNzYWdlOiBzdHJpbmcgfSA9PiB7XG4gICAgICBmb3IgKGxldCBlbGVtZW50IG9mIGZvcm1FbGVtZW50cykge1xuICAgICAgICBpZiAoZWxlbWVudC5lbnRpdHlUeXBlID09PSAnUVVFU1RJT04nKSB7XG4gICAgICAgICAgY29uc3QgcmVxdWlyZWQgPSBlbGVtZW50LmVsZW1lbnQucmVxdWlyZWQ7XG4gICAgICAgICAgY29uc3QgYW5zd2VyID0gZWxlbWVudC5lbGVtZW50LmFuc3dlcjtcbiAgICAgICAgICBpZiAoZWxlbWVudC5lbGVtZW50LmZvcm1FbGVtZW50LmVsZW1lbnRUeXBlID09PSAnRklMRV9QSUNLRVInKSB7XG4gICAgICAgICAgICBpZiAocmVxdWlyZWQgJiYgKCFhbnN3ZXIgfHwgYW5zd2VyLmxlbmd0aCA9PT0gMCkpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBpc1ZhbGlkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBgUmVxdWlyZWQgZmlsZSBxdWVzdGlvbiAke2VsZW1lbnQuZWxlbWVudC5xdWVzdGlvbk51bWJlcn0gaXMgbm90IGFuc3dlcmVkLmAsXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChyZXF1aXJlZCAmJiAoIWFuc3dlciB8fCBPYmplY3Qua2V5cyhhbnN3ZXIpLmxlbmd0aCA9PT0gMCkpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBpc1ZhbGlkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBgUmVxdWlyZWQgcXVlc3Rpb24gJHtlbGVtZW50LmVsZW1lbnQucXVlc3Rpb25OdW1iZXJ9IGlzIG5vdCBhbnN3ZXJlZC5gLFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWxlbWVudC5lbGVtZW50LmNoaWxkTG9naWNzKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBsb2dpYyBvZiBlbGVtZW50LmVsZW1lbnQuY2hpbGRMb2dpY3MpIHtcbiAgICAgICAgICAgICAgaWYgKGxvZ2ljLnNob3dMb2dpYykge1xuICAgICAgICAgICAgICAgIGZvciAobGV0IHJvdyBvZiBsb2dpYy5yb3dzKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCByZXN1bHQgPSB2YWxpZGF0ZVF1ZXN0aW9ucyhyb3cuZ3JpZCk7XG4gICAgICAgICAgICAgICAgICBpZiAoIXJlc3VsdC5pc1ZhbGlkKSByZXR1cm4gcmVzdWx0O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChlbGVtZW50LmVudGl0eVR5cGUgPT09ICdTVUJGT1JNJykge1xuICAgICAgICAgIGlmIChlbGVtZW50LmVsZW1lbnQucm93cykge1xuICAgICAgICAgICAgZm9yIChsZXQgcm93IG9mIGVsZW1lbnQuZWxlbWVudC5yb3dzKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHZhbGlkYXRlUXVlc3Rpb25zKHJvdy5ncmlkKTtcbiAgICAgICAgICAgICAgaWYgKCFyZXN1bHQuaXNWYWxpZCkgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgICAgKGVsZW1lbnQuZW50aXR5VHlwZSA9PT0gJ1FVRVNUSU9OX0dST1VQJyB8fCBlbGVtZW50LmVudGl0eVR5cGUgPT09ICdNVUxUSUZJRUxEJykgJiZcbiAgICAgICAgICBlbGVtZW50LmVsZW1lbnQucm93c1xuICAgICAgICApIHtcbiAgICAgICAgICBmb3IgKGNvbnN0IHJvdyBvZiBlbGVtZW50LmVsZW1lbnQucm93cykge1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gdmFsaWRhdGVRdWVzdGlvbnMocm93LmdyaWQpO1xuICAgICAgICAgICAgaWYgKCFyZXN1bHQuaXNWYWxpZCkgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHsgaXNWYWxpZDogdHJ1ZSwgbWVzc2FnZTogJ0FsbCByZXF1aXJlZCBxdWVzdGlvbnMgYXJlIGFuc3dlcmVkLicgfTtcbiAgICB9O1xuXG4gICAgZm9yIChsZXQgcm93IG9mIHBhZ2Uucm93cykge1xuICAgICAgY29uc3QgcmVzdWx0ID0gdmFsaWRhdGVRdWVzdGlvbnMocm93LmdyaWQpO1xuICAgICAgaWYgKCFyZXN1bHQuaXNWYWxpZCkge1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7IGlzVmFsaWQ6IHRydWUsIG1lc3NhZ2U6ICdBbGwgcmVxdWlyZWQgcXVlc3Rpb25zIGFyZSBhbnN3ZXJlZC4nIH07XG4gIH1cbn1cbiJdfQ==
135
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9ybS12YWxpZGF0aW9uLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9mb3JtLXN1Ym1pc3Npb24vc3JjL3NlcnZpY2VzL2Zvcm0tdmFsaWRhdGlvbi5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7O0FBSzNDLE1BQU0sT0FBTyxxQkFBcUI7SUFFaEMsZ0JBQWUsQ0FBQztJQUVoQix5QkFBeUIsQ0FBQyxJQUFTO1FBQ2pDLE1BQU0saUJBQWlCLEdBQUcsQ0FDeEIsWUFBbUIsRUFDbkIsb0JBQXlCLElBQUksRUFDVSxFQUFFO1lBQ3pDLEtBQUssSUFBSSxPQUFPLElBQUksWUFBWSxFQUFFO2dCQUNoQyxJQUFJLE9BQU8sQ0FBQyxVQUFVLEtBQUssVUFBVSxFQUFFO29CQUNyQyxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDO29CQUNqQyxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsUUFBUSxDQUFDO29CQUNuQyxNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMsRUFBRSxDQUFDO29CQUMvQixNQUFNLFdBQVcsR0FBRyxRQUFRLENBQUMsV0FBVyxFQUFFLFdBQVcsQ0FBQztvQkFFdEQseUNBQXlDO29CQUN6QyxJQUFJLGlCQUFpQixFQUFFLG1CQUFtQixFQUFFO3dCQUMxQyxNQUFNLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxHQUFHLGlCQUFpQixDQUFDO3dCQUV0RCxNQUFNLGVBQWUsR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLElBQUksQ0FDN0MsQ0FBQyxHQUFRLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEtBQUssVUFBVSxDQUM1QyxDQUFDO3dCQUVGLE1BQU0sTUFBTSxHQUFHLGVBQWUsRUFBRSxNQUFNLENBQUM7d0JBRXZDLE1BQU0sT0FBTyxHQUNYLE1BQU0sS0FBSyxJQUFJOzRC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LGlCQUFpQixDQUFDLENBQUM7d0JBQzlELElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTzs0QkFBRSxPQUFPLE1BQU0sQ0FBQztxQkFDcEM7aUJBQ0Y7YUFDRjtZQUVELE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxDQUFDO1FBQzVFLENBQUMsQ0FBQztRQUVGLHVDQUF1QztRQUN2QyxLQUFLLE1BQU0sR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDM0IsTUFBTSxNQUFNLEdBQUcsaUJBQWlCLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzNDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTztnQkFBRSxPQUFPLE1BQU0sQ0FBQztTQUNwQztRQUVELE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxDQUFDO0lBQzVFLENBQUM7K0dBN0lVLHFCQUFxQjttSEFBckIscUJBQXFCLGNBRnBCLE1BQU07OzRGQUVQLHFCQUFxQjtrQkFIakMsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIEZvcm1WYWxpZGF0aW9uU2VydmljZSB7XG5cbiAgY29uc3RydWN0b3IoKSB7fVxuXG4gIHZhbGlkYXRlUmVxdWlyZWRRdWVzdGlvbnMocGFnZTogYW55KTogeyBpc1ZhbGlkOiBib29sZWFuOyBtZXNzYWdlOiBzdHJpbmcgfSB7XG4gICAgY29uc3QgdmFsaWRhdGVRdWVzdGlvbnMgPSAoXG4gICAgICBmb3JtRWxlbWVudHM6IGFueVtdLFxuICAgICAgc3VibWlzc2lvbkNvbnRleHQ6IGFueSA9IG51bGxcbiAgICApOiB7IGlzVmFsaWQ6IGJvb2xlYW47IG1lc3NhZ2U6IHN0cmluZyB9ID0+IHtcbiAgICAgIGZvciAobGV0IGVsZW1lbnQgb2YgZm9ybUVsZW1lbnRzKSB7XG4gICAgICAgIGlmIChlbGVtZW50LmVudGl0eVR5cGUgPT09ICdRVUVTVElPTicpIHtcbiAgICAgICAgICBjb25zdCBxdWVzdGlvbiA9IGVsZW1lbnQuZWxlbWVudDtcbiAgICAgICAgICBjb25zdCByZXF1aXJlZCA9IHF1ZXN0aW9uLnJlcXVpcmVkO1xuICAgICAgICAgIGNvbnN0IHF1ZXN0aW9uSWQgPSBxdWVzdGlvbi5pZDtcbiAgICAgICAgICBjb25zdCBlbGVtZW50VHlwZSA9IHF1ZXN0aW9uLmZvcm1FbGVtZW50Py5lbGVtZW50VHlwZTtcbiAgXG4gICAgICAgICAgLy8gSWYgaW5zaWRlIGEgc3ViZm9ybSBzdWJtaXNzaW9uIGNvbnRleHRcbiAgICAgICAgICBpZiAoc3VibWlzc2lvbkNvbnRleHQ/LmlzSW5NdWx0aXBsZVN1YmZvcm0pIHtcbiAgICAgICAgICAgIGNvbnN0IHsgc3VibWlzc2lvbiwgcXVlc3Rpb25NYXAgfSA9IHN1Ym1pc3Npb25Db250ZXh0O1xuICBcbiAgICAgICAgICAgIGNvbnN0IHN1Ym1pdHRlZEFuc3dlciA9IHN1Ym1pc3Npb24uYW5zd2Vycy5maW5kKFxuICAgICAgICAgICAgICAoYW5zOiBhbnkpID0+IGFucy5xdWVzdGlvbklkID09PSBxdWVzdGlvbklkXG4gICAgICAgICAgICApO1xuICBcbiAgICAgICAgICAgIGNvbnN0IGFuc3dlciA9IHN1Ym1pdHRlZEFuc3dlcj8uYW5zd2VyO1xuICBcbiAgICAgICAgICAgIGNvbnN0IGlzRW1wdHkgPVxuICAgICAgICAgICAgICBhbnN3ZXIgPT09IG51bGwgfHxcbiAgICAgICAgICAgICAgYW5zd2VyID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICAgICAgKHR5cGVvZiBhbnN3ZXIgPT09ICdzdHJpbmcnICYmIGFuc3dlci50cmltKCkgPT09ICcnKSB8fFxuICAgICAgICAgICAgICAodHlwZW9mIGFuc3dlciA9PT0gJ29iamVjdCcgJiYgT2JqZWN0LmtleXMoYW5zd2VyKS5sZW5ndGggPT09IDApO1xuICBcbiAgICAgICAgICAgIGlmIChyZXF1aXJlZCAmJiBpc0VtcHR5KSB7XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgaXNWYWxpZDogZmFsc2UsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogYFJlcXVpcmVkIHF1ZXN0aW9uICR7cXVlc3Rpb24ucXVlc3Rpb25OdW1iZXJ9IGlzIG5vdCBhbnN3ZXJlZCBpbiBhIHN1YmZvcm0uYCxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gTm9ybWFsIG5vbi1zdWJmb3JtIHZhbGlkYXRpb25cbiAgICAgICAgICAgIGNvbnN0IGFuc3dlciA9IHF1ZXN0aW9uLmFuc3dlcjtcbiAgICAgICAgICAgIGNvbnN0IGlzRW1wdHkgPVxuICAgICAgICAgICAgICBhbnN3ZXIgPT09IG51bGwgfHxcbiAgICAgICAgICAgICAgYW5zd2VyID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICAgICAgKHR5cGVvZiBhbnN3ZXIgPT09ICdzdHJpbmcnICYmIGFuc3dlci50cmltKCkgPT09ICcnKSB8fFxuICAgICAgICAgICAgICAodHlwZW9mIGFuc3dlciA9PT0gJ29iamVjdCcgJiYgT2JqZWN0LmtleXMoYW5zd2VyKS5sZW5ndGggPT09IDApO1xuICBcbiAgICAgICAgICAgIGlmIChyZXF1aXJlZCAmJiBpc0VtcHR5KSB7XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgaXNWYWxpZDogZmFsc2UsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogYFJlcXVpcmVkIHF1ZXN0aW9uICR7cXVlc3Rpb24ucXVlc3Rpb25OdW1iZXJ9IGlzIG5vdCBhbnN3ZXJlZC5gLFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgXG4gICAgICAgICAgLy8gUmVjdXJzaXZlbHkgdmFsaWRhdGUgY2hpbGQgbG9naWNzXG4gICAgICAgICAgaWYgKHF1ZXN0aW9uLmNoaWxkTG9naWNzKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBsb2dpYyBvZiBxdWVzdGlvbi5jaGlsZExvZ2ljcykge1xuICAgICAgICAgICAgICBpZiAobG9naWMuc2hvd0xvZ2ljKSB7XG4gICAgICAgICAgICAgICAgZm9yIChsZXQgcm93IG9mIGxvZ2ljLnJvd3MpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHZhbGlkYXRlUXVlc3Rpb25zKHJvdy5ncmlkLCBzdWJtaXNzaW9uQ29udGV4dCk7XG4gICAgICAgICAgICAgICAgICBpZiAoIXJlc3VsdC5pc1ZhbGlkKSByZXR1cm4gcmVzdWx0O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChlbGVtZW50LmVudGl0eVR5cGUgPT09ICdTVUJGT1JNJykge1xuICAgICAgICAgIGNvbnN0IHN1YmZvcm0gPSBlbGVtZW50LmVsZW1lbnQ7XG4gIFxuICAgICAgICAgIGlmIChzdWJmb3JtLnByb3BlcnR5Py5zdWJGb3JtU3RydWN0dXJlID09PSAnbXVsdGlwbGUnKSB7XG4gICAgICAgICAgICBjb25zdCBzdWJtaXNzaW9ucyA9IHN1YmZvcm0uc3VibWlzc2lvbnMgfHwgW107XG4gICAgICAgICAgXG4gICAgICAgICAgICAvLyBDaGVjayBpZiBzdWJmb3JtIGhhcyBhbnkgcmVxdWlyZWQgcXVlc3Rpb25zXG4gICAgICAgICAgICBsZXQgaGFzUmVxdWlyZWRRdWVzdGlvbiA9IGZhbHNlO1xuICAgICAgICAgICAgZm9yIChjb25zdCByb3cgb2Ygc3ViZm9ybS5yb3dzKSB7XG4gICAgICAgICAgICAgIGZvciAoY29uc3QgZmllbGQgb2Ygcm93LmdyaWQpIHtcbiAgICAgICAgICAgICAgICBpZiAoZmllbGQuZW50aXR5VHlwZSA9PT0gJ1FVRVNUSU9OJyAmJiBmaWVsZC5lbGVtZW50LnJlcXVpcmVkKSB7XG4gICAgICAgICAgICAgICAgICBoYXNSZXF1aXJlZFF1ZXN0aW9uID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoaGFzUmVxdWlyZWRRdWVzdGlvbikgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgXG4gICAgICAgICAgICBpZiAoaGFzUmVxdWlyZWRRdWVzdGlvbiAmJiBzdWJtaXNzaW9ucy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBpc1ZhbGlkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBgU3ViZm9ybSBcIiR7c3ViZm9ybS50aXRsZSB8fCAnVW50aXRsZWQnfVwiIGlzIG1pc3NpbmcgcmVxdWlyZWQgc3VibWlzc2lvbnMuYCxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBcbiAgICAgICAgICAgIGNvbnN0IHF1ZXN0aW9uTWFwID0gbmV3IE1hcCgpO1xuICAgICAgICAgICAgZm9yIChjb25zdCByb3cgb2Ygc3ViZm9ybS5yb3dzKSB7XG4gICAgICAgICAgICAgIGZvciAoY29uc3QgZmllbGQgb2Ygcm93LmdyaWQpIHtcbiAgICAgICAgICAgICAgICBpZiAoZmllbGQuZW50aXR5VHlwZSA9PT0gJ1FVRVNUSU9OJykge1xuICAgICAgICAgICAgICAgICAgcXVlc3Rpb25NYXAuc2V0KGZpZWxkLmVsZW1lbnQuaWQsIGZpZWxkLmVsZW1lbnQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIFxuICAgICAgICAgICAgZm9yIChjb25zdCBzdWJtaXNzaW9uIG9mIHN1Ym1pc3Npb25zKSB7XG4gICAgICAgICAgICAgIGZvciAoY29uc3Qgcm93IG9mIHN1YmZvcm0ucm93cykge1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHZhbGlkYXRlUXVlc3Rpb25zKHJvdy5ncmlkLCB7XG4gICAgICAgICAgICAgICAgICBpc0luTXVsdGlwbGVTdWJmb3JtOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgc3VibWlzc2lvbixcbiAgICAgICAgICAgICAgICAgIHF1ZXN0aW9uTWFwXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgaWYgKCFyZXN1bHQuaXNWYWxpZCkgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICAgXG4gICAgICAgICAgXG4gICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBTaW5nbGUtZW50cnkgc3ViZm9ybVxuICAgICAgICAgICAgZm9yIChjb25zdCByb3cgb2Ygc3ViZm9ybS5yb3dzIHx8IFtdKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHZhbGlkYXRlUXVlc3Rpb25zKHJvdy5ncmlkKTtcbiAgICAgICAgICAgICAgaWYgKCFyZXN1bHQuaXNWYWxpZCkgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgICAgKGVsZW1lbnQuZW50aXR5VHlwZSA9PT0gJ1FVRVNUSU9OX0dST1VQJyB8fCBlbGVtZW50LmVudGl0eVR5cGUgPT09ICdNVUxUSUZJRUxEJykgJiZcbiAgICAgICAgICBlbGVtZW50LmVsZW1lbnQucm93c1xuICAgICAgICApIHtcbiAgICAgICAgICBmb3IgKGNvbnN0IHJvdyBvZiBlbGVtZW50LmVsZW1lbnQucm93cykge1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gdmFsaWRhdGVRdWVzdGlvbnMocm93LmdyaWQsIHN1Ym1pc3Npb25Db250ZXh0KTtcbiAgICAgICAgICAgIGlmICghcmVzdWx0LmlzVmFsaWQpIHJldHVybiByZXN1bHQ7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gIFxuICAgICAgcmV0dXJuIHsgaXNWYWxpZDogdHJ1ZSwgbWVzc2FnZTogJ0FsbCByZXF1aXJlZCBxdWVzdGlvbnMgYXJlIGFuc3dlcmVkLicgfTtcbiAgICB9O1xuICBcbiAgICAvLyBTdGFydCB2YWxpZGF0aW9uIGZyb20gdG9wLWxldmVsIHJvd3NcbiAgICBmb3IgKGNvbnN0IHJvdyBvZiBwYWdlLnJvd3MpIHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IHZhbGlkYXRlUXVlc3Rpb25zKHJvdy5ncmlkKTtcbiAgICAgIGlmICghcmVzdWx0LmlzVmFsaWQpIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICBcbiAgICByZXR1cm4geyBpc1ZhbGlkOiB0cnVlLCBtZXNzYWdlOiAnQWxsIHJlcXVpcmVkIHF1ZXN0aW9ucyBhcmUgYW5zd2VyZWQuJyB9O1xuICB9XG4gIFxuICBcbn1cbiJdfQ==
@@ -430,32 +430,49 @@ function mapOptionToModel(apiOption) {
430
430
  class FormValidationService {
431
431
  constructor() { }
432
432
  validateRequiredQuestions(page) {
433
- const validateQuestions = (formElements) => {
433
+ const validateQuestions = (formElements, submissionContext = null) => {
434
434
  for (let element of formElements) {
435
435
  if (element.entityType === 'QUESTION') {
436
- const required = element.element.required;
437
- const answer = element.element.answer;
438
- if (element.element.formElement.elementType === 'FILE_PICKER') {
439
- if (required && (!answer || answer.length === 0)) {
436
+ const question = element.element;
437
+ const required = question.required;
438
+ const questionId = question.id;
439
+ const elementType = question.formElement?.elementType;
440
+ // If inside a subform submission context
441
+ if (submissionContext?.isInMultipleSubform) {
442
+ const { submission, questionMap } = submissionContext;
443
+ const submittedAnswer = submission.answers.find((ans) => ans.questionId === questionId);
444
+ const answer = submittedAnswer?.answer;
445
+ const isEmpty = answer === null ||
446
+ answer === undefined ||
447
+ (typeof answer === 'string' && answer.trim() === '') ||
448
+ (typeof answer === 'object' && Object.keys(answer).length === 0);
449
+ if (required && isEmpty) {
440
450
  return {
441
451
  isValid: false,
442
- message: `Required file question ${element.element.questionNumber} is not answered.`,
452
+ message: `Required question ${question.questionNumber} is not answered in a subform.`,
443
453
  };
444
454
  }
445
455
  }
446
456
  else {
447
- if (required && (!answer || Object.keys(answer).length === 0)) {
457
+ // Normal non-subform validation
458
+ const answer = question.answer;
459
+ const isEmpty = answer === null ||
460
+ answer === undefined ||
461
+ (typeof answer === 'string' && answer.trim() === '') ||
462
+ (typeof answer === 'object' && Object.keys(answer).length === 0);
463
+ if (required && isEmpty) {
448
464
  return {
449
465
  isValid: false,
450
- message: `Required question ${element.element.questionNumber} is not answered.`,
466
+ message: `Required question ${question.questionNumber} is not answered.`,
451
467
  };
452
468
  }
453
469
  }
454
- if (element.element.childLogics) {
455
- for (let logic of element.element.childLogics) {
470
+ // Recursively validate child logics
471
+ if (question.childLogics) {
472
+ for (let logic of question.childLogics) {
456
473
  if (logic.showLogic) {
457
474
  for (let row of logic.rows) {
458
- const result = validateQuestions(row.grid);
475
+ const result = validateQuestions(row.grid, submissionContext);
459
476
  if (!result.isValid)
460
477
  return result;
461
478
  }
@@ -464,8 +481,50 @@ class FormValidationService {
464
481
  }
465
482
  }
466
483
  else if (element.entityType === 'SUBFORM') {
467
- if (element.element.rows) {
468
- for (let row of element.element.rows) {
484
+ const subform = element.element;
485
+ if (subform.property?.subFormStructure === 'multiple') {
486
+ const submissions = subform.submissions || [];
487
+ // Check if subform has any required questions
488
+ let hasRequiredQuestion = false;
489
+ for (const row of subform.rows) {
490
+ for (const field of row.grid) {
491
+ if (field.entityType === 'QUESTION' && field.element.required) {
492
+ hasRequiredQuestion = true;
493
+ break;
494
+ }
495
+ }
496
+ if (hasRequiredQuestion)
497
+ break;
498
+ }
499
+ if (hasRequiredQuestion && submissions.length === 0) {
500
+ return {
501
+ isValid: false,
502
+ message: `Subform "${subform.title || 'Untitled'}" is missing required submissions.`,
503
+ };
504
+ }
505
+ const questionMap = new Map();
506
+ for (const row of subform.rows) {
507
+ for (const field of row.grid) {
508
+ if (field.entityType === 'QUESTION') {
509
+ questionMap.set(field.element.id, field.element);
510
+ }
511
+ }
512
+ }
513
+ for (const submission of submissions) {
514
+ for (const row of subform.rows) {
515
+ const result = validateQuestions(row.grid, {
516
+ isInMultipleSubform: true,
517
+ submission,
518
+ questionMap
519
+ });
520
+ if (!result.isValid)
521
+ return result;
522
+ }
523
+ }
524
+ }
525
+ else {
526
+ // Single-entry subform
527
+ for (const row of subform.rows || []) {
469
528
  const result = validateQuestions(row.grid);
470
529
  if (!result.isValid)
471
530
  return result;
@@ -475,7 +534,7 @@ class FormValidationService {
475
534
  else if ((element.entityType === 'QUESTION_GROUP' || element.entityType === 'MULTIFIELD') &&
476
535
  element.element.rows) {
477
536
  for (const row of element.element.rows) {
478
- const result = validateQuestions(row.grid);
537
+ const result = validateQuestions(row.grid, submissionContext);
479
538
  if (!result.isValid)
480
539
  return result;
481
540
  }
@@ -483,11 +542,11 @@ class FormValidationService {
483
542
  }
484
543
  return { isValid: true, message: 'All required questions are answered.' };
485
544
  };
486
- for (let row of page.rows) {
545
+ // Start validation from top-level rows
546
+ for (const row of page.rows) {
487
547
  const result = validateQuestions(row.grid);
488
- if (!result.isValid) {
548
+ if (!result.isValid)
489
549
  return result;
490
- }
491
550
  }
492
551
  return { isValid: true, message: 'All required questions are answered.' };
493
552
  }