@@ -4,7 +4,6 @@ import _ from 'lodash';
44import { storeToRefs } from ' pinia' ;
55import {
66 computed ,
7- nextTick ,
87 onBeforeUpdate ,
98 onBeforeUnmount ,
109 onMounted ,
@@ -15,7 +14,6 @@ import { useI18n } from 'vue-i18n';
1514import { useRouter } from ' vue-router' ;
1615
1716import BaseDialog from ' ~/components/base/BaseDialog.vue' ;
18- import LocalAutosaveRecoveryDialog from ' ~/components/designer/LocalAutosaveRecoveryDialog.vue' ;
1917import FormViewerActions from ' ~/components/designer/FormViewerActions.vue' ;
2018import FormViewerMultiUpload from ' ~/components/designer/FormViewerMultiUpload.vue' ;
2119import templateExtensions from ' ~/plugins/templateExtensions' ;
@@ -24,7 +22,6 @@ import { useAppStore } from '~/store/app';
2422import { useAuthStore } from ' ~/store/auth' ;
2523import { useFormStore } from ' ~/store/form' ;
2624import { useNotificationStore } from ' ~/store/notification' ;
27- import { useLocalAutosave } from ' ~/composables/useLocalAutosave' ;
2825
2926import { isFormPublic } from ' ~/utils/permissionUtils' ;
3027import {
@@ -117,11 +114,6 @@ const authStore = useAuthStore();
117114const formStore = useFormStore ();
118115const 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-
125117const { config } = storeToRefs (appStore);
126118const { authenticated , keycloak , tokenParsed , user } = storeToRefs (authStore);
127119const { downloadedFile , isRTL } = storeToRefs (formStore);
@@ -151,6 +143,7 @@ const shouldDisableFileDownloads = computed(() => {
151143
152144const 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(
188181watch (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
212185onMounted (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 () => {
236205onBeforeUnmount (() => {
237206 window .removeEventListener (' beforeunload' , beforeWindowUnload);
238207 clearTimeout (downloadTimeout .value );
239-
240- // Cleanup local autosave debounce
241- localAutosave .cleanup ();
242208});
243209
244210onBeforeUpdate (() => {
@@ -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-
345220async 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- */
564435function 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+
594448function 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
809664async 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+
843696function showdoYouWantToSaveTheDraftModalForSwitch () {
844697 saveDraftState .value = 1 ;
845698 if (formDataEntered .value && showModal .value ) {
@@ -918,31 +771,10 @@ function closeBulkYesOrNo() {
918771}
919772
920773function 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
948780async 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