udp-stencil-component-library 25.18.2-beta.7 → 25.18.2-beta.9
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/dist/cjs/date-time-renderer_7.cjs.entry.js +2 -2
- package/dist/cjs/{enums-CgcTuQjC.js → enums-B3aaCQaV.js} +3 -3
- package/dist/cjs/{enums-CgcTuQjC.js.map → enums-B3aaCQaV.js.map} +1 -1
- package/dist/cjs/form-metadata-display_8.cjs.entry.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/question-configs-renderer_4.cjs.entry.js +2 -2
- package/dist/cjs/stencil-library.cjs.js +1 -1
- package/dist/cjs/{udp-forms-builder-question-template-BMMpObLX.js → udp-forms-builder-question-template-BS5ijef_.js} +3 -3
- package/dist/cjs/{udp-forms-builder-question-template-BMMpObLX.js.map → udp-forms-builder-question-template-BS5ijef_.js.map} +1 -1
- package/dist/cjs/udp-forms-builder.cjs.entry.js +2 -2
- package/dist/cjs/udp-forms-follow-up-list-card.cjs.entry.js +1 -1
- package/dist/cjs/udp-forms-list-card.cjs.entry.js +1 -1
- package/dist/cjs/udp-forms-list.cjs.entry.js +1 -1
- package/dist/cjs/udp-forms-renderer.cjs.entry.js +592 -604
- package/dist/cjs/udp-forms-renderer.entry.cjs.js.map +1 -1
- package/dist/cjs/udp-forms-ui.cjs.entry.js +2 -5
- package/dist/cjs/udp-forms-ui.entry.cjs.js.map +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-ui/udp-forms-ui.js +1 -4
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-ui/udp-forms-ui.js.map +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-utils/comments-crud-utils.js +153 -0
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-utils/comments-crud-utils.js.map +1 -0
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-utils/repeated-section-utils.js +104 -0
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-utils/repeated-section-utils.js.map +1 -0
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/{udp-forms-renderer-utils.js → udp-forms-renderer-utils/utils.js} +48 -2
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-utils/utils.js.map +1 -0
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer.js +187 -310
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer.js.map +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/enums.js +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/enums.js.map +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-handler/UdpFormHandler.js +13 -13
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-handler/UdpFormHandler.js.map +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/FormSubmissionHandler.js +142 -0
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/FormSubmissionHandler.js.map +1 -0
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/FormSubmissionHandlerFactory.js +3 -10
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/FormSubmissionHandlerFactory.js.map +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/IFormSubmissionHandler.js.map +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/types.js.map +1 -1
- package/dist/components/enums.js +1 -1
- package/dist/components/enums.js.map +1 -1
- package/dist/components/udp-forms-renderer.js +593 -608
- package/dist/components/udp-forms-renderer.js.map +1 -1
- package/dist/components/udp-forms-ui2.js +1 -4
- package/dist/components/udp-forms-ui2.js.map +1 -1
- package/dist/docs.json +1 -1
- package/dist/esm/date-time-renderer_7.entry.js +2 -2
- package/dist/esm/{enums-DHT5wSnX.js → enums-D5dod6Ie.js} +3 -3
- package/dist/esm/{enums-DHT5wSnX.js.map → enums-D5dod6Ie.js.map} +1 -1
- package/dist/esm/form-metadata-display_8.entry.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/question-configs-renderer_4.entry.js +2 -2
- package/dist/esm/stencil-library.js +1 -1
- package/dist/esm/{udp-forms-builder-question-template-D6ADNZEG.js → udp-forms-builder-question-template-BsYOju9V.js} +3 -3
- package/dist/esm/{udp-forms-builder-question-template-D6ADNZEG.js.map → udp-forms-builder-question-template-BsYOju9V.js.map} +1 -1
- package/dist/esm/udp-forms-builder.entry.js +2 -2
- package/dist/esm/udp-forms-follow-up-list-card.entry.js +1 -1
- package/dist/esm/udp-forms-list-card.entry.js +1 -1
- package/dist/esm/udp-forms-list.entry.js +1 -1
- package/dist/esm/udp-forms-renderer.entry.js +592 -604
- package/dist/esm/udp-forms-renderer.entry.js.map +1 -1
- package/dist/esm/udp-forms-ui.entry.js +2 -5
- package/dist/esm/udp-forms-ui.entry.js.map +1 -1
- package/dist/stencil-library/date-time-renderer_7.entry.js +1 -1
- package/dist/stencil-library/enums-D5dod6Ie.js +2 -0
- package/dist/stencil-library/{enums-DHT5wSnX.js.map → enums-D5dod6Ie.js.map} +1 -1
- package/dist/stencil-library/form-metadata-display_8.entry.js +1 -1
- package/dist/stencil-library/question-configs-renderer_4.entry.js +1 -1
- package/dist/stencil-library/stencil-library.esm.js +1 -1
- package/dist/stencil-library/{udp-forms-builder-question-template-D6ADNZEG.js → udp-forms-builder-question-template-BsYOju9V.js} +2 -2
- package/dist/stencil-library/{udp-forms-builder-question-template-D6ADNZEG.js.map → udp-forms-builder-question-template-BsYOju9V.js.map} +1 -1
- package/dist/stencil-library/udp-forms-builder.entry.js +1 -1
- package/dist/stencil-library/udp-forms-follow-up-list-card.entry.js +1 -1
- package/dist/stencil-library/udp-forms-follow-up-list-card.entry.js.map +1 -1
- package/dist/stencil-library/udp-forms-list-card.entry.js +1 -1
- package/dist/stencil-library/udp-forms-list-card.entry.js.map +1 -1
- package/dist/stencil-library/udp-forms-list.entry.js +1 -1
- package/dist/stencil-library/udp-forms-renderer.entry.esm.js.map +1 -1
- package/dist/stencil-library/udp-forms-renderer.entry.js +1 -1
- package/dist/stencil-library/udp-forms-renderer.entry.js.map +1 -1
- package/dist/stencil-library/udp-forms-ui.entry.esm.js.map +1 -1
- package/dist/stencil-library/udp-forms-ui.entry.js +1 -1
- package/dist/stencil-library/udp-forms-ui.entry.js.map +1 -1
- package/dist/types/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-utils/comments-crud-utils.d.ts +31 -0
- package/dist/types/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-utils/repeated-section-utils.d.ts +17 -0
- package/dist/types/components/forms/udp-forms/udp-forms-renderer/{udp-forms-renderer-utils.d.ts → udp-forms-renderer-utils/utils.d.ts} +4 -0
- package/dist/types/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer.d.ts +6 -10
- package/dist/types/components/forms/udp-forms/udp-forms-utils/enums.d.ts +1 -1
- package/dist/types/components/forms/udp-forms/udp-forms-utils/form-handler/UdpFormHandler.d.ts +5 -6
- package/dist/types/components/forms/udp-forms/udp-forms-utils/form-submission-handler/FormSubmissionHandler.d.ts +42 -0
- package/dist/types/components/forms/udp-forms/udp-forms-utils/form-submission-handler/FormSubmissionHandlerFactory.d.ts +1 -1
- package/dist/types/components/forms/udp-forms/udp-forms-utils/form-submission-handler/IFormSubmissionHandler.d.ts +44 -5
- package/dist/types/components/forms/udp-forms/udp-forms-utils/types.d.ts +16 -0
- package/package.json +1 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-renderer/udp-forms-renderer-utils.js.map +0 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/PrivateFormSubmissionHandler.js +0 -264
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/PrivateFormSubmissionHandler.js.map +0 -1
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/PublicFormSubmissionHandler.js +0 -63
- package/dist/collection/components/forms/udp-forms/udp-forms-utils/form-submission-handler/PublicFormSubmissionHandler.js.map +0 -1
- package/dist/stencil-library/enums-DHT5wSnX.js +0 -2
- package/dist/types/components/forms/udp-forms/udp-forms-utils/form-submission-handler/PrivateFormSubmissionHandler.d.ts +0 -131
- package/dist/types/components/forms/udp-forms/udp-forms-utils/form-submission-handler/PublicFormSubmissionHandler.d.ts +0 -15
|
@@ -2,7 +2,7 @@ import { p as proxyCustomElement, H, h } from './index2.js';
|
|
|
2
2
|
import { throttle } from 'lodash';
|
|
3
3
|
import { g as getCurrentTenantId } from './tenantUtils.js';
|
|
4
4
|
import { f as fetchLatestForms } from './udp-form-api-utils.js';
|
|
5
|
-
import { c as UdpFormsSubmissionStatusEnum,
|
|
5
|
+
import { c as UdpFormsSubmissionStatusEnum, b as UdpFormsPageIdEnum, U as UdpFormsFieldTypeEnum, a as UdpFormsTypeEnum } from './enums.js';
|
|
6
6
|
import { b as buildEmptyCurrentValues } from './utils.js';
|
|
7
7
|
import { c as createFormData, m as makeApiCall } from './makeApiCall.js';
|
|
8
8
|
import { S as SearchBuilder, c as SearchOperators } from './SearchBuilder.js';
|
|
@@ -58,67 +58,6 @@ function isFileArray(array) {
|
|
|
58
58
|
return array.every(item => item instanceof File);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// TODO: This handler needs to be updated with new public form logic.
|
|
62
|
-
// ******* THIS HANDLER IS OUTDATED, AND WILL LIKELY NOT WORK *********
|
|
63
|
-
/**
|
|
64
|
-
* Handles submission for *public* (unauthenticated) forms.
|
|
65
|
-
*/
|
|
66
|
-
class PublicFormSubmissionHandler {
|
|
67
|
-
constructor(formId, formVersion, tenantId) { }
|
|
68
|
-
async fetchAndPopulateUdpFormSubmissionObj(submission) {
|
|
69
|
-
return submission;
|
|
70
|
-
}
|
|
71
|
-
objectToFormData(obj) {
|
|
72
|
-
var _a, _b;
|
|
73
|
-
const files = [];
|
|
74
|
-
const formObj = {};
|
|
75
|
-
for (const key in obj) {
|
|
76
|
-
let value = (_b = (_a = obj[key]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : obj[key];
|
|
77
|
-
if (isFileArray(value)) {
|
|
78
|
-
if (value.length > 0) {
|
|
79
|
-
files.push(value[0]);
|
|
80
|
-
formObj[key] = value[0].name;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
else if (Array.isArray(value)) {
|
|
84
|
-
formObj[key] = value.map(v => (typeof v === 'string' ? v : v.value)).join(',');
|
|
85
|
-
}
|
|
86
|
-
else if (value !== null && value !== undefined && value !== '') {
|
|
87
|
-
formObj[key] = value;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return createFormData({
|
|
91
|
-
formData: JSON.stringify(formObj),
|
|
92
|
-
formFiles: files[0] || null
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
async saveCurrentFormSubmissionState(values, submission) {
|
|
96
|
-
return submission;
|
|
97
|
-
}
|
|
98
|
-
async saveFormSubmissionComments(values, submission) {
|
|
99
|
-
return submission;
|
|
100
|
-
}
|
|
101
|
-
async createNewLinkedFollowUpFormSubmission(submission) {
|
|
102
|
-
return submission;
|
|
103
|
-
}
|
|
104
|
-
async finalizeFormSubmissionState(values, submission) {
|
|
105
|
-
try {
|
|
106
|
-
// const formData = this.objectToFormData(values);
|
|
107
|
-
// await makeApiCall(
|
|
108
|
-
// 'POST',
|
|
109
|
-
// `${ConfigService.productV1ApiUrl}/UdpForm/${this.formId}/${this.formVersion}/submit/public?tenantId=${this.tenantId}`,
|
|
110
|
-
// formData,
|
|
111
|
-
// true
|
|
112
|
-
// );
|
|
113
|
-
return submission;
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
console.error('Public form submission failed:', error);
|
|
117
|
-
throw error;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
61
|
class UdpFormSubmission {
|
|
123
62
|
constructor(initialData = {}) {
|
|
124
63
|
this.id = null;
|
|
@@ -251,260 +190,138 @@ class UdpFormSubmission {
|
|
|
251
190
|
}
|
|
252
191
|
|
|
253
192
|
/**
|
|
254
|
-
*
|
|
193
|
+
* Single handler for UdpFormSubmission operations.
|
|
194
|
+
*
|
|
195
|
+
* Behavior changes by access mode:
|
|
196
|
+
* - private: supports draft save and follow-ups; comments are saved via standard draft-save until submitted,
|
|
197
|
+
* and via dedicated comment endpoints when submitted or viewed by a non-owner.
|
|
198
|
+
* - public : no draft save, no follow-ups; submit uses the `/UdpFormSubmission/public` endpoint;
|
|
199
|
+
* comments use dedicated comment endpoints once a submission exists.
|
|
255
200
|
*/
|
|
256
|
-
class
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
* Fetch and refresh an existing UdpFormSubmission instance.
|
|
262
|
-
*
|
|
263
|
-
* Attempts to load the latest server representation of the provided
|
|
264
|
-
* udpFormSubmission. Uses generic fields (generic1/2/3) when present to
|
|
265
|
-
* disambiguate records, otherwise falls back to a simple id lookup.
|
|
266
|
-
*
|
|
267
|
-
* @param udpFormSubmission - local UdpFormSubmission object to refresh
|
|
268
|
-
* @returns a new UdpFormSubmission merged with server data, or the original on failure
|
|
269
|
-
* @throws network or unexpected errors
|
|
270
|
-
*/
|
|
271
|
-
async fetchAndPopulateUdpFormSubmissionObj(udpFormSubmission) {
|
|
201
|
+
class FormSubmissionHandler {
|
|
202
|
+
constructor(accessMode) {
|
|
203
|
+
this.accessMode = accessMode;
|
|
204
|
+
}
|
|
205
|
+
async addComment(udpFormSubmission, comment) {
|
|
272
206
|
if (!(udpFormSubmission === null || udpFormSubmission === void 0 ? void 0 : udpFormSubmission.id)) {
|
|
273
|
-
|
|
274
|
-
return udpFormSubmission;
|
|
275
|
-
}
|
|
276
|
-
try {
|
|
277
|
-
const { id, generic1, generic2, generic3 } = udpFormSubmission;
|
|
278
|
-
let response = null;
|
|
279
|
-
// Prefer the generics-aware lookup if any generic values are present
|
|
280
|
-
if (generic1 || generic2 || generic3) {
|
|
281
|
-
response = await this.getFormSubmissionByIdAndGenerics(id, generic1 || undefined, generic2 || undefined, generic3 || undefined);
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
response = await this.getFormSubmissionById(id);
|
|
285
|
-
}
|
|
286
|
-
if (!response) {
|
|
287
|
-
console.warn('No form submission found for the provided object.');
|
|
288
|
-
return udpFormSubmission;
|
|
289
|
-
}
|
|
290
|
-
return new UdpFormSubmission(Object.assign(Object.assign({}, udpFormSubmission), response));
|
|
291
|
-
}
|
|
292
|
-
catch (error) {
|
|
293
|
-
console.error('Error fetching form submission:', error);
|
|
294
|
-
throw error;
|
|
207
|
+
throw new Error('Cannot add a comment without a submission ID.');
|
|
295
208
|
}
|
|
209
|
+
// Backend contract:
|
|
210
|
+
// - public : POST /UdpFormSubmission/{submissionId}/comments/public
|
|
211
|
+
// - private: POST /UdpFormSubmission/{submissionId}/comments/private
|
|
212
|
+
const url = `${ConfigService.productV1ApiUrl}/UdpFormSubmission/${udpFormSubmission.id}/comments/${this.accessMode}`;
|
|
213
|
+
await makeApiCall('POST', url, comment, true);
|
|
296
214
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
* Performs a minimal search (page size 1) for the given id and returns
|
|
301
|
-
* the first match or null if not found. Logs and returns null on errors.
|
|
302
|
-
*
|
|
303
|
-
* @param id - submission id to query
|
|
304
|
-
* @returns UdpFormSubmission or null
|
|
305
|
-
*/
|
|
306
|
-
async getFormSubmissionById(id) {
|
|
307
|
-
var _a;
|
|
308
|
-
try {
|
|
309
|
-
const search = new SearchBuilder(1, 1)
|
|
310
|
-
.addFilter('id', id, SearchOperators.EQUALS);
|
|
311
|
-
const response = await search.execute('UdpFormSubmission');
|
|
312
|
-
return ((_a = response === null || response === void 0 ? void 0 : response.pageList) === null || _a === void 0 ? void 0 : _a[0]) || null;
|
|
215
|
+
async updateComment(udpFormSubmission, args) {
|
|
216
|
+
if (!(udpFormSubmission === null || udpFormSubmission === void 0 ? void 0 : udpFormSubmission.id)) {
|
|
217
|
+
throw new Error('Cannot update a comment without a submission ID.');
|
|
313
218
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
219
|
+
const { sectionKey, questionKey, commentId, value } = args;
|
|
220
|
+
// Backend contract: PUT /UdpFormSubmission/{submissionId}/comments/{commentId}
|
|
221
|
+
// Body: { value, sectionKey, questionKey }
|
|
222
|
+
const url = `${ConfigService.productV1ApiUrl}/UdpFormSubmission/${udpFormSubmission.id}/comments/${commentId}`;
|
|
223
|
+
await makeApiCall('PUT', url, { value, sectionKey, questionKey }, true);
|
|
224
|
+
}
|
|
225
|
+
async deleteComment(udpFormSubmission, args) {
|
|
226
|
+
if (!(udpFormSubmission === null || udpFormSubmission === void 0 ? void 0 : udpFormSubmission.id)) {
|
|
227
|
+
throw new Error('Cannot delete a comment without a submission ID.');
|
|
317
228
|
}
|
|
229
|
+
const { commentId } = args;
|
|
230
|
+
// Backend contract: DELETE /UdpFormSubmission/{submissionId}/comments/{commentId}
|
|
231
|
+
const url = `${ConfigService.productV1ApiUrl}/UdpFormSubmission/${udpFormSubmission.id}/comments/${commentId}`;
|
|
232
|
+
await makeApiCall('DELETE', url, null, true);
|
|
318
233
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
* then executes a minimal search and returns the first match or null.
|
|
324
|
-
*
|
|
325
|
-
* @param id - submission id to query
|
|
326
|
-
* @param generic1 - optional generic1 value to filter by
|
|
327
|
-
* @param generic2 - optional generic2 value to filter by
|
|
328
|
-
* @param generic3 - optional generic3 value to filter by
|
|
329
|
-
* @returns UdpFormSubmission or null
|
|
330
|
-
*/
|
|
331
|
-
async getFormSubmissionByIdAndGenerics(id, generic1, generic2, generic3) {
|
|
332
|
-
var _a;
|
|
333
|
-
try {
|
|
334
|
-
const search = new SearchBuilder(1, 1)
|
|
335
|
-
.addFilter('id', id, SearchOperators.EQUALS);
|
|
336
|
-
if (generic1) {
|
|
337
|
-
search.addFilter('generic1', generic1, SearchOperators.EQUALS);
|
|
338
|
-
}
|
|
339
|
-
if (generic2) {
|
|
340
|
-
search.addFilter('generic2', generic2, SearchOperators.EQUALS);
|
|
341
|
-
}
|
|
342
|
-
if (generic3) {
|
|
343
|
-
search.addFilter('generic3', generic3, SearchOperators.EQUALS);
|
|
344
|
-
}
|
|
345
|
-
const response = await search.execute('UdpFormSubmission');
|
|
346
|
-
return ((_a = response === null || response === void 0 ? void 0 : response.pageList) === null || _a === void 0 ? void 0 : _a[0]) || null;
|
|
234
|
+
async fetchAndPopulateUdpFormSubmissionObj(udpFormSubmission) {
|
|
235
|
+
if (!(udpFormSubmission === null || udpFormSubmission === void 0 ? void 0 : udpFormSubmission.id)) {
|
|
236
|
+
console.warn('Cannot fetch form submission without an ID.');
|
|
237
|
+
return udpFormSubmission;
|
|
347
238
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
239
|
+
// Public reads should not leak other users' submissions.
|
|
240
|
+
if (this.accessMode === 'public') {
|
|
241
|
+
const response = await this.getFormSubmissionByIdAndUser(udpFormSubmission.id, udpFormSubmission.unityUserId);
|
|
242
|
+
return response ? new UdpFormSubmission(Object.assign(Object.assign({}, udpFormSubmission), response)) : udpFormSubmission;
|
|
351
243
|
}
|
|
244
|
+
// Private: fetch by id.
|
|
245
|
+
const { id } = udpFormSubmission;
|
|
246
|
+
const response = await this.getFormSubmissionById(id);
|
|
247
|
+
return response ? new UdpFormSubmission(Object.assign(Object.assign({}, udpFormSubmission), response)) : udpFormSubmission;
|
|
352
248
|
}
|
|
353
|
-
// =============================
|
|
354
|
-
// Save & Submit Methods
|
|
355
|
-
// =============================
|
|
356
|
-
/**
|
|
357
|
-
* Create a new follow-up form submission that is linked to an existing submission.
|
|
358
|
-
*
|
|
359
|
-
* Saves a new UdpFormSubmission in 'in-progress' state without populating field values
|
|
360
|
-
* (only metadata/links), and returns the created submission object.
|
|
361
|
-
*
|
|
362
|
-
* @param udpFormSubmission - template/parent submission to base the new follow-up on
|
|
363
|
-
* @returns newly created UdpFormSubmission
|
|
364
|
-
*/
|
|
365
249
|
async createNewLinkedFollowUpFormSubmission(udpFormSubmission) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* Persist submission metadata (without full field values).
|
|
371
|
-
*
|
|
372
|
-
* Converts provided udpFormSubmission into formData excluding field values,
|
|
373
|
-
* determines whether to POST or PUT based on presence of id, and merges the
|
|
374
|
-
* server response into a new UdpFormSubmission instance.
|
|
375
|
-
*
|
|
376
|
-
* @param udpFormSubmission - the submission to save
|
|
377
|
-
* @param status - desired status to save (e.g., 'in-progress' or 'submitted')
|
|
378
|
-
* @returns saved UdpFormSubmission (refetches if server returns empty PUT response)
|
|
379
|
-
*/
|
|
380
|
-
async saveFormDataWithoutValues(udpFormSubmission, status) {
|
|
381
|
-
try {
|
|
382
|
-
const formData = udpFormSubmission.processSubmissionIntoFormDataWithoutValues(status);
|
|
383
|
-
const { method, url } = this.getApiRequestInfo(udpFormSubmission);
|
|
384
|
-
const response = await makeApiCall(method, url, formData, true);
|
|
385
|
-
// PUT returns an empty string; refetch to refresh local data
|
|
386
|
-
if (response === '') {
|
|
387
|
-
return await this.fetchAndPopulateUdpFormSubmissionObj(udpFormSubmission);
|
|
388
|
-
}
|
|
389
|
-
return new UdpFormSubmission(Object.assign(Object.assign({}, udpFormSubmission), response));
|
|
250
|
+
if (this.accessMode === 'public') {
|
|
251
|
+
throw new Error('Follow-up form submissions are not supported for public forms.');
|
|
390
252
|
}
|
|
391
|
-
|
|
392
|
-
console.error('Failed to save form udpFormSubmission:', error);
|
|
393
|
-
throw error;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Save only comment-related fields for a submission.
|
|
398
|
-
*
|
|
399
|
-
* Delegates to the lower-level saveCommentsToDB helper to persist comment changes
|
|
400
|
-
* while preserving existing submission state and metadata.
|
|
401
|
-
*
|
|
402
|
-
* @param values - comment values to persist
|
|
403
|
-
* @param udpFormSubmission - target submission object
|
|
404
|
-
* @returns updated UdpFormSubmission
|
|
405
|
-
*/
|
|
406
|
-
async saveFormSubmissionComments(values, udpFormSubmission) {
|
|
407
|
-
return this.saveCommentsToDB(values, udpFormSubmission);
|
|
253
|
+
return this.saveFormDataWithoutValues(udpFormSubmission, UdpFormsSubmissionStatusEnum.InProgress);
|
|
408
254
|
}
|
|
409
|
-
/**
|
|
410
|
-
* Save the current state of the form's data as a draft ('in-progress').
|
|
411
|
-
*
|
|
412
|
-
* Converts the provided values into the required form payload and persists them.
|
|
413
|
-
* Determines POST vs PUT automatically and refreshes local object on empty PUT responses.
|
|
414
|
-
*
|
|
415
|
-
* @param values - field data to save
|
|
416
|
-
* @param udpFormSubmission - submission being updated
|
|
417
|
-
* @returns updated UdpFormSubmission
|
|
418
|
-
*/
|
|
419
255
|
async saveCurrentFormSubmissionState(values, udpFormSubmission) {
|
|
256
|
+
if (this.accessMode === 'public') {
|
|
257
|
+
// Public forms do not support draft save / return-later.
|
|
258
|
+
return udpFormSubmission;
|
|
259
|
+
}
|
|
420
260
|
return this.saveSubmissionToDB(values, udpFormSubmission, UdpFormsSubmissionStatusEnum.InProgress);
|
|
421
261
|
}
|
|
422
|
-
/**
|
|
423
|
-
* Finalize and submit the form (set state to 'submitted').
|
|
424
|
-
*
|
|
425
|
-
* Persists the provided values and marks the submission as 'submitted'.
|
|
426
|
-
*
|
|
427
|
-
* @param values - final form values to submit
|
|
428
|
-
* @param udpFormSubmission - submission being finalized
|
|
429
|
-
* @returns updated UdpFormSubmission
|
|
430
|
-
*/
|
|
431
262
|
async finalizeFormSubmissionState(values, udpFormSubmission) {
|
|
432
263
|
return this.saveSubmissionToDB(values, udpFormSubmission, UdpFormsSubmissionStatusEnum.Submitted);
|
|
433
264
|
}
|
|
434
265
|
// =============================
|
|
435
266
|
// Helpers
|
|
436
267
|
// =============================
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
268
|
+
async saveFormDataWithoutValues(udpFormSubmission, status) {
|
|
269
|
+
const formData = udpFormSubmission.processSubmissionIntoFormDataWithoutValues(status);
|
|
270
|
+
const { method, url } = this.getApiRequestInfo(udpFormSubmission);
|
|
271
|
+
const response = await makeApiCall(method, url, formData, true);
|
|
272
|
+
if (response === '') {
|
|
273
|
+
return await this.fetchAndPopulateUdpFormSubmissionObj(udpFormSubmission);
|
|
274
|
+
}
|
|
275
|
+
return new UdpFormSubmission(Object.assign(Object.assign({}, udpFormSubmission), response));
|
|
276
|
+
}
|
|
446
277
|
getApiRequestInfo(udpFormSubmission) {
|
|
447
|
-
const baseUrl = `${ConfigService.productV1ApiUrl}/UdpFormSubmission`;
|
|
278
|
+
const baseUrl = `${ConfigService.productV1ApiUrl}/UdpFormSubmission/${this.accessMode}`;
|
|
279
|
+
// Public mode does not support draft saves or updates; only a single submit (POST /public).
|
|
280
|
+
// Private mode supports POST (create) and PUT (update).
|
|
281
|
+
if (this.accessMode === 'public') {
|
|
282
|
+
return { method: 'POST', url: baseUrl };
|
|
283
|
+
}
|
|
448
284
|
return udpFormSubmission.id
|
|
449
|
-
? { method: 'PUT', url:
|
|
285
|
+
? { method: 'PUT', url: baseUrl }
|
|
450
286
|
: { method: 'POST', url: baseUrl };
|
|
451
287
|
}
|
|
452
|
-
/**
|
|
453
|
-
* Persist form values and metadata to the backend.
|
|
454
|
-
*
|
|
455
|
-
* Builds the form payload using udpFormSubmission.processSubmissionIntoFormData,
|
|
456
|
-
* resolves POST vs PUT using getApiRequestInfo, and returns a merged UdpFormSubmission.
|
|
457
|
-
* If the server returns an empty string for PUT, the method refetches the record.
|
|
458
|
-
*
|
|
459
|
-
* @param values - field values to be saved
|
|
460
|
-
* @param udpFormSubmission - submission being updated
|
|
461
|
-
* @param status - status to persist (e.g., 'in-progress' | 'submitted')
|
|
462
|
-
* @returns updated UdpFormSubmission
|
|
463
|
-
*/
|
|
464
288
|
async saveSubmissionToDB(values, udpFormSubmission, status) {
|
|
289
|
+
const formData = udpFormSubmission.processSubmissionIntoFormData(values, status);
|
|
290
|
+
const { method, url } = this.getApiRequestInfo(udpFormSubmission);
|
|
291
|
+
const response = await makeApiCall(method, url, formData, true);
|
|
292
|
+
if (response === '') {
|
|
293
|
+
return await this.fetchAndPopulateUdpFormSubmissionObj(udpFormSubmission);
|
|
294
|
+
}
|
|
295
|
+
const updated = new UdpFormSubmission(Object.assign(Object.assign({}, udpFormSubmission), response));
|
|
296
|
+
// IMPORTANT: callers often pass a shared `udpFormSubmission` instance and do not use the return value.
|
|
297
|
+
// Ensure the original instance gets the new server-assigned id/status when the first save/submit creates it.
|
|
298
|
+
Object.assign(udpFormSubmission, updated);
|
|
299
|
+
return updated;
|
|
300
|
+
}
|
|
301
|
+
async getFormSubmissionById(id) {
|
|
302
|
+
var _a;
|
|
465
303
|
try {
|
|
466
|
-
const
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
// PUT returns an empty string; refetch to refresh local data
|
|
470
|
-
if (response === '') {
|
|
471
|
-
return await this.fetchAndPopulateUdpFormSubmissionObj(udpFormSubmission);
|
|
472
|
-
}
|
|
473
|
-
return new UdpFormSubmission(Object.assign(Object.assign({}, udpFormSubmission), response));
|
|
304
|
+
const search = new SearchBuilder(1, 1).addFilter('id', id, SearchOperators.EQUALS);
|
|
305
|
+
const response = await search.execute('UdpFormSubmission');
|
|
306
|
+
return ((_a = response === null || response === void 0 ? void 0 : response.pageList) === null || _a === void 0 ? void 0 : _a[0]) || null;
|
|
474
307
|
}
|
|
475
308
|
catch (error) {
|
|
476
|
-
console.error('
|
|
477
|
-
|
|
309
|
+
console.error('Error fetching form submission by ID:', error);
|
|
310
|
+
return null;
|
|
478
311
|
}
|
|
479
312
|
}
|
|
480
|
-
|
|
481
|
-
* Persist only comment changes for an existing submission via PUT.
|
|
482
|
-
*
|
|
483
|
-
* Forces a PUT to the specific submission id, builds a form payload containing
|
|
484
|
-
* the provided comment values, and returns the updated submission. If the API
|
|
485
|
-
* returns an empty string (PUT shorthand), the method refetches the record.
|
|
486
|
-
*
|
|
487
|
-
* @param values - comment fields to persist
|
|
488
|
-
* @param udpFormSubmission - target submission (must have id)
|
|
489
|
-
* @returns updated UdpFormSubmission
|
|
490
|
-
*/
|
|
491
|
-
async saveCommentsToDB(values, udpFormSubmission) {
|
|
313
|
+
async getFormSubmissionByIdAndUser(id, unityUserId) {
|
|
492
314
|
var _a;
|
|
493
315
|
try {
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
// PUT returns an empty string; refetch to refresh local data
|
|
500
|
-
if (response === '') {
|
|
501
|
-
return await this.fetchAndPopulateUdpFormSubmissionObj(udpFormSubmission);
|
|
502
|
-
}
|
|
503
|
-
return new UdpFormSubmission(Object.assign(Object.assign({}, udpFormSubmission), response));
|
|
316
|
+
const search = new SearchBuilder(1, 1)
|
|
317
|
+
.addFilter('id', id, SearchOperators.EQUALS)
|
|
318
|
+
.addFilter('unityUserId', unityUserId, SearchOperators.EQUALS);
|
|
319
|
+
const response = await search.execute('UdpFormSubmission');
|
|
320
|
+
return ((_a = response === null || response === void 0 ? void 0 : response.pageList) === null || _a === void 0 ? void 0 : _a[0]) || null;
|
|
504
321
|
}
|
|
505
322
|
catch (error) {
|
|
506
|
-
console.error('
|
|
507
|
-
|
|
323
|
+
console.error('Error fetching form submission by ID:', error);
|
|
324
|
+
return null;
|
|
508
325
|
}
|
|
509
326
|
}
|
|
510
327
|
}
|
|
@@ -513,36 +330,28 @@ class PrivateFormSubmissionHandler {
|
|
|
513
330
|
* Factory for creating form submission handlers.
|
|
514
331
|
*/
|
|
515
332
|
class FormSubmissionHandlerFactory {
|
|
516
|
-
static create(isPublic, userId
|
|
517
|
-
|
|
518
|
-
return new PublicFormSubmissionHandler(formId, version, tenantId);
|
|
519
|
-
}
|
|
520
|
-
if (!userId) {
|
|
521
|
-
throw new Error('User ID is required for private forms');
|
|
522
|
-
}
|
|
523
|
-
return new PrivateFormSubmissionHandler();
|
|
333
|
+
static create(isPublic, userId) {
|
|
334
|
+
return new FormSubmissionHandler(isPublic ? 'public' : 'private');
|
|
524
335
|
}
|
|
525
336
|
}
|
|
526
337
|
|
|
527
|
-
/**
|
|
528
|
-
* UDP Form handler (new UdpFormSubmission endpoint)
|
|
529
|
-
*/
|
|
530
338
|
class UdpFormHandler {
|
|
531
|
-
constructor(tenantId) {
|
|
339
|
+
constructor(tenantId, isPublic = false) {
|
|
532
340
|
this.tenantId = tenantId;
|
|
341
|
+
this.isPublic = isPublic;
|
|
533
342
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
343
|
+
buildDescribeUrl(formId, formVersion) {
|
|
344
|
+
const base = ConfigService.productV1ApiUrl;
|
|
345
|
+
if (this.isPublic) {
|
|
346
|
+
return `${base}/UdpForm/${formId}/${formVersion}/describe/public`;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
return `${base}/UdpForm/${formId}/${formVersion}/describe`;
|
|
541
350
|
}
|
|
542
|
-
return formData;
|
|
543
351
|
}
|
|
544
352
|
async getFormByFormIdAndFormVersion(formId, formVersion) {
|
|
545
|
-
const
|
|
353
|
+
const url = this.buildDescribeUrl(formId, formVersion);
|
|
354
|
+
const formData = await makeApiCall('GET', url);
|
|
546
355
|
if (formData.styleOverrides && typeof formData.styleOverrides === 'string') {
|
|
547
356
|
formData.styleOverrides = JSON.parse(formData.styleOverrides);
|
|
548
357
|
}
|
|
@@ -751,6 +560,306 @@ const applyUrlSeedValuesForAll = (dynamicSections, urlContext) => {
|
|
|
751
560
|
});
|
|
752
561
|
return merged;
|
|
753
562
|
};
|
|
563
|
+
const replaceUrlWithSubmissionId = (submissionId, history) => {
|
|
564
|
+
// build a URL that preserves the current pathname + hash but replaces the query string
|
|
565
|
+
const pathname = typeof window !== 'undefined' ? window.location.pathname : `/page/${UdpFormsPageIdEnum.FormRendererPageId}`;
|
|
566
|
+
const hash = typeof window !== 'undefined' ? window.location.hash : '';
|
|
567
|
+
const newUrl = `${pathname}?udpf_submissionId=${submissionId}${hash}`;
|
|
568
|
+
const h = history;
|
|
569
|
+
// Prefer history.replace when available, handle react-router v6 navigate function, fallback to push or native replaceState
|
|
570
|
+
if (h) {
|
|
571
|
+
if (typeof h.replace === 'function') {
|
|
572
|
+
h.replace(newUrl);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (typeof h.push === 'function') {
|
|
576
|
+
h.push(newUrl);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
// react-router v6 exposes a navigate function
|
|
580
|
+
if (typeof h === 'function') {
|
|
581
|
+
try {
|
|
582
|
+
h(newUrl, { replace: true });
|
|
583
|
+
}
|
|
584
|
+
catch (_a) {
|
|
585
|
+
h(newUrl);
|
|
586
|
+
}
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (typeof window !== 'undefined' && window.history && typeof window.history.replaceState === 'function') {
|
|
591
|
+
window.history.replaceState({}, '', newUrl);
|
|
592
|
+
}
|
|
593
|
+
else if (typeof window !== 'undefined') {
|
|
594
|
+
window.location.href = newUrl;
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
const enqueueSnackbarSuccess = (message, enqueueSnackbar) => {
|
|
598
|
+
enqueueSnackbar === null || enqueueSnackbar === void 0 ? void 0 : enqueueSnackbar(message, {
|
|
599
|
+
variant: 'success',
|
|
600
|
+
anchorOrigin: { vertical: 'top', horizontal: 'center' },
|
|
601
|
+
});
|
|
602
|
+
};
|
|
603
|
+
const enqueueSnackbarError = (message, enqueueSnackbar) => {
|
|
604
|
+
enqueueSnackbar === null || enqueueSnackbar === void 0 ? void 0 : enqueueSnackbar(message, {
|
|
605
|
+
variant: 'error',
|
|
606
|
+
anchorOrigin: { vertical: 'top', horizontal: 'center' },
|
|
607
|
+
});
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
const getSectionKey = (section) => {
|
|
611
|
+
return section.isOriginalSection ? section.name : `${section.name}_${section.sectionPositionSuffix}`;
|
|
612
|
+
};
|
|
613
|
+
const safeParseFieldProperties = (fieldProperties) => {
|
|
614
|
+
if (!fieldProperties)
|
|
615
|
+
return {};
|
|
616
|
+
if (typeof fieldProperties === 'object')
|
|
617
|
+
return fieldProperties;
|
|
618
|
+
if (typeof fieldProperties !== 'string')
|
|
619
|
+
return {};
|
|
620
|
+
try {
|
|
621
|
+
return JSON.parse(fieldProperties || '{}');
|
|
622
|
+
}
|
|
623
|
+
catch (_a) {
|
|
624
|
+
return {};
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
const computeDuplicateRepeatableSection = (params) => {
|
|
628
|
+
var _a, _b, _c;
|
|
629
|
+
const { dynamicSections, values, index } = params;
|
|
630
|
+
const sectionToClone = dynamicSections[index];
|
|
631
|
+
if (!sectionToClone)
|
|
632
|
+
return { nextDynamicSections: dynamicSections, nextValues: values };
|
|
633
|
+
const cloningSectionName = sectionToClone.name;
|
|
634
|
+
// Find existing repeat group indices
|
|
635
|
+
const repeatKeys = findRepeatGroupKeys(cloningSectionName, values);
|
|
636
|
+
const nextRepeatIndex = repeatKeys.length > 0 ? Math.max(...repeatKeys) + 1 : 2;
|
|
637
|
+
const clonedSection = Object.assign(Object.assign({}, structuredClone(sectionToClone)), { formQuestions: (sectionToClone.formQuestions || []).map(q => {
|
|
638
|
+
const newQuestionObj = structuredClone(q);
|
|
639
|
+
newQuestionObj.questionIdentifierKey = `${cloningSectionName}_${nextRepeatIndex}.${q.name}`;
|
|
640
|
+
return newQuestionObj;
|
|
641
|
+
}), isOriginalSection: false, sectionPositionSuffix: nextRepeatIndex });
|
|
642
|
+
// Find the last index of this section group
|
|
643
|
+
let insertAtIndex = index;
|
|
644
|
+
for (let i = index + 1; i < dynamicSections.length; i++) {
|
|
645
|
+
const s = dynamicSections[i];
|
|
646
|
+
if (s.name === cloningSectionName)
|
|
647
|
+
insertAtIndex = i;
|
|
648
|
+
else
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
const nextDynamicSections = [
|
|
652
|
+
...dynamicSections.slice(0, insertAtIndex + 1),
|
|
653
|
+
clonedSection,
|
|
654
|
+
...dynamicSections.slice(insertAtIndex + 1),
|
|
655
|
+
];
|
|
656
|
+
const nextValues = structuredClone(values || {});
|
|
657
|
+
const newSectionKey = `${cloningSectionName}_${nextRepeatIndex}`;
|
|
658
|
+
const sourceSectionKey = getSectionKey(sectionToClone);
|
|
659
|
+
for (const q of clonedSection.formQuestions || []) {
|
|
660
|
+
if (!nextValues[newSectionKey])
|
|
661
|
+
nextValues[newSectionKey] = {};
|
|
662
|
+
let value = '';
|
|
663
|
+
if (q.fieldTypeId === UdpFormsFieldTypeEnum.Paragraph) {
|
|
664
|
+
const sourceVal = (_b = (_a = values === null || values === void 0 ? void 0 : values[sourceSectionKey]) === null || _a === void 0 ? void 0 : _a[q.name]) === null || _b === void 0 ? void 0 : _b.value;
|
|
665
|
+
const props = safeParseFieldProperties(q.fieldProperties);
|
|
666
|
+
const paragraphDefault = (_c = props === null || props === void 0 ? void 0 : props.paragraphText) !== null && _c !== void 0 ? _c : '';
|
|
667
|
+
value = sourceVal !== null && sourceVal !== void 0 ? sourceVal : paragraphDefault;
|
|
668
|
+
}
|
|
669
|
+
nextValues[newSectionKey][q.name] = { value, comments: [], metadata: {} };
|
|
670
|
+
}
|
|
671
|
+
return { nextDynamicSections, nextValues };
|
|
672
|
+
};
|
|
673
|
+
const computeDeleteRepeatableSection = (params) => {
|
|
674
|
+
const { dynamicSections, values, index } = params;
|
|
675
|
+
const sectionToDelete = dynamicSections[index];
|
|
676
|
+
if (!sectionToDelete || sectionToDelete.isOriginalSection) {
|
|
677
|
+
return { nextDynamicSections: dynamicSections, nextValues: values };
|
|
678
|
+
}
|
|
679
|
+
const deleteSectionName = sectionToDelete.name;
|
|
680
|
+
const deleteSuffix = sectionToDelete.sectionPositionSuffix;
|
|
681
|
+
const sectionKeyToDelete = `${deleteSectionName}_${deleteSuffix}`;
|
|
682
|
+
const nextValues = Object.assign({}, (values || {}));
|
|
683
|
+
delete nextValues[sectionKeyToDelete];
|
|
684
|
+
const updatedSections = structuredClone(dynamicSections);
|
|
685
|
+
updatedSections.splice(index, 1);
|
|
686
|
+
// Shift all later repeatable sections backward by 1
|
|
687
|
+
updatedSections.forEach(section => {
|
|
688
|
+
var _a;
|
|
689
|
+
if (section.name !== deleteSectionName ||
|
|
690
|
+
section.isOriginalSection ||
|
|
691
|
+
((_a = section.sectionPositionSuffix) !== null && _a !== void 0 ? _a : 0) <= (deleteSuffix !== null && deleteSuffix !== void 0 ? deleteSuffix : 0)) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
const oldSuffix = section.sectionPositionSuffix;
|
|
695
|
+
const oldSectionKey = `${deleteSectionName}_${oldSuffix}`;
|
|
696
|
+
const newSuffix = oldSuffix - 1;
|
|
697
|
+
const newSectionKey = `${deleteSectionName}_${newSuffix}`;
|
|
698
|
+
section.sectionPositionSuffix = newSuffix;
|
|
699
|
+
section.formQuestions = (section.formQuestions || []).map(q => {
|
|
700
|
+
const newSectionQuestion = structuredClone(q);
|
|
701
|
+
newSectionQuestion.questionIdentifierKey = `${newSectionKey}.${q.name}`;
|
|
702
|
+
return newSectionQuestion;
|
|
703
|
+
});
|
|
704
|
+
if (nextValues[oldSectionKey]) {
|
|
705
|
+
nextValues[newSectionKey] = structuredClone(nextValues[oldSectionKey]);
|
|
706
|
+
delete nextValues[oldSectionKey];
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
return { nextDynamicSections: updatedSections, nextValues };
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Pure comment CRUD logic.
|
|
714
|
+
*
|
|
715
|
+
* - Updates submissionResponseData structure for comments/draftComments.
|
|
716
|
+
* - Does NOT perform any network calls.
|
|
717
|
+
* - Returns persistence intent for caller.
|
|
718
|
+
*/
|
|
719
|
+
function applyQuestionCommentCrud(params) {
|
|
720
|
+
var _a, _b, _c, _d, _e;
|
|
721
|
+
const { actionType, questionIdentifierKey, commentId, currentSubmissionResponseData, clientUserInfo } = params;
|
|
722
|
+
const parts = (questionIdentifierKey || '').split('.');
|
|
723
|
+
const sectionKey = parts[0] || '';
|
|
724
|
+
const questionKey = parts[1] || '';
|
|
725
|
+
// Defensive: if malformed key, do nothing.
|
|
726
|
+
if (!sectionKey || !questionKey) {
|
|
727
|
+
return {
|
|
728
|
+
sectionKey,
|
|
729
|
+
questionKey,
|
|
730
|
+
nextSubmissionResponseData: currentSubmissionResponseData || {},
|
|
731
|
+
didMutate: false,
|
|
732
|
+
shouldPersist: false,
|
|
733
|
+
persistIntent: 'none',
|
|
734
|
+
commentId,
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
const next = structuredClone(currentSubmissionResponseData || {});
|
|
738
|
+
// ensure structure exists WITHOUT overwriting existing data
|
|
739
|
+
if (!next[sectionKey])
|
|
740
|
+
next[sectionKey] = {};
|
|
741
|
+
if (!next[sectionKey][questionKey]) {
|
|
742
|
+
// create defaults but do not clobber any existing saved shape from submissionResponseData
|
|
743
|
+
next[sectionKey][questionKey] = {
|
|
744
|
+
value: '',
|
|
745
|
+
comments: [],
|
|
746
|
+
draftComments: [],
|
|
747
|
+
metadata: {},
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
// ensure arrays/objects exist so later code can safely push/filter
|
|
752
|
+
next[sectionKey][questionKey].comments = (_a = next[sectionKey][questionKey].comments) !== null && _a !== void 0 ? _a : [];
|
|
753
|
+
next[sectionKey][questionKey].draftComments = (_b = next[sectionKey][questionKey].draftComments) !== null && _b !== void 0 ? _b : [];
|
|
754
|
+
next[sectionKey][questionKey].metadata = (_c = next[sectionKey][questionKey].metadata) !== null && _c !== void 0 ? _c : {};
|
|
755
|
+
}
|
|
756
|
+
// normalize draftComments to array if needed (back-compat)
|
|
757
|
+
const maybeDraft = next[sectionKey][questionKey].draftComments;
|
|
758
|
+
if (maybeDraft && !Array.isArray(maybeDraft)) {
|
|
759
|
+
next[sectionKey][questionKey].draftComments = [maybeDraft];
|
|
760
|
+
}
|
|
761
|
+
else if (!maybeDraft) {
|
|
762
|
+
next[sectionKey][questionKey].draftComments = [];
|
|
763
|
+
}
|
|
764
|
+
const commentsArr = next[sectionKey][questionKey].comments || [];
|
|
765
|
+
const draftsArr = next[sectionKey][questionKey].draftComments || [];
|
|
766
|
+
let didMutate = false;
|
|
767
|
+
switch (actionType) {
|
|
768
|
+
case 'add': {
|
|
769
|
+
const newDraft = {
|
|
770
|
+
value: '',
|
|
771
|
+
commentId: v4(),
|
|
772
|
+
isTempComment: true,
|
|
773
|
+
timestamp: null,
|
|
774
|
+
};
|
|
775
|
+
// put new draft first so activeDraft === draftComments[0] matches UX
|
|
776
|
+
next[sectionKey][questionKey].draftComments = [newDraft, ...draftsArr];
|
|
777
|
+
didMutate = true;
|
|
778
|
+
break;
|
|
779
|
+
}
|
|
780
|
+
case 'save': {
|
|
781
|
+
const draftIdx = draftsArr.findIndex((d) => d.commentId === commentId);
|
|
782
|
+
if (draftIdx === -1) {
|
|
783
|
+
return {
|
|
784
|
+
sectionKey,
|
|
785
|
+
questionKey,
|
|
786
|
+
nextSubmissionResponseData: next,
|
|
787
|
+
didMutate: false,
|
|
788
|
+
shouldPersist: false,
|
|
789
|
+
persistIntent: 'none',
|
|
790
|
+
commentId,
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
const draft = draftsArr[draftIdx];
|
|
794
|
+
const saveTimestamp = draft.timestamp || new Date().toISOString();
|
|
795
|
+
const savedComment = Object.assign(Object.assign({}, draft), { timestamp: saveTimestamp, editedTimestamp: new Date().toISOString(), userId: clientUserInfo === null || clientUserInfo === void 0 ? void 0 : clientUserInfo.id, userDisplayName: clientUserInfo === null || clientUserInfo === void 0 ? void 0 : clientUserInfo.displayName, isDraftComment: false });
|
|
796
|
+
const existingIdx = commentsArr.findIndex((c) => c.commentId === commentId);
|
|
797
|
+
if (existingIdx !== -1) {
|
|
798
|
+
const newComments = [...commentsArr];
|
|
799
|
+
newComments[existingIdx] = Object.assign({}, savedComment);
|
|
800
|
+
next[sectionKey][questionKey].comments = newComments;
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
next[sectionKey][questionKey].comments = [...commentsArr, savedComment];
|
|
804
|
+
}
|
|
805
|
+
next[sectionKey][questionKey].draftComments = draftsArr.filter((d) => d.commentId !== commentId);
|
|
806
|
+
didMutate = true;
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
case 'edit': {
|
|
810
|
+
const saved = commentsArr.find((c) => c.commentId === commentId);
|
|
811
|
+
if (!saved) {
|
|
812
|
+
return {
|
|
813
|
+
sectionKey,
|
|
814
|
+
questionKey,
|
|
815
|
+
nextSubmissionResponseData: next,
|
|
816
|
+
didMutate: false,
|
|
817
|
+
shouldPersist: false,
|
|
818
|
+
persistIntent: 'none',
|
|
819
|
+
commentId,
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
const draftFromSaved = Object.assign(Object.assign({}, saved), { isDraftComment: true, timestamp: null });
|
|
823
|
+
next[sectionKey][questionKey].draftComments = [draftFromSaved, ...draftsArr];
|
|
824
|
+
next[sectionKey][questionKey].comments = commentsArr;
|
|
825
|
+
didMutate = true;
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
case 'delete': {
|
|
829
|
+
next[sectionKey][questionKey].comments = commentsArr.map((c) => {
|
|
830
|
+
if (c.commentId === commentId) {
|
|
831
|
+
return Object.assign(Object.assign({}, c), { isDeleted: true, value: '', editedTimestamp: new Date().toISOString() });
|
|
832
|
+
}
|
|
833
|
+
return c;
|
|
834
|
+
});
|
|
835
|
+
didMutate = true;
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
case 'editClose': {
|
|
839
|
+
next[sectionKey][questionKey].draftComments = draftsArr.filter((d) => d.commentId !== commentId);
|
|
840
|
+
didMutate = true;
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
const shouldPersist = actionType === 'save' || actionType === 'delete';
|
|
845
|
+
// For the dedicated comments API we only ever need the saved comment value.
|
|
846
|
+
let valueToPersist;
|
|
847
|
+
if (actionType === 'save') {
|
|
848
|
+
const saved = (((_e = (_d = next === null || next === void 0 ? void 0 : next[sectionKey]) === null || _d === void 0 ? void 0 : _d[questionKey]) === null || _e === void 0 ? void 0 : _e.comments) || []).find((c) => c.commentId === commentId);
|
|
849
|
+
valueToPersist = saved === null || saved === void 0 ? void 0 : saved.value;
|
|
850
|
+
}
|
|
851
|
+
return {
|
|
852
|
+
sectionKey,
|
|
853
|
+
questionKey,
|
|
854
|
+
nextSubmissionResponseData: next,
|
|
855
|
+
didMutate,
|
|
856
|
+
shouldPersist,
|
|
857
|
+
// Caller decides if this becomes public-add vs private-update.
|
|
858
|
+
persistIntent: 'none',
|
|
859
|
+
commentId,
|
|
860
|
+
valueToPersist,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
754
863
|
|
|
755
864
|
const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRenderer extends H {
|
|
756
865
|
constructor(registerHost) {
|
|
@@ -763,17 +872,14 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
763
872
|
this.autoSaveDelay = 2000; // Debounce delay for auto-save in milliseconds (currently disabled)
|
|
764
873
|
this.urlContext = {}; // additional context from URL if needed, eg. generic1, gernic2, generic3, or any initial prepopulated values for form inputs. eg section1.question1 = 'some value'
|
|
765
874
|
this.currentValues = {}; // values of the current form state
|
|
766
|
-
this.formInputSeedValues = {}; // support for a initial set of values to seed the form with
|
|
767
875
|
this.submitSuccessful = false;
|
|
768
876
|
this.isLoading = false;
|
|
769
877
|
this.isSaving = false;
|
|
770
878
|
this.isSubmitted = false;
|
|
771
|
-
this.saveErrorMessage = null;
|
|
772
879
|
this.dynamicSections = [];
|
|
773
880
|
this.isUpdatingSections = false;
|
|
774
881
|
this.reRenderKey = 0;
|
|
775
|
-
this.
|
|
776
|
-
this.isUserUpdatedSections = false;
|
|
882
|
+
this.hasUnsavedChanges = false;
|
|
777
883
|
this.sideSheetFollowUpFormsList = []; // used for follow up forms.
|
|
778
884
|
this.followUpSideSheetTotalItems = 0; // used for follow up forms.
|
|
779
885
|
this.isFollowUpFormsSideSheetOpen = false; // used for follow up forms.
|
|
@@ -793,9 +899,19 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
793
899
|
};
|
|
794
900
|
this.followUpParentSectionKey = ''; // used for follow up forms.
|
|
795
901
|
this.followUpParentQuestionKey = ''; // used for follow up forms.
|
|
902
|
+
// Feature flags for public mode. Keep capabilities in the handlers,
|
|
903
|
+
// but disable UX / automatic flows for now.
|
|
904
|
+
this.publicFeatures = {
|
|
905
|
+
allowDraftSave: false, // public cannot save and return later
|
|
906
|
+
allowComments: true, // public can add comments locally; persistence handled by backend rules
|
|
907
|
+
allowFollowUpForms: false, // public does not support follow-up forms
|
|
908
|
+
};
|
|
796
909
|
this.handleLaunchFollowUpFormSideSheet = async (e) => {
|
|
797
910
|
try {
|
|
798
|
-
|
|
911
|
+
// Public forms do not support follow-up forms.
|
|
912
|
+
if (this.isPublic) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
799
915
|
this.followUpParentSectionKey = e.detail.sectionKey;
|
|
800
916
|
this.followUpParentQuestionKey = e.detail.questionKey;
|
|
801
917
|
await this.loadFollowUpForms(this.followUpParentSectionKey, this.followUpParentQuestionKey);
|
|
@@ -814,75 +930,20 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
814
930
|
this.isUpdatingSections = true;
|
|
815
931
|
this.isLoading = true;
|
|
816
932
|
try {
|
|
817
|
-
const
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
// Find existing repeat group indices
|
|
822
|
-
const repeatKeys = findRepeatGroupKeys(cloningSectionName, this.currentValues);
|
|
823
|
-
const nextRepeatIndex = repeatKeys.length > 0 ? Math.max(...repeatKeys) + 1 : 2;
|
|
824
|
-
const clonedSection = Object.assign(Object.assign({}, structuredClone(sectionToClone)), { formQuestions: sectionToClone.formQuestions.map(q => {
|
|
825
|
-
const newQuestionObj = structuredClone(q);
|
|
826
|
-
newQuestionObj.questionIdentifierKey = `${cloningSectionName}_${nextRepeatIndex}.${q.name}`;
|
|
827
|
-
return newQuestionObj;
|
|
828
|
-
}), isOriginalSection: false, sectionPositionSuffix: nextRepeatIndex });
|
|
829
|
-
// Find the last index of this section group
|
|
830
|
-
let insertAtIndex = index;
|
|
831
|
-
for (let i = index + 1; i < this.dynamicSections.length; i++) {
|
|
832
|
-
const s = this.dynamicSections[i];
|
|
833
|
-
if (s.name === cloningSectionName) {
|
|
834
|
-
insertAtIndex = i;
|
|
835
|
-
}
|
|
836
|
-
else {
|
|
837
|
-
break;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
// Insert after the last repeat of the section group
|
|
841
|
-
this.dynamicSections = [
|
|
842
|
-
...this.dynamicSections.slice(0, insertAtIndex + 1),
|
|
843
|
-
clonedSection,
|
|
844
|
-
...this.dynamicSections.slice(insertAtIndex + 1),
|
|
845
|
-
];
|
|
846
|
-
// Create new initial values structure
|
|
847
|
-
const newCurrentValues = structuredClone(this.udpFormSubmission.data.submissionResponseData);
|
|
848
|
-
clonedSection.formQuestions.forEach(q => {
|
|
849
|
-
var _a, _b, _c, _d;
|
|
850
|
-
const newSectionNameWithSuffix = `${cloningSectionName}_${nextRepeatIndex}`;
|
|
851
|
-
const newQuestionName = q.name;
|
|
852
|
-
if (!newCurrentValues[newSectionNameWithSuffix]) {
|
|
853
|
-
newCurrentValues[newSectionNameWithSuffix] = {};
|
|
854
|
-
}
|
|
855
|
-
// Preserve only Paragraph values from the source section; clear others
|
|
856
|
-
let value = '';
|
|
857
|
-
if (q.fieldTypeId === UdpFormsFieldTypeEnum.Paragraph) {
|
|
858
|
-
// Determine source section key (the section being duplicated)
|
|
859
|
-
const sourceSectionKey = sectionToClone.isOriginalSection
|
|
860
|
-
? cloningSectionName
|
|
861
|
-
: `${cloningSectionName}_${sectionToClone.sectionPositionSuffix}`;
|
|
862
|
-
const sourceVal = (_c = (_b = (_a = this.udpFormSubmission.data.submissionResponseData) === null || _a === void 0 ? void 0 : _a[sourceSectionKey]) === null || _b === void 0 ? void 0 : _b[newQuestionName]) === null || _c === void 0 ? void 0 : _c.value;
|
|
863
|
-
let fieldProps = q === null || q === void 0 ? void 0 : q.fieldProperties;
|
|
864
|
-
if (typeof fieldProps === 'string') {
|
|
865
|
-
try {
|
|
866
|
-
fieldProps = JSON.parse(fieldProps || '{}');
|
|
867
|
-
}
|
|
868
|
-
catch (_e) {
|
|
869
|
-
fieldProps = {};
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
const paragraphDefault = (_d = fieldProps === null || fieldProps === void 0 ? void 0 : fieldProps.paragraphText) !== null && _d !== void 0 ? _d : '';
|
|
873
|
-
value = sourceVal !== null && sourceVal !== void 0 ? sourceVal : paragraphDefault;
|
|
874
|
-
}
|
|
875
|
-
newCurrentValues[newSectionNameWithSuffix][newQuestionName] = { value, comments: [], metadata: {} };
|
|
933
|
+
const { nextDynamicSections, nextValues } = computeDuplicateRepeatableSection({
|
|
934
|
+
dynamicSections: this.dynamicSections,
|
|
935
|
+
values: structuredClone(this.udpFormSubmission.data.submissionResponseData || {}),
|
|
936
|
+
index,
|
|
876
937
|
});
|
|
877
|
-
|
|
878
|
-
this.currentValues = Object.assign({},
|
|
879
|
-
this.udpFormSubmission.data.submissionResponseData = Object.assign({},
|
|
938
|
+
this.dynamicSections = nextDynamicSections;
|
|
939
|
+
this.currentValues = Object.assign({}, nextValues);
|
|
940
|
+
this.udpFormSubmission.data.submissionResponseData = Object.assign({}, nextValues);
|
|
880
941
|
this.triggerFormRerender();
|
|
881
942
|
}
|
|
882
943
|
finally {
|
|
883
944
|
this.isUpdatingSections = false;
|
|
884
945
|
this.isLoading = false;
|
|
885
|
-
this.
|
|
946
|
+
this.hasUnsavedChanges = true;
|
|
886
947
|
}
|
|
887
948
|
};
|
|
888
949
|
/**
|
|
@@ -897,137 +958,90 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
897
958
|
this.isUpdatingSections = true;
|
|
898
959
|
this.isLoading = true;
|
|
899
960
|
try {
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
delete newCurrentValues[sectionKeyToDelete];
|
|
905
|
-
// Remove the section from dynamicSections
|
|
906
|
-
const updatedSections = structuredClone(this.dynamicSections);
|
|
907
|
-
updatedSections.splice(index, 1);
|
|
908
|
-
// Shift all later repeatable sections backward by 1
|
|
909
|
-
updatedSections.forEach(section => {
|
|
910
|
-
if (section.name !== deleteSectionName ||
|
|
911
|
-
section.isOriginalSection ||
|
|
912
|
-
section.sectionPositionSuffix <= deleteSuffix) {
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
const oldSuffix = section.sectionPositionSuffix;
|
|
916
|
-
const oldSectionKey = `${deleteSectionName}_${oldSuffix}`;
|
|
917
|
-
const newSuffix = oldSuffix - 1;
|
|
918
|
-
const newSectionKey = `${deleteSectionName}_${newSuffix}`;
|
|
919
|
-
// Update suffix
|
|
920
|
-
section.sectionPositionSuffix = newSuffix;
|
|
921
|
-
// Update questionIdentifierKeys
|
|
922
|
-
section.formQuestions = section.formQuestions.map(q => {
|
|
923
|
-
const newSectionQuestion = structuredClone(q);
|
|
924
|
-
newSectionQuestion.questionIdentifierKey = `${newSectionKey}.${q.name}`;
|
|
925
|
-
return newSectionQuestion;
|
|
926
|
-
});
|
|
927
|
-
// Move data in initial values
|
|
928
|
-
if (newCurrentValues[oldSectionKey]) {
|
|
929
|
-
newCurrentValues[newSectionKey] = structuredClone(newCurrentValues[oldSectionKey]);
|
|
930
|
-
delete newCurrentValues[oldSectionKey];
|
|
931
|
-
}
|
|
961
|
+
const { nextDynamicSections, nextValues } = computeDeleteRepeatableSection({
|
|
962
|
+
dynamicSections: this.dynamicSections,
|
|
963
|
+
values: Object.assign({}, (this.udpFormSubmission.data.submissionResponseData || {})),
|
|
964
|
+
index,
|
|
932
965
|
});
|
|
933
|
-
this.dynamicSections =
|
|
934
|
-
this.currentValues = Object.assign({},
|
|
935
|
-
this.udpFormSubmission.data.submissionResponseData = Object.assign({},
|
|
966
|
+
this.dynamicSections = nextDynamicSections;
|
|
967
|
+
this.currentValues = Object.assign({}, nextValues);
|
|
968
|
+
this.udpFormSubmission.data.submissionResponseData = Object.assign({}, nextValues);
|
|
936
969
|
this.triggerFormRerender();
|
|
937
970
|
}
|
|
938
971
|
finally {
|
|
939
972
|
this.isUpdatingSections = false;
|
|
940
973
|
this.isLoading = false;
|
|
941
|
-
this.
|
|
974
|
+
this.hasUnsavedChanges = true;
|
|
942
975
|
}
|
|
943
976
|
};
|
|
944
977
|
/**
|
|
945
978
|
* Auto save (background save)
|
|
946
979
|
*/
|
|
947
980
|
this.performBackgroundSaveAndUpdateLocalSubmissionState = async (values) => {
|
|
948
|
-
|
|
949
|
-
|
|
981
|
+
return this.performBackgroundSaveAndUpdateLocalSubmissionStateInternal(values);
|
|
982
|
+
};
|
|
983
|
+
this.performBackgroundSaveAndUpdateLocalSubmissionStateInternal = async (values, opts) => {
|
|
984
|
+
var _a, _b;
|
|
985
|
+
const allowUrlReplace = (_a = opts === null || opts === void 0 ? void 0 : opts.allowUrlReplace) !== null && _a !== void 0 ? _a : !this.isPublic;
|
|
986
|
+
const forceCreateDraftIfMissingId = (_b = opts === null || opts === void 0 ? void 0 : opts.forceCreateDraftIfMissingId) !== null && _b !== void 0 ? _b : false;
|
|
987
|
+
// Public mode: disable draft save flows by default
|
|
988
|
+
if (this.isPublic && !this.publicFeatures.allowDraftSave && !forceCreateDraftIfMissingId) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
950
991
|
try {
|
|
951
|
-
|
|
952
|
-
this.
|
|
953
|
-
|
|
954
|
-
if (!new URLSearchParams(window.location.search).has('udpf_submissionId')) {
|
|
992
|
+
await this.formSubmissionHandler.saveCurrentFormSubmissionState(values, this.udpFormSubmission);
|
|
993
|
+
await this.refreshSubmissionAndSyncState();
|
|
994
|
+
if (allowUrlReplace && !new URLSearchParams(window.location.search).has('udpf_submissionId')) {
|
|
955
995
|
// replace the current entry's query string with udpf_submissionId without needing to know the path
|
|
956
|
-
|
|
996
|
+
replaceUrlWithSubmissionId(this.udpFormSubmission.id, this.history);
|
|
957
997
|
}
|
|
958
998
|
}
|
|
959
999
|
catch (error) {
|
|
960
|
-
this.saveErrorMessage = 'Failed to save form data';
|
|
961
1000
|
}
|
|
962
1001
|
finally {
|
|
963
1002
|
this.isSaving = false;
|
|
964
|
-
this.
|
|
965
|
-
this.isFormDirty = false;
|
|
1003
|
+
this.hasUnsavedChanges = false;
|
|
966
1004
|
}
|
|
967
1005
|
};
|
|
1006
|
+
this.refreshSubmissionAndSyncState = async () => {
|
|
1007
|
+
var _a, _b;
|
|
1008
|
+
if (!((_a = this.udpFormSubmission) === null || _a === void 0 ? void 0 : _a.id))
|
|
1009
|
+
return;
|
|
1010
|
+
const updated = await this.formSubmissionHandler.fetchAndPopulateUdpFormSubmissionObj(this.udpFormSubmission);
|
|
1011
|
+
this.udpFormSubmission = updated;
|
|
1012
|
+
this.currentValues = Object.assign({}, (((_b = updated.data) === null || _b === void 0 ? void 0 : _b.submissionResponseData) || {}));
|
|
1013
|
+
this.isSubmitted = updated.status === UdpFormsSubmissionStatusEnum.Submitted;
|
|
1014
|
+
};
|
|
968
1015
|
/**
|
|
969
|
-
* Handle the user saving or deleting a comment
|
|
1016
|
+
* Handle the user saving or deleting a comment (pre-submit, private forms only)
|
|
970
1017
|
*/
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const updatedUdpFormSubmission = await this.formSubmissionHandler.saveFormSubmissionComments(values, this.udpFormSubmission);
|
|
977
|
-
this.udpFormSubmission = updatedUdpFormSubmission;
|
|
978
|
-
this.currentValues = Object.assign({}, (((_a = updatedUdpFormSubmission.data) === null || _a === void 0 ? void 0 : _a.submissionResponseData) || {}));
|
|
979
|
-
this.enqueueSnackbar('Saved sucessfully.', {
|
|
980
|
-
variant: 'success',
|
|
981
|
-
anchorOrigin: { vertical: 'top', horizontal: 'center' },
|
|
982
|
-
});
|
|
983
|
-
}
|
|
984
|
-
catch (error) {
|
|
985
|
-
this.enqueueSnackbar('There was an error saving.', {
|
|
986
|
-
variant: 'error',
|
|
987
|
-
anchorOrigin: { vertical: 'top', horizontal: 'center' },
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
finally {
|
|
991
|
-
this.isSaving = false;
|
|
992
|
-
this.isUserUpdatedSections = false;
|
|
993
|
-
this.isFormDirty = false;
|
|
994
|
-
}
|
|
995
|
-
};
|
|
1018
|
+
// NOTE: comment saves for non-submitted owner flows are now handled by the standard
|
|
1019
|
+
// draft save path (manual/background save). Dedicated comment endpoints are used
|
|
1020
|
+
// only for:
|
|
1021
|
+
// 1) submitted submissions, OR
|
|
1022
|
+
// 2) non-owner viewers adding/updating/deleting comments.
|
|
996
1023
|
/**
|
|
997
|
-
* Manual save function - debounced to 5 seconds
|
|
1024
|
+
* Manual save function - debounced to 5 seconds (private forms only)
|
|
998
1025
|
*/
|
|
999
1026
|
this.handleManualSave = async (values) => {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
// return;
|
|
1003
|
-
// }
|
|
1004
|
-
var _a;
|
|
1027
|
+
if (this.isPublic && !this.publicFeatures.allowDraftSave)
|
|
1028
|
+
return;
|
|
1005
1029
|
this.isSaving = true;
|
|
1006
|
-
this.saveErrorMessage = null;
|
|
1007
1030
|
try {
|
|
1008
|
-
|
|
1009
|
-
this.
|
|
1010
|
-
this.currentValues = Object.assign({}, (((_a = updatedUdpFormSubmission.data) === null || _a === void 0 ? void 0 : _a.submissionResponseData) || {}));
|
|
1031
|
+
await this.formSubmissionHandler.saveCurrentFormSubmissionState(values, this.udpFormSubmission);
|
|
1032
|
+
await this.refreshSubmissionAndSyncState();
|
|
1011
1033
|
if (!new URLSearchParams(window.location.search).has('udpf_submissionId')) {
|
|
1012
1034
|
// replace the current entry's query string with udpf_submissionId without needing to know the path
|
|
1013
|
-
|
|
1035
|
+
replaceUrlWithSubmissionId(this.udpFormSubmission.id, this.history);
|
|
1014
1036
|
}
|
|
1015
|
-
|
|
1016
|
-
variant: 'success',
|
|
1017
|
-
anchorOrigin: { vertical: 'top', horizontal: 'center' },
|
|
1018
|
-
});
|
|
1037
|
+
enqueueSnackbarSuccess('Form saved successfully.', this.enqueueSnackbar);
|
|
1019
1038
|
}
|
|
1020
1039
|
catch (error) {
|
|
1021
|
-
|
|
1022
|
-
variant: 'error',
|
|
1023
|
-
anchorOrigin: { vertical: 'top', horizontal: 'center' },
|
|
1024
|
-
});
|
|
1025
|
-
this.saveErrorMessage = 'Failed to save form data';
|
|
1040
|
+
enqueueSnackbarError('There was an error saving this form', this.enqueueSnackbar);
|
|
1026
1041
|
}
|
|
1027
1042
|
finally {
|
|
1028
1043
|
this.isSaving = false;
|
|
1029
|
-
this.
|
|
1030
|
-
this.isFormDirty = false;
|
|
1044
|
+
this.hasUnsavedChanges = false;
|
|
1031
1045
|
}
|
|
1032
1046
|
};
|
|
1033
1047
|
this.handleFormChange = (values) => {
|
|
@@ -1094,7 +1108,9 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
1094
1108
|
// Listen for launchFollowUpFormSideSheet event from udp-question
|
|
1095
1109
|
this.el.addEventListener('launchFollowUpFormSideSheet', (e) => this.handleLaunchFollowUpFormSideSheet(e));
|
|
1096
1110
|
this.el.addEventListener('formDirtyChange', (e) => {
|
|
1097
|
-
|
|
1111
|
+
// Preserve any previously-detected unsaved changes (e.g. repeatable section add/delete)
|
|
1112
|
+
// so a later false emission doesn't hide the save icon.
|
|
1113
|
+
this.hasUnsavedChanges = this.hasUnsavedChanges || e.detail;
|
|
1098
1114
|
});
|
|
1099
1115
|
}
|
|
1100
1116
|
}
|
|
@@ -1103,17 +1119,24 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
1103
1119
|
this.isLoading = true;
|
|
1104
1120
|
try {
|
|
1105
1121
|
// Get client user info if available
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
this.clientUserInfo.
|
|
1109
|
-
|
|
1110
|
-
|
|
1122
|
+
if (this.isPublic) {
|
|
1123
|
+
this.clientUserInfo.id = '00000000-0000-0000-0000-000000000001';
|
|
1124
|
+
this.clientUserInfo.displayName = 'Anonymous';
|
|
1125
|
+
}
|
|
1126
|
+
else {
|
|
1127
|
+
const user = (_a = this.getUserCallback) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
1128
|
+
if (user) {
|
|
1129
|
+
this.clientUserInfo.id = user.id || null;
|
|
1130
|
+
this.clientUserInfo.displayName = user.name || null;
|
|
1131
|
+
this.clientUserInfo.email = user.email || null;
|
|
1132
|
+
}
|
|
1111
1133
|
}
|
|
1112
1134
|
this.formSubmissionHandler = FormSubmissionHandlerFactory.create(this.isPublic, this.clientUserInfo.id);
|
|
1113
1135
|
this.udpFormSubmission = new UdpFormSubmission({ id: this.submissionId, formId: this.formId, formVersion: this.version, unityUserId: this.clientUserInfo.id, generic1: this.urlContext.generic1, generic2: this.urlContext.generic2, generic3: this.urlContext.generic3 });
|
|
1114
1136
|
// fetch existing submission from Udp.FormSubmission if submissionId is provided
|
|
1115
1137
|
// take exisitng object, and populate with db values, and return new obj with updated values
|
|
1116
1138
|
if (this.submissionId) {
|
|
1139
|
+
// Public users should not open private forms; we rely on backend auth to block.
|
|
1117
1140
|
this.udpFormSubmission = await this.formSubmissionHandler.fetchAndPopulateUdpFormSubmissionObj(this.udpFormSubmission);
|
|
1118
1141
|
}
|
|
1119
1142
|
// get the master form from Udp.Form
|
|
@@ -1172,157 +1195,117 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
1172
1195
|
async handleSubmit(values) {
|
|
1173
1196
|
this.isLoading = true;
|
|
1174
1197
|
try {
|
|
1175
|
-
|
|
1198
|
+
await this.formSubmissionHandler.finalizeFormSubmissionState(values, this.udpFormSubmission);
|
|
1199
|
+
await this.refreshSubmissionAndSyncState();
|
|
1176
1200
|
this.submitSuccessful = true;
|
|
1177
1201
|
}
|
|
1178
1202
|
catch (error) {
|
|
1179
|
-
|
|
1180
|
-
variant: 'error',
|
|
1181
|
-
anchorOrigin: { vertical: 'top', horizontal: 'center' },
|
|
1182
|
-
});
|
|
1203
|
+
enqueueSnackbarError('There was an error submitting this form', this.enqueueSnackbar);
|
|
1183
1204
|
throw error;
|
|
1184
1205
|
}
|
|
1185
1206
|
finally {
|
|
1186
1207
|
this.isLoading = false;
|
|
1187
1208
|
}
|
|
1188
1209
|
}
|
|
1189
|
-
replaceUrlWithSubmissionId(submissionId) {
|
|
1190
|
-
// build a URL that preserves the current pathname + hash but replaces the query string
|
|
1191
|
-
const pathname = typeof window !== 'undefined' ? window.location.pathname : `/page/${UdpFormsPageIdEnum.FormRendererPageId}`;
|
|
1192
|
-
const hash = typeof window !== 'undefined' ? window.location.hash : '';
|
|
1193
|
-
const newUrl = `${pathname}?udpf_submissionId=${submissionId}${hash}`;
|
|
1194
|
-
const h = this.history;
|
|
1195
|
-
// Prefer history.replace when available, handle react-router v6 navigate function, fallback to push or native replaceState
|
|
1196
|
-
if (h) {
|
|
1197
|
-
if (typeof h.replace === 'function') {
|
|
1198
|
-
h.replace(newUrl);
|
|
1199
|
-
return;
|
|
1200
|
-
}
|
|
1201
|
-
if (typeof h.push === 'function') {
|
|
1202
|
-
h.push(newUrl);
|
|
1203
|
-
return;
|
|
1204
|
-
}
|
|
1205
|
-
// react-router v6 exposes a navigate function
|
|
1206
|
-
if (typeof h === 'function') {
|
|
1207
|
-
try {
|
|
1208
|
-
h(newUrl, { replace: true });
|
|
1209
|
-
}
|
|
1210
|
-
catch (_a) {
|
|
1211
|
-
h(newUrl);
|
|
1212
|
-
}
|
|
1213
|
-
return;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
if (typeof window !== 'undefined' && window.history && typeof window.history.replaceState === 'function') {
|
|
1217
|
-
window.history.replaceState({}, '', newUrl);
|
|
1218
|
-
}
|
|
1219
|
-
else if (typeof window !== 'undefined') {
|
|
1220
|
-
window.location.href = newUrl;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
1210
|
async handleQuestionCommentLiveCRUD(e) {
|
|
1224
|
-
var _a, _b, _c;
|
|
1211
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1212
|
+
if (this.isPublic && !this.publicFeatures.allowComments)
|
|
1213
|
+
return;
|
|
1225
1214
|
try {
|
|
1226
1215
|
const { type, questionIdentifierKey, commentId } = e.detail;
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1216
|
+
const result = applyQuestionCommentCrud({
|
|
1217
|
+
actionType: type,
|
|
1218
|
+
questionIdentifierKey,
|
|
1219
|
+
commentId,
|
|
1220
|
+
currentSubmissionResponseData: this.udpFormSubmission.data.submissionResponseData || {},
|
|
1221
|
+
clientUserInfo: this.clientUserInfo,
|
|
1222
|
+
});
|
|
1223
|
+
const newCurrentValues = result.nextSubmissionResponseData;
|
|
1224
|
+
// keep submission state in sync
|
|
1225
|
+
this.udpFormSubmission.data.submissionResponseData = Object.assign({}, newCurrentValues);
|
|
1226
|
+
// Edit-ish actions are UI-only (open/close draft editor). They must update currentValues
|
|
1227
|
+
// even though they don't persist.
|
|
1228
|
+
if (type === 'edit' || type === 'editClose' || type === 'add') {
|
|
1229
|
+
this.currentValues = Object.assign({}, newCurrentValues);
|
|
1240
1230
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
switch (type) {
|
|
1258
|
-
case 'add': {
|
|
1259
|
-
const newDraft = {
|
|
1260
|
-
value: '',
|
|
1261
|
-
commentId: v4(),
|
|
1262
|
-
isTempComment: true,
|
|
1263
|
-
timestamp: null,
|
|
1264
|
-
};
|
|
1265
|
-
// put new draft first so activeDraft === draftComments[0] matches UX
|
|
1266
|
-
newCurrentValues[sectionName][questionName].draftComments = [newDraft, ...draftsArr];
|
|
1267
|
-
break;
|
|
1268
|
-
}
|
|
1269
|
-
case 'save': {
|
|
1270
|
-
// find draft by id
|
|
1271
|
-
const draftIdx = draftsArr.findIndex(d => d.commentId === commentId);
|
|
1272
|
-
if (draftIdx === -1)
|
|
1273
|
-
return;
|
|
1274
|
-
const draft = draftsArr[draftIdx];
|
|
1275
|
-
const saveTimestamp = draft.timestamp || new Date().toISOString();
|
|
1276
|
-
const savedComment = Object.assign(Object.assign({}, draft), { timestamp: saveTimestamp, editedTimestamp: new Date().toISOString(), userId: this.clientUserInfo.id, userDisplayName: this.clientUserInfo.displayName, isDraftComment: false });
|
|
1277
|
-
// If a saved comment with same id exists, replace it. Otherwise append.
|
|
1278
|
-
const existingIdx = commentsArr.findIndex(c => c.commentId === commentId);
|
|
1279
|
-
if (existingIdx !== -1) {
|
|
1280
|
-
const newComments = [...commentsArr];
|
|
1281
|
-
newComments[existingIdx] = Object.assign({}, savedComment);
|
|
1282
|
-
newCurrentValues[sectionName][questionName].comments = newComments;
|
|
1231
|
+
// if it's a save or delete action, persist immediately
|
|
1232
|
+
if (result.shouldPersist) {
|
|
1233
|
+
const submissionIsSubmitted = ((_a = this.udpFormSubmission) === null || _a === void 0 ? void 0 : _a.status) === UdpFormsSubmissionStatusEnum.Submitted;
|
|
1234
|
+
const submissionIsOwnedByCurrentUser = ((_b = this.udpFormSubmission) === null || _b === void 0 ? void 0 : _b.unityUserId) === this.clientUserInfo.id;
|
|
1235
|
+
// Detect whether this save is updating an existing saved comment vs creating a new one.
|
|
1236
|
+
// IMPORTANT: check against the *previous* persisted state (before applyQuestionCommentCrud),
|
|
1237
|
+
// because once we apply 'save' locally the comment will exist locally even if it's new.
|
|
1238
|
+
const existingSavedComment = (((_e = (_d = (_c = ((this.currentValues || {}))) === null || _c === void 0 ? void 0 : _c[result.sectionKey]) === null || _d === void 0 ? void 0 : _d[result.questionKey]) === null || _e === void 0 ? void 0 : _e.comments) || [])
|
|
1239
|
+
.find((c) => (c === null || c === void 0 ? void 0 : c.commentId) === result.commentId && !(c === null || c === void 0 ? void 0 : c.isDeleted));
|
|
1240
|
+
// Use dedicated comment APIs in 2 scenarios:
|
|
1241
|
+
// 1) Once submitted (public+private) to avoid overwriting submission data.
|
|
1242
|
+
// 2) When viewed by a non-owner (read-only mode), since they can't save the whole submission.
|
|
1243
|
+
if (submissionIsSubmitted || !submissionIsOwnedByCurrentUser) {
|
|
1244
|
+
if (!((_f = this.udpFormSubmission) === null || _f === void 0 ? void 0 : _f.id)) {
|
|
1245
|
+
// can't persist without an ID; keep local only
|
|
1246
|
+
this.currentValues = Object.assign({}, newCurrentValues);
|
|
1283
1247
|
}
|
|
1284
|
-
else {
|
|
1285
|
-
|
|
1248
|
+
else if (type === 'save') {
|
|
1249
|
+
const valueToPersist = result.valueToPersist;
|
|
1250
|
+
if (valueToPersist) {
|
|
1251
|
+
try {
|
|
1252
|
+
// If this comment already exists, update it; otherwise add it.
|
|
1253
|
+
if (existingSavedComment) {
|
|
1254
|
+
await this.formSubmissionHandler.updateComment(this.udpFormSubmission, {
|
|
1255
|
+
sectionKey: result.sectionKey,
|
|
1256
|
+
questionKey: result.questionKey,
|
|
1257
|
+
value: valueToPersist,
|
|
1258
|
+
commentId: result.commentId,
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
else {
|
|
1262
|
+
await this.formSubmissionHandler.addComment(this.udpFormSubmission, {
|
|
1263
|
+
sectionKey: result.sectionKey,
|
|
1264
|
+
questionKey: result.questionKey,
|
|
1265
|
+
value: valueToPersist,
|
|
1266
|
+
commentId: result.commentId,
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
await this.refreshSubmissionAndSyncState();
|
|
1270
|
+
enqueueSnackbarSuccess(existingSavedComment ? 'Comment updated.' : 'Comment added.', this.enqueueSnackbar);
|
|
1271
|
+
}
|
|
1272
|
+
catch (error) {
|
|
1273
|
+
enqueueSnackbarError(existingSavedComment ? 'Failed to update comment.' : 'Failed to add comment.', this.enqueueSnackbar);
|
|
1274
|
+
throw error;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1286
1277
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
const draftFromSaved = Object.assign(Object.assign({}, saved), { isDraftComment: true, timestamp: null });
|
|
1297
|
-
// Add this draft at the front; keep saved comment intact so content doesn't disappear.
|
|
1298
|
-
newCurrentValues[sectionName][questionName].draftComments = [draftFromSaved, ...draftsArr];
|
|
1299
|
-
newCurrentValues[sectionName][questionName].comments = commentsArr;
|
|
1300
|
-
break;
|
|
1301
|
-
}
|
|
1302
|
-
case 'delete': {
|
|
1303
|
-
// mark the comment as deleted and clear its value, then persist.
|
|
1304
|
-
newCurrentValues[sectionName][questionName].comments = commentsArr.map(c => {
|
|
1305
|
-
if (c.commentId === commentId) {
|
|
1306
|
-
return Object.assign(Object.assign({}, c), { isDeleted: true, value: '', editedTimestamp: new Date().toISOString() });
|
|
1278
|
+
else if (type === 'delete') {
|
|
1279
|
+
try {
|
|
1280
|
+
await this.formSubmissionHandler.deleteComment(this.udpFormSubmission, {
|
|
1281
|
+
sectionKey: result.sectionKey,
|
|
1282
|
+
questionKey: result.questionKey,
|
|
1283
|
+
commentId: result.commentId,
|
|
1284
|
+
});
|
|
1285
|
+
await this.refreshSubmissionAndSyncState();
|
|
1286
|
+
enqueueSnackbarSuccess('Comment deleted.', this.enqueueSnackbar);
|
|
1307
1287
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1288
|
+
catch (error) {
|
|
1289
|
+
enqueueSnackbarError('Failed to delete comment.', this.enqueueSnackbar);
|
|
1290
|
+
throw error;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1311
1293
|
}
|
|
1312
|
-
|
|
1313
|
-
//
|
|
1314
|
-
|
|
1315
|
-
|
|
1294
|
+
else {
|
|
1295
|
+
// Not submitted yet:
|
|
1296
|
+
// - public: persist nothing beyond local state (submission is created on submit)
|
|
1297
|
+
// - private + owner: comment edits are persisted via standard submission save
|
|
1298
|
+
if (this.isPublic) {
|
|
1299
|
+
this.currentValues = Object.assign({}, newCurrentValues);
|
|
1300
|
+
}
|
|
1301
|
+
else {
|
|
1302
|
+
await this.handleManualSave(newCurrentValues);
|
|
1303
|
+
}
|
|
1316
1304
|
}
|
|
1317
1305
|
}
|
|
1318
|
-
// if it's a save or delete action, persist immediately
|
|
1319
|
-
if (type === 'save' || type === 'delete') {
|
|
1320
|
-
this.udpFormSubmission.data.submissionResponseData = Object.assign({}, newCurrentValues);
|
|
1321
|
-
await this.handleCommmentUpdate(newCurrentValues);
|
|
1322
|
-
}
|
|
1323
1306
|
else {
|
|
1307
|
+
// Non-persisting actions update UI state only
|
|
1324
1308
|
this.currentValues = Object.assign({}, newCurrentValues);
|
|
1325
|
-
this.udpFormSubmission.data.submissionResponseData = Object.assign({}, newCurrentValues);
|
|
1326
1309
|
}
|
|
1327
1310
|
}
|
|
1328
1311
|
catch (error) {
|
|
@@ -1335,7 +1318,6 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
1335
1318
|
async loadFollowUpForms(sectionKey, questionKey) {
|
|
1336
1319
|
var _a;
|
|
1337
1320
|
this.isLoading = true;
|
|
1338
|
-
this.saveErrorMessage = '';
|
|
1339
1321
|
try {
|
|
1340
1322
|
const response = await fetchLatestForms(this.followUpSideSheetListPageNumber, this.FOLLOW_UP_SIDE_SHEET_PAGE_SIZE, [{ searchField: 'type', searchOperator: '=', searchValue: UdpFormsTypeEnum.FollowUp }], { sortDirection: 'DESC', sortColumn: 'lastModifiedOn' });
|
|
1341
1323
|
const data = response.data;
|
|
@@ -1347,7 +1329,6 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
1347
1329
|
}
|
|
1348
1330
|
catch (err) {
|
|
1349
1331
|
console.error('Failed to follow up form.', err);
|
|
1350
|
-
this.saveErrorMessage = 'Failed to follow up forms.';
|
|
1351
1332
|
}
|
|
1352
1333
|
finally {
|
|
1353
1334
|
this.isLoading = false;
|
|
@@ -1365,11 +1346,17 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
1365
1346
|
await this.loadFollowUpForms(this.followUpParentSectionKey, this.followUpParentQuestionKey);
|
|
1366
1347
|
}
|
|
1367
1348
|
renderFollowUpSideSheet() {
|
|
1349
|
+
if (this.isPublic)
|
|
1350
|
+
return null;
|
|
1368
1351
|
return (h("udp-side-sheet", { title: "Link a Follow Up Form", open: this.isFollowUpFormsSideSheetOpen, onUdpSideSheetClose: () => this.handleSideSheetClose(), position: "right", width: "md" }, this.isLoading && this.isFollowUpFormsSideSheetOpen && h("udp-linear-loader", null), h("udp-list-renderer", { itemComponent: "udp-forms-follow-up-list-card", data: this.sideSheetFollowUpFormsList, pagination: true, isServerSide: true, isLoading: this.isLoading, itemsPerPage: this.FOLLOW_UP_SIDE_SHEET_PAGE_SIZE, currentPage: this.followUpSideSheetListPageNumber, totalItems: this.followUpSideSheetTotalItems, onPageChange: e => this.handleSideSheetPageChange(e.detail), componentDataMap: this.componentMap, spacing: 'md' })));
|
|
1369
1352
|
}
|
|
1370
1353
|
isShowManualSaveIcon() {
|
|
1371
|
-
|
|
1372
|
-
|
|
1354
|
+
if (this.isPublic)
|
|
1355
|
+
return false; // cannot save a form in public mode. can only submit.
|
|
1356
|
+
// NOTE: repeatable section add/delete sets hasUnsavedChanges=true, but udp-forms-ui likely
|
|
1357
|
+
// also emits a formDirtyChange event that can override the value back to false.
|
|
1358
|
+
// Keep the save icon sticky once we've detected any unsaved change.
|
|
1359
|
+
const showSaveIcon = !!this.clientUserInfo.id && (this.hasUnsavedChanges);
|
|
1373
1360
|
return showSaveIcon;
|
|
1374
1361
|
}
|
|
1375
1362
|
;
|
|
@@ -1377,7 +1364,8 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
1377
1364
|
return this.isSubmitted || this.udpFormSubmission.unityUserId !== this.clientUserInfo.id;
|
|
1378
1365
|
}
|
|
1379
1366
|
render() {
|
|
1380
|
-
return (h("div", { key: '
|
|
1367
|
+
return (h("div", { key: '37ec47fc7f3ecec70ba7ca3e12717789285be5d8', class: "forms-renderer-container", style: this.isLoading ? { minHeight: '100vh' } : {} }, this.renderFollowUpSideSheet(), h("udp-forms-ui", { udpForm: this.udpForm, currentValues: this.currentValues, udpFormSubmission: this.udpFormSubmission, submitSuccessful: this.submitSuccessful, isSaving: this.isSaving,
|
|
1368
|
+
// saveErrorMessage={this.saveErrorMessage}
|
|
1381
1369
|
// showAutoSaveStatus={this.formSubmissionHandler.supportsAutoSave() && !!this.userId}
|
|
1382
1370
|
readonly: this.isReadOnlyMode, handleSubmit: this.handleSubmit.bind(this), handleSave: values => Promise.resolve(this.debouncedManualSave(values)), handleChange: this.handleFormChange, handleAction: this.triggerAction, handleFinish: this.handleFinish, clientUserInfo: this.clientUserInfo, isSubmitted: this.isSubmitted, dynamicSections: this.dynamicSections, duplicateRepeatableSection: this.duplicateRepeatableSection, deleteRepeatableSection: this.deleteRepeatableSection, key: `form-rerender-key-${this.reRenderKey}`, isShowManualSaveIcon: this.isShowManualSaveIcon(), isLoading: this.isLoading, performBackgroundSaveAndUpdateLocalSubmissionState: this.performBackgroundSaveAndUpdateLocalSubmissionState })));
|
|
1383
1371
|
}
|
|
@@ -1396,17 +1384,14 @@ const UdpFormsRenderer$1 = /*@__PURE__*/ proxyCustomElement(class UdpFormsRender
|
|
|
1396
1384
|
"history": [8],
|
|
1397
1385
|
"urlContext": [16],
|
|
1398
1386
|
"currentValues": [32],
|
|
1399
|
-
"formInputSeedValues": [32],
|
|
1400
1387
|
"submitSuccessful": [32],
|
|
1401
1388
|
"isLoading": [32],
|
|
1402
1389
|
"isSaving": [32],
|
|
1403
1390
|
"isSubmitted": [32],
|
|
1404
|
-
"saveErrorMessage": [32],
|
|
1405
1391
|
"dynamicSections": [32],
|
|
1406
1392
|
"isUpdatingSections": [32],
|
|
1407
1393
|
"reRenderKey": [32],
|
|
1408
|
-
"
|
|
1409
|
-
"isUserUpdatedSections": [32],
|
|
1394
|
+
"hasUnsavedChanges": [32],
|
|
1410
1395
|
"sideSheetFollowUpFormsList": [32],
|
|
1411
1396
|
"followUpSideSheetTotalItems": [32],
|
|
1412
1397
|
"isFollowUpFormsSideSheetOpen": [32],
|