Skip to content

Commit f0b1dd2

Browse files
Revert "feat: FORMS-2670 Client-side auto-save with localStorage for form dat…" (#1788)
This reverts commit 08fc85c.
1 parent 08fc85c commit f0b1dd2

File tree

30 files changed

+36
-1322
lines changed

30 files changed

+36
-1322
lines changed

app/frontend/src/components/designer/FormDesigner.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,6 @@ async function schemaCreateNew() {
405405
allowSubmitterToUploadFile: form.value.allowSubmitterToUploadFile,
406406
enableCopyExistingSubmission: form.value.enableCopyExistingSubmission,
407407
wideFormLayout: form.value.wideFormLayout,
408-
enableAutoSave: form.value.enableAutoSave,
409408
enableStatusUpdates: form.value.enableStatusUpdates,
410409
enableSubmitterRevision: form.value.enableSubmitterRevision,
411410
showAssigneeInSubmissionsTable: form.value.showAssigneeInSubmissionsTable,

app/frontend/src/components/designer/FormViewer.vue

Lines changed: 17 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import _ from 'lodash';
44
import { storeToRefs } from 'pinia';
55
import {
66
computed,
7-
nextTick,
87
onBeforeUpdate,
98
onBeforeUnmount,
109
onMounted,
@@ -15,7 +14,6 @@ import { useI18n } from 'vue-i18n';
1514
import { useRouter } from 'vue-router';
1615
1716
import BaseDialog from '~/components/base/BaseDialog.vue';
18-
import LocalAutosaveRecoveryDialog from '~/components/designer/LocalAutosaveRecoveryDialog.vue';
1917
import FormViewerActions from '~/components/designer/FormViewerActions.vue';
2018
import FormViewerMultiUpload from '~/components/designer/FormViewerMultiUpload.vue';
2119
import templateExtensions from '~/plugins/templateExtensions';
@@ -24,7 +22,6 @@ import { useAppStore } from '~/store/app';
2422
import { useAuthStore } from '~/store/auth';
2523
import { useFormStore } from '~/store/form';
2624
import { useNotificationStore } from '~/store/notification';
27-
import { useLocalAutosave } from '~/composables/useLocalAutosave';
2825
2926
import { isFormPublic } from '~/utils/permissionUtils';
3027
import {
@@ -117,11 +114,6 @@ const authStore = useAuthStore();
117114
const formStore = useFormStore();
118115
const notificationStore = useNotificationStore();
119116
120-
// Local crash-recovery autosave (storage layer)
121-
const localAutosave = useLocalAutosave();
122-
const showLocalRecoveryDialog = ref(false);
123-
const autosaveInitialized = ref(false);
124-
125117
const { config } = storeToRefs(appStore);
126118
const { authenticated, keycloak, tokenParsed, user } = storeToRefs(authStore);
127119
const { downloadedFile, isRTL } = storeToRefs(formStore);
@@ -151,6 +143,7 @@ const shouldDisableFileDownloads = computed(() => {
151143
152144
const viewerOptions = computed(() => {
153145
// Force recomputation of viewerOptions after rerendered formio to prevent duplicate submission update calls
146+
reRenderFormIo.value;
154147
155148
return {
156149
sanitizeConfig: {
@@ -188,26 +181,6 @@ const canSaveDraft = computed(
188181
watch(locale, () => {
189182
reRenderFormIo.value += 1;
190183
});
191-
// === Autosave initialization watcher ===
192-
watch(
193-
() => ({
194-
auth: authenticated.value,
195-
formLoaded: !!form.value?.id,
196-
enableAutoSave: form.value?.enableAutoSave,
197-
userReady: !!authStore.currentUser?.idpUserId,
198-
}),
199-
({ auth, formLoaded, enableAutoSave, userReady }) => {
200-
if (
201-
!autosaveInitialized.value &&
202-
auth &&
203-
formLoaded &&
204-
enableAutoSave &&
205-
userReady
206-
) {
207-
initializeLocalAutosave();
208-
}
209-
}
210-
);
211184
212185
onMounted(async () => {
213186
// load up headers for any External API calls
@@ -224,10 +197,6 @@ onMounted(async () => {
224197
showModal.value = true;
225198
await getFormSchema();
226199
}
227-
228-
// Initialize local autosave after first render
229-
await nextTick();
230-
231200
window.addEventListener('beforeunload', beforeWindowUnload);
232201
233202
reRenderFormIo.value += 1;
@@ -236,9 +205,6 @@ onMounted(async () => {
236205
onBeforeUnmount(() => {
237206
window.removeEventListener('beforeunload', beforeWindowUnload);
238207
clearTimeout(downloadTimeout.value);
239-
240-
// Cleanup local autosave debounce
241-
localAutosave.cleanup();
242208
});
243209
244210
onBeforeUpdate(() => {
@@ -251,102 +217,12 @@ function getCurrentAuthHeader() {
251217
return `Bearer ${keycloak.value.token}`;
252218
}
253219
254-
/**
255-
* Central autosave initialization:
256-
* - Only for authenticated, non-preview, non-readonly forms
257-
* - Only when form.enableAutoSave is true
258-
* - Builds a per-user, per-form(+submission) storage key
259-
* - Decides whether to show the recovery dialog
260-
*/
261-
function initializeLocalAutosave() {
262-
// Only run once per mount
263-
if (autosaveInitialized.value) {
264-
return;
265-
}
266-
267-
// Only enable for authenticated users on non-readonly, non-preview forms
268-
if (
269-
!properties.formId ||
270-
properties.readOnly ||
271-
properties.preview ||
272-
!authenticated.value
273-
) {
274-
return;
275-
}
276-
277-
// We need the form loaded and also enableAutoSave = true
278-
if (!form.value || !form.value.enableAutoSave) {
279-
return;
280-
}
281-
282-
// Mark as initialized so we don't re-run if watchers fire again
283-
autosaveInitialized.value = true;
284-
285-
// Initialize autosave storage key
286-
// Build autosave key config
287-
const keyConfig = {
288-
formId: properties.formId,
289-
userId: authStore.currentUser.idpUserId,
290-
};
291-
292-
// Only include submissionId when it's a REAL submissionId
293-
if (
294-
properties.submissionId &&
295-
properties.submissionId !== 'new' &&
296-
properties.submissionId !== 'undefined' &&
297-
properties.submissionId !== null &&
298-
properties.submissionId !== undefined
299-
) {
300-
keyConfig.submissionId = properties.submissionId;
301-
}
302-
303-
// Initialize autosave
304-
localAutosave.init(keyConfig);
305-
306-
// Decide if we should show the recovery dialog
307-
const shouldRecover = localAutosave.shouldShowRecoveryDialog(
308-
submissionRecord.value
309-
);
310-
311-
if (shouldRecover) {
312-
const localData = localAutosave.load();
313-
if (localData && localData.data) {
314-
showLocalRecoveryDialog.value = true;
315-
}
316-
}
317-
}
318-
319-
function handleLocalRestore() {
320-
const localData = localAutosave.load();
321-
322-
if (localData && localData.data) {
323-
// Restore the form data
324-
submission.value = { data: localData.data };
325-
formDataEntered.value = true;
326-
327-
// Force form re-render to display restored data
328-
reRenderFormIo.value += 1;
329-
330-
// Clear the local autosave since it's been restored
331-
localAutosave.clear();
332-
333-
notificationStore.addNotification({
334-
text: t('trans.localAutosave.restored'),
335-
type: 'success',
336-
});
337-
}
338-
}
339-
340-
function handleLocalDiscard() {
341-
// User chose to discard local autosave
342-
localAutosave.clear();
343-
}
344-
345220
async function getFormData() {
346221
function iterate(obj, stack, fields, propNeeded) {
347222
//Get property path from nested object
348223
for (let property in obj) {
349224
const innerObject = obj[property];
225+
350226
if (propNeeded === property) {
351227
fields = fields + stack + '.' + property;
352228
return fields.replace(/^\./, '');
@@ -361,10 +237,12 @@ async function getFormData() {
361237
fields,
362238
propNeeded
363239
);
240+
364241
if (next) {
365242
fieldsArray.push(next);
366243
}
367244
}
245+
368246
if (fieldsArray.length > 0) {
369247
return fieldsArray;
370248
}
@@ -425,8 +303,7 @@ async function getFormData() {
425303
response.data?.version?.schema?.components.length
426304
) {
427305
response.data.version.schema.components.map((component) => {
428-
deleteFieldData(component, submission.value);
429-
// Delete all the fields data that are not enabled for duplication
306+
deleteFieldData(component, submission.value); //Delete all the fields data that are not enabled for duplication
430307
});
431308
}
432309
}
@@ -525,6 +402,7 @@ async function getFormSchema() {
525402
version.value = response.data.versions[0].version;
526403
versionIdToSubmitTo.value = response.data.versions[0].id;
527404
formSchema.value = response.data.versions[0].schema;
405+
528406
if (response.data.schedule && response.data.schedule.expire) {
529407
let formScheduleStatus = response.data.schedule;
530408
isFormScheduleExpired.value = formScheduleStatus.expire;
@@ -554,43 +432,19 @@ function isProcessingMultiUpload(e) {
554432
block.value = e;
555433
}
556434
557-
/**
558-
* Form change handler:
559-
* - Validates drafts on change
560-
* - Ignores Form.io's internal "fromSubmission" changes
561-
* - Marks real user input
562-
* - Triggers autosave only when ready and allowed
563-
*/
564435
function formChange(e) {
565-
//If this is a draft, validate on change
436+
// if draft check validation on render
566437
if (submissionRecord.value.draft) {
567438
chefForm.value.formio.checkValidity(null, true, null, false);
568439
}
569-
570-
//Ignore internal Form.io changes (like loading submission/recall)
571-
if (!e.changed || e.changed.flags?.fromSubmission) {
572-
return;
573-
}
574-
575-
//user typing
576-
formDataEntered.value = true;
577-
578-
// AUTOSAVE – only when:
579-
// form has enableAutoSave turned on
580-
// not read-only or preview
581-
// Form.io has valid _data
582-
if (
583-
form.value?.enableAutoSave &&
584-
!properties.readOnly &&
585-
!properties.preview &&
586-
!e.changed?.flags?.fromSubmission &&
587-
chefForm.value?.formio?._data
588-
) {
589-
localAutosave.save(chefForm.value.formio._data);
440+
if (e.changed != undefined && !e.changed.flags.fromSubmission) {
441+
formDataEntered.value = true;
590442
}
591443
444+
// Seems to be the only place the form changes on load
592445
jsonManager();
593446
}
447+
594448
function jsonManager() {
595449
json_csv.value.file_name = 'template_' + form.value.name + '_' + Date.now();
596450
if (chefForm.value?.formio) {
@@ -692,6 +546,7 @@ function onSubmitButton(event) {
692546
}
693547
// this is our first event in the submission chain.
694548
// most important thing here is ensuring that the formio form does not have an action, or else it POSTs to that action.
549+
// console.info('onSubmitButton()') ; // eslint-disable-line no-console
695550
currentForm.value = event.instance.parent.root;
696551
currentForm.value.form.action = undefined;
697552
@@ -807,9 +662,6 @@ async function doSubmit(sub) {
807662
}
808663
809664
async function onSubmitDone() {
810-
// Clear local autosave on successful submission
811-
localAutosave.clear();
812-
813665
// huzzah!
814666
// really nothing to do, the formio button has consumed the event and updated its display
815667
// is there anything here for us to do?
@@ -840,6 +692,7 @@ function switchView() {
840692
}
841693
bulkFile.value = !bulkFile.value;
842694
}
695+
843696
function showdoYouWantToSaveTheDraftModalForSwitch() {
844697
saveDraftState.value = 1;
845698
if (formDataEntered.value && showModal.value) {
@@ -918,31 +771,10 @@ function closeBulkYesOrNo() {
918771
}
919772
920773
function beforeWindowUnload(e) {
921-
// Do nothing for preview or read-only views
922-
if (properties.preview || properties.readOnly) return;
923-
924-
// Default behaviour: warn the user before leaving
925-
let shouldWarn = true;
926-
927-
// If autosave is enabled on this form, it can *reduce* warnings
928-
if (form.value?.enableAutoSave) {
929-
// If a debounced save is still pending, assume it will complete soon,
930-
// or if there is an autosave snapshot available, do not warn.
931-
if (
932-
(localAutosave._isPending && localAutosave._isPending()) ||
933-
localAutosave.exists()
934-
) {
935-
shouldWarn = false;
936-
}
937-
}
938-
939-
if (!shouldWarn) {
940-
return;
774+
if (!properties.preview && !properties.readOnly) {
775+
e.preventDefault();
776+
e.returnValue = '';
941777
}
942-
943-
// Show browser "Are you sure you want to leave?" dialog
944-
e.preventDefault();
945-
e.returnValue = '';
946778
}
947779
948780
async function deleteFile(file) {
@@ -1002,6 +834,7 @@ async function uploadFile(file, config = {}) {
1002834
return fileService.uploadFile(file, uploadConfig);
1003835
}
1004836
</script>
837+
1005838
<template>
1006839
<v-skeleton-loader :loading="loadingSubmission" type="article, actions">
1007840
<v-container fluid>
@@ -1174,14 +1007,6 @@ async function uploadFile(file, config = {}) {
11741007
</p>
11751008
</div>
11761009
</div>
1177-
1178-
<LocalAutosaveRecoveryDialog
1179-
:show="showLocalRecoveryDialog"
1180-
@update:show="showLocalRecoveryDialog = $event"
1181-
@autosave:restore="handleLocalRestore"
1182-
@autosave:discard="handleLocalDiscard"
1183-
/>
1184-
11851010
<BaseDialog
11861011
v-model="doYouWantToSaveTheDraft"
11871012
:class="{ 'dir-rtl': isRTL }"

0 commit comments

Comments
 (0)