@flusys/nestjs-form-builder 1.1.0-beta → 1.1.0
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.
- package/README.md +723 -0
- package/cjs/controllers/form-result.controller.js +67 -5
- package/cjs/controllers/form.controller.js +48 -15
- package/cjs/docs/form-builder-swagger.config.js +6 -100
- package/cjs/dtos/form-result.dto.js +6 -93
- package/cjs/dtos/form.dto.js +21 -163
- package/cjs/entities/form-with-company.entity.js +12 -2
- package/cjs/entities/form.entity.js +103 -3
- package/cjs/entities/index.js +28 -16
- package/cjs/index.js +1 -0
- package/cjs/interfaces/form-result.interface.js +1 -6
- package/cjs/modules/form-builder.module.js +57 -83
- package/cjs/services/form-builder-config.service.js +6 -16
- package/cjs/services/form-builder-datasource.provider.js +17 -63
- package/cjs/services/form-result.service.js +107 -181
- package/cjs/services/form.service.js +56 -72
- package/cjs/utils/computed-field.utils.js +17 -29
- package/cjs/utils/permission.utils.js +11 -16
- package/controllers/form-result.controller.d.ts +10 -12
- package/dtos/form-result.dto.d.ts +2 -19
- package/dtos/form.dto.d.ts +6 -32
- package/entities/form-with-company.entity.d.ts +2 -2
- package/entities/form.entity.d.ts +12 -2
- package/entities/index.d.ts +7 -2
- package/fesm/controllers/form-result.controller.js +69 -7
- package/fesm/controllers/form.controller.js +50 -17
- package/fesm/docs/form-builder-swagger.config.js +6 -100
- package/fesm/dtos/form-result.dto.js +9 -99
- package/fesm/dtos/form.dto.js +22 -165
- package/fesm/entities/form-with-company.entity.js +12 -2
- package/fesm/entities/form.entity.js +104 -4
- package/fesm/entities/index.js +18 -24
- package/fesm/index.js +2 -0
- package/fesm/modules/form-builder.module.js +57 -83
- package/fesm/services/form-builder-config.service.js +6 -16
- package/fesm/services/form-builder-datasource.provider.js +17 -63
- package/fesm/services/form-result.service.js +107 -181
- package/fesm/services/form.service.js +56 -72
- package/fesm/utils/computed-field.utils.js +17 -29
- package/fesm/utils/permission.utils.js +2 -9
- package/index.d.ts +1 -0
- package/interfaces/form-builder-module.interface.d.ts +4 -7
- package/interfaces/form-result.interface.d.ts +2 -9
- package/interfaces/form.interface.d.ts +2 -10
- package/modules/form-builder.module.d.ts +4 -3
- package/package.json +3 -3
- package/services/form-builder-config.service.d.ts +5 -3
- package/services/form-builder-datasource.provider.d.ts +3 -6
- package/services/form-result.service.d.ts +5 -0
- package/services/form.service.d.ts +13 -10
- package/utils/permission.utils.d.ts +0 -2
- package/cjs/entities/form-base.entity.js +0 -113
- package/entities/form-base.entity.d.ts +0 -13
- package/fesm/entities/form-base.entity.js +0 -106
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "FormService", {
|
|
|
10
10
|
});
|
|
11
11
|
const _classes = require("@flusys/nestjs-shared/classes");
|
|
12
12
|
const _modules = require("@flusys/nestjs-shared/modules");
|
|
13
|
+
const _utils = require("@flusys/nestjs-shared/utils");
|
|
13
14
|
const _common = require("@nestjs/common");
|
|
14
15
|
const _typeorm = require("typeorm");
|
|
15
16
|
const _formbuilderconfigservice = require("./form-builder-config.service");
|
|
@@ -45,17 +46,11 @@ function _ts_param(paramIndex, decorator) {
|
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
48
|
-
|
|
49
|
-
* Resolve entity class for this service
|
|
50
|
-
* @returns Form or FormWithCompany based on configuration
|
|
51
|
-
*/ resolveEntity() {
|
|
49
|
+
resolveEntity() {
|
|
52
50
|
const enableCompanyFeature = this.formBuilderConfig.isCompanyFeatureEnabled();
|
|
53
51
|
return enableCompanyFeature ? _entities.FormWithCompany : _entities.Form;
|
|
54
52
|
}
|
|
55
|
-
|
|
56
|
-
* Get DataSource provider for this service
|
|
57
|
-
* @returns FormBuilderDataSourceProvider instance
|
|
58
|
-
*/ getDataSourceProvider() {
|
|
53
|
+
getDataSourceProvider() {
|
|
59
54
|
return this.dataSourceProvider;
|
|
60
55
|
}
|
|
61
56
|
// Entity Conversion
|
|
@@ -94,7 +89,7 @@ let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
|
94
89
|
form.companyId = dto.companyId;
|
|
95
90
|
}
|
|
96
91
|
// If companyId is not in form at all, set from user
|
|
97
|
-
if (
|
|
92
|
+
if (form.companyId === undefined) {
|
|
98
93
|
form.companyId = user?.companyId ?? null;
|
|
99
94
|
}
|
|
100
95
|
} else {
|
|
@@ -132,22 +127,18 @@ let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
|
132
127
|
isRaw: false
|
|
133
128
|
};
|
|
134
129
|
}
|
|
135
|
-
|
|
136
|
-
* Override: Extra query manipulation - Auto-filter by user's company
|
|
137
|
-
*/ async getExtraManipulateQuery(query, filterDto, user) {
|
|
130
|
+
async getExtraManipulateQuery(query, filterDto, user) {
|
|
138
131
|
const result = await super.getExtraManipulateQuery(query, filterDto, user);
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
});
|
|
145
|
-
}
|
|
132
|
+
// Apply company filter using shared utility
|
|
133
|
+
(0, _utils.applyCompanyFilter)(query, {
|
|
134
|
+
isCompanyFeatureEnabled: this.formBuilderConfig.isCompanyFeatureEnabled(),
|
|
135
|
+
entityAlias: 'form'
|
|
136
|
+
}, user);
|
|
146
137
|
return result;
|
|
147
138
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
139
|
+
convertEntityToResponseDto(entity, _isRaw) {
|
|
140
|
+
// Type guard for company-enabled entity
|
|
141
|
+
const entityWithCompany = entity;
|
|
151
142
|
return {
|
|
152
143
|
id: entity.id,
|
|
153
144
|
name: entity.name,
|
|
@@ -158,7 +149,7 @@ let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
|
158
149
|
accessType: entity.accessType,
|
|
159
150
|
actionGroups: entity.actionGroups,
|
|
160
151
|
isActive: entity.isActive,
|
|
161
|
-
companyId:
|
|
152
|
+
companyId: entityWithCompany.companyId ?? null,
|
|
162
153
|
metadata: entity.metadata,
|
|
163
154
|
createdAt: entity.createdAt,
|
|
164
155
|
updatedAt: entity.updatedAt,
|
|
@@ -168,33 +159,37 @@ let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
|
168
159
|
deletedById: entity.deletedById
|
|
169
160
|
};
|
|
170
161
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
162
|
+
toPublicForm(form) {
|
|
163
|
+
return {
|
|
164
|
+
id: form.id,
|
|
165
|
+
name: form.name,
|
|
166
|
+
description: form.description,
|
|
167
|
+
schema: form.schema,
|
|
168
|
+
schemaVersion: form.schemaVersion
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async findPublicActiveForm(where) {
|
|
175
172
|
await this.ensureRepositoryInitialized();
|
|
176
|
-
|
|
173
|
+
return this.repository.findOne({
|
|
177
174
|
where: {
|
|
178
|
-
|
|
175
|
+
...where,
|
|
179
176
|
accessType: _formaccesstypeenum.FormAccessType.PUBLIC,
|
|
180
177
|
isActive: true,
|
|
181
178
|
deletedAt: (0, _typeorm.IsNull)()
|
|
182
179
|
}
|
|
183
180
|
});
|
|
181
|
+
}
|
|
182
|
+
// Public Form Access
|
|
183
|
+
async getPublicForm(formId) {
|
|
184
|
+
const form = await this.findPublicActiveForm({
|
|
185
|
+
id: formId
|
|
186
|
+
});
|
|
184
187
|
if (!form) {
|
|
185
188
|
throw new _common.NotFoundException('Form not found or not available for public access');
|
|
186
189
|
}
|
|
187
|
-
return
|
|
188
|
-
id: form.id,
|
|
189
|
-
name: form.name,
|
|
190
|
-
description: form.description,
|
|
191
|
-
schema: form.schema,
|
|
192
|
-
schemaVersion: form.schemaVersion
|
|
193
|
-
};
|
|
190
|
+
return this.toPublicForm(form);
|
|
194
191
|
}
|
|
195
|
-
|
|
196
|
-
* Get form for submission with access validation
|
|
197
|
-
*/ async getFormForSubmission(formId, user) {
|
|
192
|
+
async getFormForSubmission(formId, user) {
|
|
198
193
|
await this.ensureRepositoryInitialized();
|
|
199
194
|
const form = await this.repository.findOne({
|
|
200
195
|
where: {
|
|
@@ -207,27 +202,19 @@ let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
|
207
202
|
throw new _common.NotFoundException('Form not found or inactive');
|
|
208
203
|
}
|
|
209
204
|
// Access validation based on accessType
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (!user) {
|
|
220
|
-
throw new _common.UnauthorizedException('Authentication required to submit this form');
|
|
221
|
-
}
|
|
222
|
-
// Permission check is handled by the controller/guard
|
|
223
|
-
return form;
|
|
224
|
-
default:
|
|
225
|
-
throw new _common.BadRequestException('Invalid access type');
|
|
205
|
+
if (form.accessType === _formaccesstypeenum.FormAccessType.PUBLIC) {
|
|
206
|
+
return form; // Anyone can access
|
|
207
|
+
}
|
|
208
|
+
// All non-public forms require authentication
|
|
209
|
+
if (!user) {
|
|
210
|
+
throw new _common.UnauthorizedException('Authentication required to submit this form');
|
|
211
|
+
}
|
|
212
|
+
if (form.accessType === _formaccesstypeenum.FormAccessType.AUTHENTICATED || form.accessType === _formaccesstypeenum.FormAccessType.ACTION_GROUP) {
|
|
213
|
+
return form; // Permission check for ACTION_GROUP is handled by the controller/guard
|
|
226
214
|
}
|
|
215
|
+
throw new _common.BadRequestException('Invalid access type');
|
|
227
216
|
}
|
|
228
|
-
|
|
229
|
-
* Get form by slug
|
|
230
|
-
*/ async getBySlug(slug) {
|
|
217
|
+
async getBySlug(slug) {
|
|
231
218
|
await this.ensureRepositoryInitialized();
|
|
232
219
|
const form = await this.repository.findOne({
|
|
233
220
|
where: {
|
|
@@ -238,9 +225,15 @@ let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
|
238
225
|
return form ? this.convertEntityToResponseDto(form, false) : null;
|
|
239
226
|
}
|
|
240
227
|
/**
|
|
241
|
-
* Get form
|
|
242
|
-
* Returns
|
|
243
|
-
*/ async
|
|
228
|
+
* Get public form by slug (no authentication required)
|
|
229
|
+
* Returns null if form doesn't exist, is not public, or is inactive
|
|
230
|
+
*/ async getPublicFormBySlug(slug) {
|
|
231
|
+
const form = await this.findPublicActiveForm({
|
|
232
|
+
slug
|
|
233
|
+
});
|
|
234
|
+
return form ? this.toPublicForm(form) : null;
|
|
235
|
+
}
|
|
236
|
+
async getFormAccessInfo(formId) {
|
|
244
237
|
await this.ensureRepositoryInitialized();
|
|
245
238
|
const form = await this.repository.findOne({
|
|
246
239
|
where: {
|
|
@@ -268,10 +261,7 @@ let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
|
268
261
|
isActive: form.isActive
|
|
269
262
|
};
|
|
270
263
|
}
|
|
271
|
-
|
|
272
|
-
* Get form for authenticated submission
|
|
273
|
-
* Returns full form for users who are logged in
|
|
274
|
-
*/ async getAuthenticatedForm(formId, user) {
|
|
264
|
+
async getAuthenticatedForm(formId, user) {
|
|
275
265
|
const form = await this.getFormForSubmission(formId, user);
|
|
276
266
|
// For action_group access, check permissions from cache
|
|
277
267
|
if (form.accessType === _formaccesstypeenum.FormAccessType.ACTION_GROUP && form.actionGroups?.length) {
|
|
@@ -280,13 +270,7 @@ let FormService = class FormService extends _classes.RequestScopedApiService {
|
|
|
280
270
|
throw new _common.ForbiddenException('You do not have permission to access this form');
|
|
281
271
|
}
|
|
282
272
|
}
|
|
283
|
-
return
|
|
284
|
-
id: form.id,
|
|
285
|
-
name: form.name,
|
|
286
|
-
description: form.description,
|
|
287
|
-
schema: form.schema,
|
|
288
|
-
schemaVersion: form.schemaVersion
|
|
289
|
-
};
|
|
273
|
+
return this.toPublicForm(form);
|
|
290
274
|
}
|
|
291
275
|
constructor(cacheManager, utilsService, formBuilderConfig, dataSourceProvider){
|
|
292
276
|
super('form', null, cacheManager, utilsService, FormService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "formBuilderConfig", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.formBuilderConfig = formBuilderConfig, this.dataSourceProvider = dataSourceProvider;
|
|
@@ -23,16 +23,22 @@ Object.defineProperty(exports, "calculateComputedFields", {
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
// ============================================================================
|
|
26
|
-
// Type Guards
|
|
26
|
+
// Type Guards & Helpers
|
|
27
27
|
// ============================================================================
|
|
28
|
+
function isObject(value) {
|
|
29
|
+
return typeof value === 'object' && value !== null;
|
|
30
|
+
}
|
|
28
31
|
function isDirectValueConfig(config) {
|
|
29
|
-
return
|
|
32
|
+
return isObject(config) && config.type === 'direct' && 'value' in config;
|
|
30
33
|
}
|
|
31
34
|
function isFieldReferenceConfig(config) {
|
|
32
|
-
return
|
|
35
|
+
return isObject(config) && config.type === 'field_reference' && 'fieldId' in config;
|
|
33
36
|
}
|
|
34
37
|
function isArithmeticConfig(config) {
|
|
35
|
-
return
|
|
38
|
+
return isObject(config) && config.type === 'arithmetic' && 'operation' in config && 'operands' in config && Array.isArray(config.operands);
|
|
39
|
+
}
|
|
40
|
+
function isEmptyValue(value) {
|
|
41
|
+
return value === null || value === undefined || value === '' || Array.isArray(value) && value.length === 0;
|
|
36
42
|
}
|
|
37
43
|
/**
|
|
38
44
|
* Normalize operator to uppercase string (handles both enum and string values)
|
|
@@ -95,9 +101,9 @@ function calculateComputedFields(formData, computedFields) {
|
|
|
95
101
|
case 'not_equals':
|
|
96
102
|
return fieldValue !== compareValue && String(fieldValue) !== String(compareValue);
|
|
97
103
|
case 'is_empty':
|
|
98
|
-
return
|
|
104
|
+
return isEmptyValue(fieldValue);
|
|
99
105
|
case 'is_not_empty':
|
|
100
|
-
return
|
|
106
|
+
return !isEmptyValue(fieldValue);
|
|
101
107
|
case 'contains':
|
|
102
108
|
return String(fieldValue).includes(String(compareValue));
|
|
103
109
|
case 'not_contains':
|
|
@@ -123,25 +129,11 @@ function calculateComputedFields(formData, computedFields) {
|
|
|
123
129
|
case 'is_not_checked':
|
|
124
130
|
return fieldValue === false || fieldValue === 'false' || fieldValue === 0 || !fieldValue;
|
|
125
131
|
case 'is_any_of':
|
|
126
|
-
if (Array.isArray(compareValue)) {
|
|
127
|
-
return compareValue.includes(fieldValue);
|
|
128
|
-
}
|
|
129
|
-
return false;
|
|
130
|
-
case 'is_none_of':
|
|
131
|
-
if (Array.isArray(compareValue)) {
|
|
132
|
-
return !compareValue.includes(fieldValue);
|
|
133
|
-
}
|
|
134
|
-
return true;
|
|
135
132
|
case 'in':
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
return false;
|
|
133
|
+
return Array.isArray(compareValue) && compareValue.includes(fieldValue);
|
|
134
|
+
case 'is_none_of':
|
|
140
135
|
case 'not_in':
|
|
141
|
-
|
|
142
|
-
return !compareValue.includes(fieldValue);
|
|
143
|
-
}
|
|
144
|
-
return true;
|
|
136
|
+
return !Array.isArray(compareValue) || !compareValue.includes(fieldValue);
|
|
145
137
|
default:
|
|
146
138
|
return false;
|
|
147
139
|
}
|
|
@@ -183,8 +175,10 @@ function calculateComputedFields(formData, computedFields) {
|
|
|
183
175
|
}
|
|
184
176
|
switch(config.operation){
|
|
185
177
|
case 'sum':
|
|
178
|
+
case 'increment':
|
|
186
179
|
return values.reduce((acc, val)=>acc + val, 0);
|
|
187
180
|
case 'subtract':
|
|
181
|
+
case 'decrement':
|
|
188
182
|
return values.reduce((acc, val, idx)=>idx === 0 ? val : acc - val, 0);
|
|
189
183
|
case 'multiply':
|
|
190
184
|
return values.reduce((acc, val)=>acc * val, 1);
|
|
@@ -199,12 +193,6 @@ function calculateComputedFields(formData, computedFields) {
|
|
|
199
193
|
return Math.min(...values);
|
|
200
194
|
case 'max':
|
|
201
195
|
return Math.max(...values);
|
|
202
|
-
case 'increment':
|
|
203
|
-
// First value is the base, rest are increments
|
|
204
|
-
return values.reduce((acc, val)=>acc + val, 0);
|
|
205
|
-
case 'decrement':
|
|
206
|
-
// First value is the base, rest are decrements
|
|
207
|
-
return values.reduce((acc, val, idx)=>idx === 0 ? val : acc - val, 0);
|
|
208
196
|
default:
|
|
209
197
|
return null;
|
|
210
198
|
}
|
|
@@ -2,26 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get buildPermissionCacheKey () {
|
|
13
|
-
return buildPermissionCacheKey;
|
|
14
|
-
},
|
|
15
|
-
get getUserPermissionsFromCache () {
|
|
16
|
-
return getUserPermissionsFromCache;
|
|
17
|
-
},
|
|
18
|
-
get validateUserPermissions () {
|
|
5
|
+
Object.defineProperty(exports, "validateUserPermissions", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
19
8
|
return validateUserPermissions;
|
|
20
9
|
}
|
|
21
10
|
});
|
|
22
11
|
const _common = require("@nestjs/common");
|
|
23
12
|
const CACHE_PREFIX = 'permissions';
|
|
24
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Build permission cache key for a user
|
|
15
|
+
* Uses same cache key format as PermissionGuard in nestjs-shared
|
|
16
|
+
*/ function buildPermissionCacheKey(user, enableCompanyFeature) {
|
|
25
17
|
if (enableCompanyFeature && user.companyId) {
|
|
26
18
|
// Company-based permissions
|
|
27
19
|
return `${CACHE_PREFIX}:company:${user.companyId}:branch:${user.branchId || 'null'}:user:${user.id}`;
|
|
@@ -29,7 +21,10 @@ function buildPermissionCacheKey(user, enableCompanyFeature) {
|
|
|
29
21
|
// User-based permissions
|
|
30
22
|
return `${CACHE_PREFIX}:user:${user.id}`;
|
|
31
23
|
}
|
|
32
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Get user permissions from cache
|
|
26
|
+
* Fail-closed: if cache fails, deny access rather than allowing potentially unauthorized access
|
|
27
|
+
*/ async function getUserPermissionsFromCache(user, cacheManager, enableCompanyFeature, logger) {
|
|
33
28
|
const cacheKey = buildPermissionCacheKey(user, enableCompanyFeature);
|
|
34
29
|
try {
|
|
35
30
|
const permissions = await cacheManager.get(cacheKey);
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import { ListResponseDto, SingleResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
1
2
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
2
3
|
import { CreateFormResultDto, FormResultResponseDto, GetMyDraftDto, GetResultsByFormDto, SubmitFormDto, UpdateDraftDto, UpdateFormResultDto } from '../dtos';
|
|
3
4
|
import { FormResultService } from '../services/form-result.service';
|
|
4
5
|
declare const FormResultController_base: abstract new (service: FormResultService) => {
|
|
5
6
|
service: FormResultService;
|
|
6
|
-
insert(addDto: CreateFormResultDto, user: ILoggedUserInfo | null): Promise<
|
|
7
|
-
insertMany(addDto: CreateFormResultDto[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared").BulkResponseDto<FormResultResponseDto>>;
|
|
8
|
-
getById(id: string, body: import("@flusys/nestjs-shared").GetByIdBodyDto, user: ILoggedUserInfo | null): Promise<
|
|
9
|
-
update(updateDto: UpdateFormResultDto, user: ILoggedUserInfo | null): Promise<
|
|
10
|
-
updateMany(updateDtos: UpdateFormResultDto[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared").BulkResponseDto<FormResultResponseDto>>;
|
|
11
|
-
getAll(filterAndPaginationDto: import("@flusys/nestjs-shared").FilterAndPaginationDto, user: ILoggedUserInfo | null, search?: string): Promise<
|
|
12
|
-
delete(deleteDto: import("@flusys/nestjs-shared").DeleteDto, user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared").MessageResponseDto>;
|
|
7
|
+
insert(addDto: CreateFormResultDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<FormResultResponseDto>>;
|
|
8
|
+
insertMany(addDto: CreateFormResultDto[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").BulkResponseDto<FormResultResponseDto>>;
|
|
9
|
+
getById(id: string, body: import("@flusys/nestjs-shared/dtos").GetByIdBodyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<FormResultResponseDto>>;
|
|
10
|
+
update(updateDto: UpdateFormResultDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<FormResultResponseDto>>;
|
|
11
|
+
updateMany(updateDtos: UpdateFormResultDto[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").BulkResponseDto<FormResultResponseDto>>;
|
|
12
|
+
getAll(filterAndPaginationDto: import("@flusys/nestjs-shared/dtos").FilterAndPaginationDto, user: ILoggedUserInfo | null, search?: string): Promise<ListResponseDto<FormResultResponseDto>>;
|
|
13
|
+
delete(deleteDto: import("@flusys/nestjs-shared/dtos").DeleteDto, user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").MessageResponseDto>;
|
|
13
14
|
};
|
|
14
15
|
export declare class FormResultController extends FormResultController_base {
|
|
15
16
|
formResultService: FormResultService;
|
|
@@ -18,10 +19,7 @@ export declare class FormResultController extends FormResultController_base {
|
|
|
18
19
|
submitPublicForm(dto: SubmitFormDto): Promise<FormResultResponseDto>;
|
|
19
20
|
getMyDraft(dto: GetMyDraftDto, user: ILoggedUserInfo): Promise<FormResultResponseDto | null>;
|
|
20
21
|
updateDraft(dto: UpdateDraftDto, user: ILoggedUserInfo): Promise<FormResultResponseDto>;
|
|
21
|
-
getByFormId(dto: GetResultsByFormDto, user: ILoggedUserInfo): Promise<
|
|
22
|
-
|
|
23
|
-
total: number;
|
|
24
|
-
}>;
|
|
25
|
-
hasUserSubmitted(dto: GetMyDraftDto, user: ILoggedUserInfo): Promise<boolean>;
|
|
22
|
+
getByFormId(dto: GetResultsByFormDto, user: ILoggedUserInfo): Promise<ListResponseDto<FormResultResponseDto>>;
|
|
23
|
+
hasUserSubmitted(dto: GetMyDraftDto, user: ILoggedUserInfo): Promise<SingleResponseDto<boolean>>;
|
|
26
24
|
}
|
|
27
25
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { IdentityResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
1
2
|
export declare class SubmitFormDto {
|
|
2
|
-
[key: string]: unknown;
|
|
3
3
|
formId: string;
|
|
4
4
|
data: Record<string, unknown>;
|
|
5
5
|
isDraft?: boolean;
|
|
@@ -12,31 +12,20 @@ export declare class CreateFormResultDto extends SubmitFormDto {
|
|
|
12
12
|
}
|
|
13
13
|
declare const UpdateFormResultDto_base: import("@nestjs/common").Type<Partial<CreateFormResultDto>>;
|
|
14
14
|
export declare class UpdateFormResultDto extends UpdateFormResultDto_base {
|
|
15
|
-
[key: string]: unknown;
|
|
16
15
|
id: string;
|
|
17
16
|
}
|
|
18
|
-
export declare class FormResultQueryDto {
|
|
19
|
-
[key: string]: unknown;
|
|
20
|
-
formId?: string;
|
|
21
|
-
isDraft?: boolean;
|
|
22
|
-
submittedById?: string;
|
|
23
|
-
}
|
|
24
17
|
export declare class GetMyDraftDto {
|
|
25
|
-
[key: string]: unknown;
|
|
26
18
|
formId: string;
|
|
27
19
|
}
|
|
28
20
|
export declare class UpdateDraftDto extends SubmitFormDto {
|
|
29
21
|
draftId: string;
|
|
30
22
|
}
|
|
31
23
|
export declare class GetResultsByFormDto {
|
|
32
|
-
[key: string]: unknown;
|
|
33
24
|
formId: string;
|
|
34
25
|
page?: number;
|
|
35
26
|
pageSize?: number;
|
|
36
27
|
}
|
|
37
|
-
export declare class FormResultResponseDto {
|
|
38
|
-
[key: string]: unknown;
|
|
39
|
-
id: string;
|
|
28
|
+
export declare class FormResultResponseDto extends IdentityResponseDto {
|
|
40
29
|
formId: string;
|
|
41
30
|
schemaVersionSnapshot: Record<string, unknown>;
|
|
42
31
|
schemaVersion: number;
|
|
@@ -45,11 +34,5 @@ export declare class FormResultResponseDto {
|
|
|
45
34
|
submittedAt: Date;
|
|
46
35
|
isDraft: boolean;
|
|
47
36
|
metadata: Record<string, unknown> | null;
|
|
48
|
-
createdAt: Date;
|
|
49
|
-
updatedAt: Date;
|
|
50
|
-
deletedAt: Date | null;
|
|
51
|
-
createdById: string | null;
|
|
52
|
-
updatedById: string | null;
|
|
53
|
-
deletedById: string | null;
|
|
54
37
|
}
|
|
55
38
|
export {};
|
package/dtos/form.dto.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FormAccessType } from '../enums/form-access-type.enum';
|
|
2
|
+
import { IdentityResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
2
3
|
export declare class CreateFormDto {
|
|
3
|
-
[key: string]: unknown;
|
|
4
4
|
name: string;
|
|
5
5
|
description?: string;
|
|
6
6
|
slug?: string;
|
|
@@ -13,19 +13,10 @@ export declare class CreateFormDto {
|
|
|
13
13
|
}
|
|
14
14
|
declare const UpdateFormDto_base: import("@nestjs/common").Type<Partial<CreateFormDto>>;
|
|
15
15
|
export declare class UpdateFormDto extends UpdateFormDto_base {
|
|
16
|
-
[key: string]: unknown;
|
|
17
16
|
id: string;
|
|
18
17
|
schemaVersion?: number;
|
|
19
18
|
}
|
|
20
|
-
export declare class
|
|
21
|
-
[key: string]: unknown;
|
|
22
|
-
companyId?: string;
|
|
23
|
-
isActive?: boolean;
|
|
24
|
-
accessType?: FormAccessType;
|
|
25
|
-
}
|
|
26
|
-
export declare class FormResponseDto {
|
|
27
|
-
[key: string]: unknown;
|
|
28
|
-
id: string;
|
|
19
|
+
export declare class FormResponseDto extends IdentityResponseDto {
|
|
29
20
|
name: string;
|
|
30
21
|
description: string | null;
|
|
31
22
|
slug: string | null;
|
|
@@ -36,28 +27,11 @@ export declare class FormResponseDto {
|
|
|
36
27
|
companyId: string | null;
|
|
37
28
|
isActive: boolean;
|
|
38
29
|
metadata: Record<string, unknown> | null;
|
|
39
|
-
createdAt: Date;
|
|
40
|
-
updatedAt: Date;
|
|
41
|
-
deletedAt: Date | null;
|
|
42
|
-
createdById: string | null;
|
|
43
|
-
updatedById: string | null;
|
|
44
|
-
deletedById: string | null;
|
|
45
30
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
id: string;
|
|
49
|
-
name: string;
|
|
50
|
-
description: string | null;
|
|
51
|
-
schema: Record<string, unknown>;
|
|
52
|
-
schemaVersion: number;
|
|
31
|
+
declare const PublicFormResponseDto_base: import("@nestjs/common").Type<Pick<FormResponseDto, "description" | "name" | "schema" | "id" | "schemaVersion">>;
|
|
32
|
+
export declare class PublicFormResponseDto extends PublicFormResponseDto_base {
|
|
53
33
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
id: string;
|
|
57
|
-
name: string;
|
|
58
|
-
description: string | null;
|
|
59
|
-
accessType: FormAccessType;
|
|
60
|
-
actionGroups: string[] | null;
|
|
61
|
-
isActive: boolean;
|
|
34
|
+
declare const FormAccessInfoResponseDto_base: import("@nestjs/common").Type<Pick<FormResponseDto, "description" | "name" | "accessType" | "actionGroups" | "isActive" | "id">>;
|
|
35
|
+
export declare class FormAccessInfoResponseDto extends FormAccessInfoResponseDto_base {
|
|
62
36
|
}
|
|
63
37
|
export {};
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { Identity } from '@flusys/nestjs-shared/entities';
|
|
2
|
+
import { FormAccessType } from '../enums/form-access-type.enum';
|
|
3
|
+
export declare class Form extends Identity {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string | null;
|
|
6
|
+
slug: string | null;
|
|
7
|
+
schema: Record<string, unknown>;
|
|
8
|
+
schemaVersion: number;
|
|
9
|
+
accessType: FormAccessType;
|
|
10
|
+
actionGroups: string[] | null;
|
|
11
|
+
isActive: boolean;
|
|
12
|
+
metadata: Record<string, unknown> | null;
|
|
3
13
|
}
|
package/entities/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
export * from './form-base.entity';
|
|
2
1
|
export * from './form.entity';
|
|
3
|
-
export * from './form-with-company.entity';
|
|
4
2
|
export * from './form-result.entity';
|
|
3
|
+
export * from './form-with-company.entity';
|
|
4
|
+
import { Form } from './form.entity';
|
|
5
|
+
import { FormResult } from './form-result.entity';
|
|
6
|
+
import { FormWithCompany } from './form-with-company.entity';
|
|
7
|
+
export declare const FormCoreEntities: (typeof Form | typeof FormResult)[];
|
|
8
|
+
export declare const FormCompanyEntities: (typeof FormResult | typeof FormWithCompany)[];
|
|
5
9
|
export declare function getFormBuilderEntitiesByConfig(enableCompanyFeature: boolean): any[];
|
|
10
|
+
export { Form as FormBase } from './form.entity';
|
|
@@ -25,8 +25,8 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import { createApiController } from '@flusys/nestjs-shared/classes';
|
|
29
|
-
import { CurrentUser, Public } from '@flusys/nestjs-shared/decorators';
|
|
28
|
+
import { createApiController, FORM_RESULT_PERMISSIONS } from '@flusys/nestjs-shared/classes';
|
|
29
|
+
import { CurrentUser, Public, RequirePermission } from '@flusys/nestjs-shared/decorators';
|
|
30
30
|
import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
|
|
31
31
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
32
32
|
import { Body, Controller, HttpCode, HttpStatus, Inject, Post, UseGuards } from '@nestjs/common';
|
|
@@ -34,7 +34,50 @@ import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
|
|
34
34
|
import { CreateFormResultDto, FormResultResponseDto, GetMyDraftDto, GetResultsByFormDto, SubmitFormDto, UpdateDraftDto, UpdateFormResultDto } from '../dtos';
|
|
35
35
|
import { FormResultService } from '../services/form-result.service';
|
|
36
36
|
export class FormResultController extends createApiController(CreateFormResultDto, UpdateFormResultDto, FormResultResponseDto, {
|
|
37
|
-
security:
|
|
37
|
+
security: {
|
|
38
|
+
insert: {
|
|
39
|
+
level: 'permission',
|
|
40
|
+
permissions: [
|
|
41
|
+
FORM_RESULT_PERMISSIONS.CREATE
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
insertMany: {
|
|
45
|
+
level: 'permission',
|
|
46
|
+
permissions: [
|
|
47
|
+
FORM_RESULT_PERMISSIONS.CREATE
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
getById: {
|
|
51
|
+
level: 'permission',
|
|
52
|
+
permissions: [
|
|
53
|
+
FORM_RESULT_PERMISSIONS.READ
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
getAll: {
|
|
57
|
+
level: 'permission',
|
|
58
|
+
permissions: [
|
|
59
|
+
FORM_RESULT_PERMISSIONS.READ
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
update: {
|
|
63
|
+
level: 'permission',
|
|
64
|
+
permissions: [
|
|
65
|
+
FORM_RESULT_PERMISSIONS.UPDATE
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
updateMany: {
|
|
69
|
+
level: 'permission',
|
|
70
|
+
permissions: [
|
|
71
|
+
FORM_RESULT_PERMISSIONS.UPDATE
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
delete: {
|
|
75
|
+
level: 'permission',
|
|
76
|
+
permissions: [
|
|
77
|
+
FORM_RESULT_PERMISSIONS.DELETE
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
38
81
|
}) {
|
|
39
82
|
// Submit Endpoints
|
|
40
83
|
/**
|
|
@@ -63,16 +106,34 @@ export class FormResultController extends createApiController(CreateFormResultDt
|
|
|
63
106
|
/**
|
|
64
107
|
* Get results by form ID with pagination
|
|
65
108
|
*/ async getByFormId(dto, user) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
109
|
+
const page = dto.page ?? 0;
|
|
110
|
+
const pageSize = dto.pageSize ?? 10;
|
|
111
|
+
const result = await this.formResultService.getByFormId(dto.formId, user, {
|
|
112
|
+
page,
|
|
113
|
+
pageSize
|
|
69
114
|
});
|
|
115
|
+
return {
|
|
116
|
+
success: true,
|
|
117
|
+
message: 'Form results retrieved successfully',
|
|
118
|
+
data: result.data,
|
|
119
|
+
meta: {
|
|
120
|
+
total: result.total,
|
|
121
|
+
page,
|
|
122
|
+
pageSize,
|
|
123
|
+
count: result.data.length
|
|
124
|
+
}
|
|
125
|
+
};
|
|
70
126
|
}
|
|
71
127
|
/**
|
|
72
128
|
* Check if current user has already submitted this form (non-draft)
|
|
73
129
|
* Used for single response mode
|
|
74
130
|
*/ async hasUserSubmitted(dto, user) {
|
|
75
|
-
|
|
131
|
+
const hasSubmitted = await this.formResultService.hasUserSubmitted(dto.formId, user);
|
|
132
|
+
return {
|
|
133
|
+
success: true,
|
|
134
|
+
message: hasSubmitted ? 'User has submitted this form' : 'User has not submitted this form',
|
|
135
|
+
data: hasSubmitted
|
|
136
|
+
};
|
|
76
137
|
}
|
|
77
138
|
constructor(formResultService){
|
|
78
139
|
super(formResultService), _define_property(this, "formResultService", void 0), this.formResultService = formResultService;
|
|
@@ -147,6 +208,7 @@ _ts_decorate([
|
|
|
147
208
|
Post('by-form'),
|
|
148
209
|
UseGuards(JwtAuthGuard),
|
|
149
210
|
ApiBearerAuth(),
|
|
211
|
+
RequirePermission(FORM_RESULT_PERMISSIONS.READ),
|
|
150
212
|
HttpCode(HttpStatus.OK),
|
|
151
213
|
ApiOperation({
|
|
152
214
|
summary: 'Get results by form ID'
|