@abyss-project/form 1.0.3 → 1.0.4
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.
|
@@ -64,6 +64,7 @@ export type FormContentQuestionBaseOptionsDTO = {
|
|
|
64
64
|
formDocumentIds: string[] | null;
|
|
65
65
|
formCodeBlockIds: string[];
|
|
66
66
|
description: string | null;
|
|
67
|
+
displayCondition?: FormContentQuestionDisplayConditionDTO;
|
|
67
68
|
};
|
|
68
69
|
export type FormContentQuestionDateOptionsDTO = FormContentQuestionBaseOptionsDTO & {
|
|
69
70
|
type: FormContentQuestionType.DATE;
|
|
@@ -139,3 +140,27 @@ export type FormResponseQuestionDTO = {
|
|
|
139
140
|
export type FormResponseDTO = {
|
|
140
141
|
questions: Record<string, FormResponseQuestionDTO>;
|
|
141
142
|
};
|
|
143
|
+
export type FormContentQuestionDisplayConditionDTO = {
|
|
144
|
+
shouldDisplay: boolean;
|
|
145
|
+
conditions: {
|
|
146
|
+
operator: 'and' | 'or';
|
|
147
|
+
filters: {
|
|
148
|
+
questionId: string;
|
|
149
|
+
number?: {
|
|
150
|
+
operator?: 'eq' | 'neq' | 'gt' | 'lt' | 'gte' | 'lte';
|
|
151
|
+
value?: number;
|
|
152
|
+
} | null;
|
|
153
|
+
period?: {
|
|
154
|
+
before?: Date | null;
|
|
155
|
+
after?: Date | null;
|
|
156
|
+
} | null;
|
|
157
|
+
date?: {
|
|
158
|
+
before?: Date | null;
|
|
159
|
+
after?: Date | null;
|
|
160
|
+
} | null;
|
|
161
|
+
freeText?: {
|
|
162
|
+
contains?: string | null;
|
|
163
|
+
};
|
|
164
|
+
}[];
|
|
165
|
+
}[];
|
|
166
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { FormContentDTO, FormContentPageDTO, FormContentQuestionBaseOptionsDTO, FormContentQuestionDTO } from '../types';
|
|
1
|
+
import { FormContentDTO, FormContentPageDTO, FormContentQuestionBaseOptionsDTO, FormContentQuestionDTO, FormResponseDTO } from '../types';
|
|
2
2
|
export declare const formContentMapper: (formContent: FormContentDTO) => FormContentDTO;
|
|
3
|
-
export declare const formContentPageMapper: (formContentPage: FormContentPageDTO) => FormContentPageDTO;
|
|
4
|
-
export declare const formContentQuestionMapper: (question: FormContentQuestionDTO) => FormContentQuestionBaseOptionsDTO;
|
|
5
|
-
export declare const mapQuestionByType: (question: FormContentQuestionDTO) => FormContentQuestionDTO;
|
|
3
|
+
export declare const formContentPageMapper: (formContentPage: FormContentPageDTO, allQuestions: FormContentQuestionDTO[]) => FormContentPageDTO;
|
|
4
|
+
export declare const formContentQuestionMapper: (question: FormContentQuestionDTO, allQuestions: FormContentQuestionDTO[]) => FormContentQuestionBaseOptionsDTO;
|
|
5
|
+
export declare const mapQuestionByType: (question: FormContentQuestionDTO, allQuestions: FormContentQuestionDTO[]) => FormContentQuestionDTO;
|
|
6
|
+
export declare const shouldDisplayQuestion: (question: FormContentQuestionDTO, content: FormContentDTO, response: FormResponseDTO) => boolean;
|
package/dist/utils/form.utils.js
CHANGED
|
@@ -1,44 +1,114 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.mapQuestionByType = exports.formContentQuestionMapper = exports.formContentPageMapper = exports.formContentMapper = void 0;
|
|
3
|
+
exports.shouldDisplayQuestion = exports.mapQuestionByType = exports.formContentQuestionMapper = exports.formContentPageMapper = exports.formContentMapper = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
|
+
const lodash_1 = require("lodash");
|
|
5
6
|
const formContentMapper = (formContent) => {
|
|
6
7
|
return {
|
|
7
|
-
pages: formContent.pages.map(exports.formContentPageMapper),
|
|
8
|
+
pages: formContent.pages.map((page) => (0, exports.formContentPageMapper)(page, formContent.pages.flatMap((p) => p.questions))),
|
|
8
9
|
formDocumentIds: formContent.formDocumentIds || null,
|
|
9
10
|
formCodeBlockIds: formContent.formCodeBlockIds || null,
|
|
10
11
|
};
|
|
11
12
|
};
|
|
12
13
|
exports.formContentMapper = formContentMapper;
|
|
13
|
-
const formContentPageMapper = (formContentPage) => {
|
|
14
|
+
const formContentPageMapper = (formContentPage, allQuestions) => {
|
|
14
15
|
return {
|
|
15
16
|
id: formContentPage.id,
|
|
16
17
|
title: formContentPage.title,
|
|
17
18
|
description: formContentPage.description,
|
|
18
|
-
questions: formContentPage.questions.map(exports.mapQuestionByType),
|
|
19
|
+
questions: formContentPage.questions.map((question) => (0, exports.mapQuestionByType)(question, allQuestions)),
|
|
19
20
|
formDocumentIds: formContentPage.formDocumentIds || null,
|
|
20
21
|
formCodeBlockIds: formContentPage.formCodeBlockIds || null,
|
|
21
22
|
};
|
|
22
23
|
};
|
|
23
24
|
exports.formContentPageMapper = formContentPageMapper;
|
|
24
|
-
const formContentQuestionMapper = (question) => {
|
|
25
|
+
const formContentQuestionMapper = (question, allQuestions) => {
|
|
26
|
+
var _a;
|
|
27
|
+
const { id, ask, description, isRequired, allowFileUpload, formDocumentIds, formCodeBlockIds, displayCondition, } = question;
|
|
28
|
+
const cleanedDisplayCondition = {
|
|
29
|
+
...displayCondition,
|
|
30
|
+
shouldDisplay: (displayCondition === null || displayCondition === void 0 ? void 0 : displayCondition.shouldDisplay) || false,
|
|
31
|
+
conditions: (_a = displayCondition === null || displayCondition === void 0 ? void 0 : displayCondition.conditions) === null || _a === void 0 ? void 0 : _a.map((condition) => {
|
|
32
|
+
return {
|
|
33
|
+
operator: condition.operator,
|
|
34
|
+
filters: (0, lodash_1.compact)(condition.filters.map((filter) => {
|
|
35
|
+
const correspondingQuestion = allQuestions.find((q) => q.id === filter.questionId);
|
|
36
|
+
if (!correspondingQuestion) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (correspondingQuestion.type === types_1.FormContentQuestionType.DATE && filter.date) {
|
|
40
|
+
return {
|
|
41
|
+
questionId: filter.questionId,
|
|
42
|
+
date: {
|
|
43
|
+
before: filter.date.before || null,
|
|
44
|
+
after: filter.date.after || null,
|
|
45
|
+
},
|
|
46
|
+
freeText: null,
|
|
47
|
+
number: null,
|
|
48
|
+
period: null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (correspondingQuestion.type === types_1.FormContentQuestionType.PERIOD && filter.period) {
|
|
52
|
+
return {
|
|
53
|
+
questionId: filter.questionId,
|
|
54
|
+
period: {
|
|
55
|
+
before: filter.period.before || null,
|
|
56
|
+
after: filter.period.after || null,
|
|
57
|
+
},
|
|
58
|
+
freeText: null,
|
|
59
|
+
number: null,
|
|
60
|
+
date: null,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (correspondingQuestion.type === types_1.FormContentQuestionType.NUMBER && filter.number) {
|
|
64
|
+
return {
|
|
65
|
+
questionId: filter.questionId,
|
|
66
|
+
number: {
|
|
67
|
+
operator: filter.number.operator,
|
|
68
|
+
value: filter.number.value,
|
|
69
|
+
},
|
|
70
|
+
freeText: null,
|
|
71
|
+
period: null,
|
|
72
|
+
date: null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (correspondingQuestion.type === types_1.FormContentQuestionType.FREE_TEXT &&
|
|
76
|
+
filter.freeText) {
|
|
77
|
+
return {
|
|
78
|
+
questionId: filter.questionId,
|
|
79
|
+
freeText: {
|
|
80
|
+
contains: filter.freeText.contains || '',
|
|
81
|
+
},
|
|
82
|
+
number: null,
|
|
83
|
+
period: null,
|
|
84
|
+
date: null,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
})),
|
|
89
|
+
};
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
25
92
|
return {
|
|
26
|
-
id
|
|
27
|
-
ask
|
|
28
|
-
description
|
|
29
|
-
isRequired
|
|
30
|
-
allowFileUpload
|
|
31
|
-
formDocumentIds:
|
|
32
|
-
formCodeBlockIds:
|
|
93
|
+
id,
|
|
94
|
+
ask,
|
|
95
|
+
description,
|
|
96
|
+
isRequired,
|
|
97
|
+
allowFileUpload,
|
|
98
|
+
formDocumentIds: formDocumentIds || null,
|
|
99
|
+
formCodeBlockIds: formCodeBlockIds || null,
|
|
100
|
+
...(displayCondition && {
|
|
101
|
+
displayCondition: cleanedDisplayCondition,
|
|
102
|
+
}),
|
|
33
103
|
};
|
|
34
104
|
};
|
|
35
105
|
exports.formContentQuestionMapper = formContentQuestionMapper;
|
|
36
|
-
const mapQuestionByType = (question) => {
|
|
106
|
+
const mapQuestionByType = (question, allQuestions) => {
|
|
37
107
|
switch (question.type) {
|
|
38
108
|
case types_1.FormContentQuestionType.DATE:
|
|
39
109
|
case types_1.FormContentQuestionType.PERIOD:
|
|
40
110
|
return {
|
|
41
|
-
...(0, exports.formContentQuestionMapper)(question),
|
|
111
|
+
...(0, exports.formContentQuestionMapper)(question, allQuestions),
|
|
42
112
|
type: question.type,
|
|
43
113
|
minDate: question.minDate || null,
|
|
44
114
|
maxDate: question.maxDate || null,
|
|
@@ -48,7 +118,7 @@ const mapQuestionByType = (question) => {
|
|
|
48
118
|
};
|
|
49
119
|
case types_1.FormContentQuestionType.RATE:
|
|
50
120
|
return {
|
|
51
|
-
...(0, exports.formContentQuestionMapper)(question),
|
|
121
|
+
...(0, exports.formContentQuestionMapper)(question, allQuestions),
|
|
52
122
|
type: question.type,
|
|
53
123
|
display: question.display,
|
|
54
124
|
answers: question.answers.map((answer) => ({
|
|
@@ -62,7 +132,7 @@ const mapQuestionByType = (question) => {
|
|
|
62
132
|
};
|
|
63
133
|
case types_1.FormContentQuestionType.NUMBER:
|
|
64
134
|
return {
|
|
65
|
-
...(0, exports.formContentQuestionMapper)(question),
|
|
135
|
+
...(0, exports.formContentQuestionMapper)(question, allQuestions),
|
|
66
136
|
type: question.type,
|
|
67
137
|
minValue: question.minValue || null,
|
|
68
138
|
maxValue: question.maxValue || null,
|
|
@@ -70,13 +140,13 @@ const mapQuestionByType = (question) => {
|
|
|
70
140
|
};
|
|
71
141
|
case types_1.FormContentQuestionType.FREE_TEXT:
|
|
72
142
|
return {
|
|
73
|
-
...(0, exports.formContentQuestionMapper)(question),
|
|
143
|
+
...(0, exports.formContentQuestionMapper)(question, allQuestions),
|
|
74
144
|
type: question.type,
|
|
75
145
|
allowCodeBlock: question.allowCodeBlock || false,
|
|
76
146
|
};
|
|
77
147
|
case types_1.FormContentQuestionType.SELECT:
|
|
78
148
|
return {
|
|
79
|
-
...(0, exports.formContentQuestionMapper)(question),
|
|
149
|
+
...(0, exports.formContentQuestionMapper)(question, allQuestions),
|
|
80
150
|
type: question.type,
|
|
81
151
|
allowMultipleAnswers: question.allowMultipleAnswers || false,
|
|
82
152
|
allowFreeText: question.allowFreeText || false,
|
|
@@ -88,7 +158,7 @@ const mapQuestionByType = (question) => {
|
|
|
88
158
|
};
|
|
89
159
|
case types_1.FormContentQuestionType.CARD:
|
|
90
160
|
return {
|
|
91
|
-
...(0, exports.formContentQuestionMapper)(question),
|
|
161
|
+
...(0, exports.formContentQuestionMapper)(question, allQuestions),
|
|
92
162
|
type: question.type,
|
|
93
163
|
allowMultipleAnswers: question.allowMultipleAnswers || false,
|
|
94
164
|
allowFreeText: question.allowFreeText || false,
|
|
@@ -101,3 +171,80 @@ const mapQuestionByType = (question) => {
|
|
|
101
171
|
}
|
|
102
172
|
};
|
|
103
173
|
exports.mapQuestionByType = mapQuestionByType;
|
|
174
|
+
const shouldDisplayQuestion = (question, content, response) => {
|
|
175
|
+
if (!question.displayCondition || !question.displayCondition.conditions.length) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
const allQuestions = content.pages.flatMap((page) => page.questions);
|
|
179
|
+
const some = question.displayCondition.conditions.some((condition) => {
|
|
180
|
+
if (condition.filters.length === 0) {
|
|
181
|
+
condition.filters.some((filter) => {
|
|
182
|
+
const responseQuestion = response.questions[filter.questionId];
|
|
183
|
+
const question = allQuestions.find((q) => q.id === filter.questionId);
|
|
184
|
+
if (!question || !responseQuestion) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return computeFiler(question, responseQuestion, filter);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
condition.filters.every((filter) => {
|
|
192
|
+
const responseQuestion = response.questions[filter.questionId];
|
|
193
|
+
const question = allQuestions.find((q) => q.id === filter.questionId);
|
|
194
|
+
if (!question || !responseQuestion) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
return computeFiler(question, responseQuestion, filter);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return question.displayCondition.shouldDisplay ? some : !some;
|
|
202
|
+
};
|
|
203
|
+
exports.shouldDisplayQuestion = shouldDisplayQuestion;
|
|
204
|
+
const computeFiler = (question, responseQuestion, filter) => {
|
|
205
|
+
if (!responseQuestion || !question) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
if (question.type === types_1.FormContentQuestionType.DATE && filter.date) {
|
|
209
|
+
return !!((filter.date.before && responseQuestion.date && responseQuestion.date < filter.date.before) ||
|
|
210
|
+
(filter.date.after && responseQuestion.date && responseQuestion.date > filter.date.after));
|
|
211
|
+
}
|
|
212
|
+
if (question.type === types_1.FormContentQuestionType.PERIOD && filter.period) {
|
|
213
|
+
return !!((filter.period.before &&
|
|
214
|
+
responseQuestion.startDate &&
|
|
215
|
+
responseQuestion.startDate < filter.period.before) ||
|
|
216
|
+
(filter.period.after &&
|
|
217
|
+
responseQuestion.endDate &&
|
|
218
|
+
responseQuestion.endDate > filter.period.after));
|
|
219
|
+
}
|
|
220
|
+
if (question.type === types_1.FormContentQuestionType.NUMBER && filter.number) {
|
|
221
|
+
const value = responseQuestion.value;
|
|
222
|
+
if (value === undefined ||
|
|
223
|
+
value === null ||
|
|
224
|
+
filter.number.value === undefined ||
|
|
225
|
+
filter.number.value === null) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
switch (filter.number.operator) {
|
|
229
|
+
case 'eq':
|
|
230
|
+
return value === filter.number.value;
|
|
231
|
+
case 'neq':
|
|
232
|
+
return value !== filter.number.value;
|
|
233
|
+
case 'gt':
|
|
234
|
+
return value > filter.number.value;
|
|
235
|
+
case 'lt':
|
|
236
|
+
return value < filter.number.value;
|
|
237
|
+
case 'gte':
|
|
238
|
+
return value >= filter.number.value;
|
|
239
|
+
case 'lte':
|
|
240
|
+
return value <= filter.number.value;
|
|
241
|
+
default:
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (question.type === types_1.FormContentQuestionType.FREE_TEXT && filter.freeText) {
|
|
246
|
+
return !!(responseQuestion.freeText &&
|
|
247
|
+
responseQuestion.freeText.includes(filter.freeText.contains || ''));
|
|
248
|
+
}
|
|
249
|
+
return false;
|
|
250
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abyss-project/form",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Core package to interact with AbyssForm",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
"core-js": "^3.37.1",
|
|
18
18
|
"dayjs": "^1.10.8",
|
|
19
19
|
"form-data": "^4.0.0",
|
|
20
|
+
"lodash": "^4.17.21",
|
|
20
21
|
"uuid": "^9.0.0"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
24
|
"@types/express-serve-static-core": "^4.17.31",
|
|
25
|
+
"@types/lodash": "^4.17.16",
|
|
24
26
|
"@types/node": "^20.11.0",
|
|
25
27
|
"@types/uuid": "^9.0.0",
|
|
26
28
|
"@typescript-eslint/eslint-plugin": "6.21.0",
|