Jake Vanderwerf
2026-05-12 c32ed859f4abd1591c882f4f2a6ee16b1ec275e2
assets/js/concise/FormController.js
@@ -19,6 +19,7 @@
      this.isRestoring = false;
      this.hasListeners = false;
      this.hasUploads = false;
      this.summaryTemplate = false;
      this.init();
@@ -30,6 +31,20 @@
      this.initListeners();
      this.initStore();
      this.initValidators();
      this.initUploadSubscription();
   }
   initUploadSubscription() {
      window.jvbUploads.subscribe((event, data) => {
         if (!this.hasUploads) return;
         if (event === 'upload-received') {
            let form = this.getForm(data.field);
            if (form) {
               this.updateItem(`${data.field.dataset.field}_tempUpload`, data.id, form);
            }
         }
      });
   }
   initElements() {
      this.inputSelectors = 'input, textarea, select';
@@ -51,7 +66,12 @@
               status: '.fstatus',
               message: '.fstatus .message',
               icon: '.fstatus .icon',
               actions: '.fstatus .actions'
               actions: '.fstatus .actions',
            },
            restore: {
               container: '.restore-form',
               restore: '[data-action="restore"]',
               clear: '[data-action="clear"]',
            }
         },
         inputs: this.inputSelectors,              //querySelectorAll
@@ -162,32 +182,47 @@
            return;
         }
         const notification = document.createElement('div');
         notification.className = 'pendingChanges';
         notification.innerHTML = `
         <p>We noticed unsaved changes from last time. Would you like to restore them?</p>
        <button class="restore" type="button" data-form-id="${formId}">Restore</button>
        <button class="discard" type="button" data-form-id="${formId}">Discard</button>`;
         element.insertBefore(notification, form.ui.status.status);
         notification.querySelector('.restore').addEventListener('click', async () => {
      form.ui.restore.container.hidden = false;
      const handleRestore = async (changes, element) => {
            this.isRestoring = true;
            let theChanges = {['fields']: changes};
         await this.checkStoredUploads(changes, element);
            this.populate.populate(element, theChanges);
            this.a11y.announce('Previous changes restored');
            this.isRestoring = false;
            notification.remove();
         });
         notification.querySelector('.discard').addEventListener('click', async () => {
         form.ui.restore.container.remove();
      };
      const clearRestore = async (formId) => {
         await this.checkStoredUploads(changes, element, false);
            await this.store.delete(formId);
            this.a11y.announce('Previous changes discarded');
            notification.remove();
         });
         form.ui.restore.container.remove();
      };
      form.ui.restore.restore.addEventListener('click', () => handleRestore(changes, element));
      form.ui.restore.clear.addEventListener('click', async ()  => clearRestore(formId));
   }
      async checkStoredUploads(changes, element, restore = true) {
         let form = this.forms.get(element.dataset.formId);
         if (!form) return;
         let uploads = [];
         for (let [key, value] of Object.entries(changes)) {
            if (key.includes('_tempUpload')) {
               let field = key.replace('_tempUpload', '');
               if (Object.hasOwn(form.ui.uploads, field)) {
                  uploads.push(value);
               }
            }
         }
         if (uploads.length > 0) {
            if (restore) {
               await window.jvbUploads.restoreUploads(uploads);
            } else {
               await window.jvbUploads.clearUploads(uploads);
            }
         }
      }
   initValidators() {
      this.validators = {
@@ -438,6 +473,7 @@
    * @param form
    */
   updateItem(name, value, form) {
      if (value === undefined) return;
      if (!this.changes.has(form.id)) {
         this.changes.set(form.id, {
            id: form.id,
@@ -1210,7 +1246,15 @@
               }
            }
         checkForImageUploads(form, config) {
      this.hasUploads = true;
            window.jvbUploads.scanFields(form, config.options.autoUpload, config.options.imageMeta);
      let uploads = form.querySelectorAll('[data-field-type="upload"]');
      if (uploads) {
         config.ui.uploads = {};
         uploads.forEach(upload => {
            config.ui.uploads[upload.dataset.field] = upload;
         });
      }
         }
         checkForTabs(form, config) {
@@ -1588,8 +1632,8 @@
            return this.getTagListValue(element, conf);
         case 'group':
            return null;
            //Do we actually need anything here? I think each subfield just
            break;
         case 'location':
            return this.getLocationValue(element, conf);
@@ -1712,7 +1756,7 @@
      if (element.tagName !== 'INPUT' || element.type !== 'hidden'){
         element = element.querySelector('input[type="hidden"][name="'+fieldName+'"]');
         if (!element) {
            return;
            return null;
         }
      }