@abyss-project/form 1.0.3 → 1.0.5

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.
@@ -1,5 +1,6 @@
1
- import { IArchiveFormSubmissionParams, IArchiveFormSubmissionResponse, IGetFormSubmissionParams, IGetFormSubmissionResponse, IPaginateFormSubmissionParams, IPaginateFormSubmissionQuery, IPaginateFormSubmissionResponse, IUpdateFormSubmissionBody, IUpdateFormSubmissionParams, IUpdateFormSubmissionResponse } from '..';
1
+ import { IArchiveFormSubmissionParams, IArchiveFormSubmissionResponse, IGetFormSubmissionParams, IGetFormSubmissionResponse, IGetNextToReviewFormSubmissionParams, IGetNextToReviewFormSubmissionQuery, IGetNextToReviewFormSubmissionResponse, IPaginateFormSubmissionParams, IPaginateFormSubmissionQuery, IPaginateFormSubmissionResponse, IUpdateFormSubmissionBody, IUpdateFormSubmissionParams, IUpdateFormSubmissionResponse } from '..';
2
2
  export declare const getFormSubmission: (params: IGetFormSubmissionParams) => Promise<IGetFormSubmissionResponse>;
3
+ export declare const getNextToReviewFormSubmission: (params: IGetNextToReviewFormSubmissionParams, query: IGetNextToReviewFormSubmissionQuery) => Promise<IGetNextToReviewFormSubmissionResponse>;
3
4
  export declare const updateFormSubmission: (params: IUpdateFormSubmissionParams, body: IUpdateFormSubmissionBody) => Promise<IUpdateFormSubmissionResponse>;
4
5
  export declare const archiveFormSubmission: (params: IArchiveFormSubmissionParams) => Promise<IArchiveFormSubmissionResponse>;
5
6
  export declare const paginateFormSubmission: (params: IPaginateFormSubmissionParams, query: IPaginateFormSubmissionQuery) => Promise<IPaginateFormSubmissionResponse>;
@@ -1,11 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.paginateFormSubmission = exports.archiveFormSubmission = exports.updateFormSubmission = exports.getFormSubmission = void 0;
3
+ exports.paginateFormSubmission = exports.archiveFormSubmission = exports.updateFormSubmission = exports.getNextToReviewFormSubmission = exports.getFormSubmission = void 0;
4
4
  const __1 = require("..");
5
5
  const getFormSubmission = async (params) => {
6
6
  return (await __1.AbyssFormCore.axios.get(`form-submission/${params.formId}/submission/${params.formSubmissionId}`)).data;
7
7
  };
8
8
  exports.getFormSubmission = getFormSubmission;
9
+ const getNextToReviewFormSubmission = async (params, query) => {
10
+ return (await __1.AbyssFormCore.axios.get(`form-submission/${params.formId}/submission/next-to-review`, {
11
+ params: query,
12
+ })).data;
13
+ };
14
+ exports.getNextToReviewFormSubmission = getNextToReviewFormSubmission;
9
15
  const updateFormSubmission = async (params, body) => {
10
16
  return (await __1.AbyssFormCore.axios.put(`form-submission/${params.formId}/submission/${params.formSubmissionId}`, body)).data;
11
17
  };
@@ -9,6 +9,7 @@ export interface IUpdateFormSubmissionParams {
9
9
  }
10
10
  export interface IUpdateFormSubmissionBody {
11
11
  comment?: string;
12
+ reviewed?: boolean;
12
13
  }
13
14
  export interface IGetFormSubmissionParams {
14
15
  formId: string;
@@ -18,3 +19,9 @@ export interface IPaginateFormSubmissionParams {
18
19
  formId: string;
19
20
  }
20
21
  export type IPaginateFormSubmissionQuery = QueryPaginate;
22
+ export interface IGetNextToReviewFormSubmissionParams {
23
+ formId: string;
24
+ }
25
+ export interface IGetNextToReviewFormSubmissionQuery {
26
+ excludeFormSubmissionId?: string;
27
+ }
@@ -1,7 +1,6 @@
1
- import * as core from 'express-serve-static-core';
2
1
  import { FormWebhookType } from '../../../enum';
3
2
  import { QueryPaginate } from '..';
4
- export interface IGetFormWebhookHistoryAdminParams extends core.ParamsDictionary {
3
+ export interface IGetFormWebhookHistoryAdminParams {
5
4
  formWebhookHistoryId: string;
6
5
  }
7
6
  export type IPaginateFormWebhookHistoryAdminQuery = {
@@ -1,11 +1,10 @@
1
- import * as core from 'express-serve-static-core';
2
1
  import { FormWebhookType } from '../../../enum';
3
2
  import { QueryPaginate } from '..';
4
- export interface IGetFormWebhookHistoryParams extends core.ParamsDictionary {
3
+ export interface IGetFormWebhookHistoryParams {
5
4
  formId: string;
6
5
  formWebhookHistoryId: string;
7
6
  }
8
- export interface IPaginateFormWebhookHistoryParams extends core.ParamsDictionary {
7
+ export interface IPaginateFormWebhookHistoryParams {
9
8
  formId: string;
10
9
  }
11
10
  export type IPaginateFormWebhookHistoryQuery = {
@@ -12,3 +12,8 @@ export interface IArchiveFormSubmissionData {
12
12
  formSubmission: IFormSubmission;
13
13
  }
14
14
  export type IArchiveFormSubmissionResponse = IResponse<IArchiveFormSubmissionData>;
15
+ export interface IGetNextToReviewFormSubmissionData {
16
+ formSubmission: IFormSubmission | null;
17
+ remainingCount: number;
18
+ }
19
+ export type IGetNextToReviewFormSubmissionResponse = IResponse<IGetNextToReviewFormSubmissionData>;
@@ -64,6 +64,8 @@ export type FormContentQuestionBaseOptionsDTO = {
64
64
  formDocumentIds: string[] | null;
65
65
  formCodeBlockIds: string[];
66
66
  description: string | null;
67
+ displayCondition?: FormContentQuestionDisplayConditionDTO;
68
+ archivedAt?: Date | null;
67
69
  };
68
70
  export type FormContentQuestionDateOptionsDTO = FormContentQuestionBaseOptionsDTO & {
69
71
  type: FormContentQuestionType.DATE;
@@ -135,7 +137,32 @@ export type FormResponseQuestionDTO = {
135
137
  language: string;
136
138
  content: string;
137
139
  } | null;
140
+ userQuestionRate?: boolean | null;
138
141
  };
139
142
  export type FormResponseDTO = {
140
143
  questions: Record<string, FormResponseQuestionDTO>;
141
144
  };
145
+ export type FormContentQuestionDisplayConditionDTO = {
146
+ shouldDisplay: boolean;
147
+ conditions: {
148
+ operator: 'and' | 'or';
149
+ filters: {
150
+ questionId: string;
151
+ number?: {
152
+ operator?: 'eq' | 'neq' | 'gt' | 'lt' | 'gte' | 'lte';
153
+ value?: number;
154
+ } | null;
155
+ period?: {
156
+ before?: Date | null;
157
+ after?: Date | null;
158
+ } | null;
159
+ date?: {
160
+ before?: Date | null;
161
+ after?: Date | null;
162
+ } | null;
163
+ freeText?: {
164
+ contains?: string | null;
165
+ };
166
+ }[];
167
+ }[];
168
+ };
@@ -14,6 +14,7 @@ export interface IFormSubmission {
14
14
  countRestart: number;
15
15
  formId: string;
16
16
  archivedAt: Date | null;
17
+ reviewedAt: Date | null;
17
18
  updatedAt?: Date;
18
19
  createdAt?: Date;
19
20
  form: IForm;
@@ -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;
@@ -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: question.id,
27
- ask: question.ask,
28
- description: question.description,
29
- isRequired: question.isRequired,
30
- allowFileUpload: question.allowFileUpload,
31
- formDocumentIds: question.formDocumentIds || null,
32
- formCodeBlockIds: question.formCodeBlockIds || null,
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,9 +118,9 @@ 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
- display: question.display,
123
+ display: question.display || 'star',
54
124
  answers: question.answers.map((answer) => ({
55
125
  id: answer.id,
56
126
  content: answer.content,
@@ -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,83 @@ const mapQuestionByType = (question) => {
101
171
  }
102
172
  };
103
173
  exports.mapQuestionByType = mapQuestionByType;
174
+ const shouldDisplayQuestion = (question, content, response) => {
175
+ if (question.archivedAt) {
176
+ return false;
177
+ }
178
+ if (!question.displayCondition || !question.displayCondition.conditions.length) {
179
+ return true;
180
+ }
181
+ const allQuestions = content.pages.flatMap((page) => page.questions);
182
+ const some = question.displayCondition.conditions.some((condition) => {
183
+ if (condition.filters.length === 0) {
184
+ condition.filters.some((filter) => {
185
+ const responseQuestion = response.questions[filter.questionId];
186
+ const question = allQuestions.find((q) => q.id === filter.questionId);
187
+ if (!question || !responseQuestion) {
188
+ return false;
189
+ }
190
+ return computeFiler(question, responseQuestion, filter);
191
+ });
192
+ }
193
+ else {
194
+ condition.filters.every((filter) => {
195
+ const responseQuestion = response.questions[filter.questionId];
196
+ const question = allQuestions.find((q) => q.id === filter.questionId);
197
+ if (!question || !responseQuestion) {
198
+ return false;
199
+ }
200
+ return computeFiler(question, responseQuestion, filter);
201
+ });
202
+ }
203
+ });
204
+ return question.displayCondition.shouldDisplay ? some : !some;
205
+ };
206
+ exports.shouldDisplayQuestion = shouldDisplayQuestion;
207
+ const computeFiler = (question, responseQuestion, filter) => {
208
+ if (!responseQuestion || !question) {
209
+ return false;
210
+ }
211
+ if (question.type === types_1.FormContentQuestionType.DATE && filter.date) {
212
+ return !!((filter.date.before && responseQuestion.date && responseQuestion.date < filter.date.before) ||
213
+ (filter.date.after && responseQuestion.date && responseQuestion.date > filter.date.after));
214
+ }
215
+ if (question.type === types_1.FormContentQuestionType.PERIOD && filter.period) {
216
+ return !!((filter.period.before &&
217
+ responseQuestion.startDate &&
218
+ responseQuestion.startDate < filter.period.before) ||
219
+ (filter.period.after &&
220
+ responseQuestion.endDate &&
221
+ responseQuestion.endDate > filter.period.after));
222
+ }
223
+ if (question.type === types_1.FormContentQuestionType.NUMBER && filter.number) {
224
+ const value = responseQuestion.value;
225
+ if (value === undefined ||
226
+ value === null ||
227
+ filter.number.value === undefined ||
228
+ filter.number.value === null) {
229
+ return false;
230
+ }
231
+ switch (filter.number.operator) {
232
+ case 'eq':
233
+ return value === filter.number.value;
234
+ case 'neq':
235
+ return value !== filter.number.value;
236
+ case 'gt':
237
+ return value > filter.number.value;
238
+ case 'lt':
239
+ return value < filter.number.value;
240
+ case 'gte':
241
+ return value >= filter.number.value;
242
+ case 'lte':
243
+ return value <= filter.number.value;
244
+ default:
245
+ return false;
246
+ }
247
+ }
248
+ if (question.type === types_1.FormContentQuestionType.FREE_TEXT && filter.freeText) {
249
+ return !!(responseQuestion.freeText &&
250
+ responseQuestion.freeText.includes(filter.freeText.contains || ''));
251
+ }
252
+ return false;
253
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abyss-project/form",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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",