Jake Vanderwerf
8 days ago 3b83905603d44b1a08f8b2b36a605808ce686ad6
src/forms/view.js
@@ -1,81 +1,112 @@
/**
 * view.js
 * Frontend JavaScript for the Form Block
 * Handles form validation and submission
 */
/**
 * view.js
 * Frontend JavaScript for the Form Block
 */
class FormBlock {
   constructor() {
      this.controller = window.jvbForm;
// Wait for DOM to be ready
document.addEventListener('DOMContentLoaded', function() {
   let formController = new window.jvbForm();
      document.querySelectorAll('.jvb-form-block form').forEach(form => {
         this.controller.registerForm(form, {
            cache: true,
            autoUpload: false,
            imageMeta: false,
         });
      });
   document.querySelectorAll('.jvb-form-block').forEach(block => {
      const formHandler = formController.registerForm(block.querySelector('form'));
   });
   formController.subscribe((event, data) => {
      if (event === 'form-submit') {
         handleFormSubmission(null, data.data, data.config.element);
      this.controller.subscribe((event, data) => {
         if (event === 'form-submit') {
            this.handleFormSubmission(data).then(()=>{});
         }
      });
   }
   async handleFormSubmission(eventData) {
      const { config, data } = eventData;
      const form = config.element;
      const submitData = new FormData();
      // Add regular form fields
      for (const [key, value] of Object.entries(data)) {
         if (Array.isArray(value)) {
            value.forEach(v => submitData.append(`${key}[]`, v));
         } else if (typeof value === 'object' && value !== null) {
            submitData.append(key, JSON.stringify(value));
         } else {
            submitData.append(key, value);
         }
      }
      config.element.querySelectorAll('[name="form_id"],[name="form_type"],[name="timestamp"],[name="cf-turnstile-response"]').forEach(input => {
         submitData.append(input.name, input.value);
      });
      // Add uploaded files
      if (window.jvbUploads) {
         try {
            const files = await window.jvbUploads.getFilesForForm(form);
            files.forEach(({ file, fieldName }) => {
               submitData.append(`${fieldName}[]`, file);
            });
         } catch (error) {
            console.error('Error getting files:', error);
         }
      }
      this.controller.showFormStatus(config.id, 'uploading');
      try {
         const response = await fetch(`${jvbSettings.api}forms`, {
            method: 'POST',
            credentials: 'same-origin',
            body: submitData
         });
         const result = await response.json();
         if (!response.ok) {
            this.controller.showFormStatus(config.id, 'error');
            this.controller.handleFormError(form, result);
            return;
         }
         this.controller.showFormStatus(config.id, 'submitted');
         // this.controller.handleFormSuccess(form, result);
         this.controller.showSummary({ changes: data, config: config });
         window.jvbA11y.announce('Form successfully submitted!');
         // Clean up uploaded files
         if (window.jvbUploads) {
            const uploadFields = form.querySelectorAll('[data-upload-field]');
            for (const field of uploadFields) {
               const fieldId = window.jvbUploads.determineFieldId(field);
               await window.jvbUploads.clearFieldFromStores(fieldId);
            }
         }
      } catch (error) {
         console.error('Form submission error:', error);
         this.controller.showFormStatus(config.id, 'error');
         this.controller.handleFormError(form, {
            message: 'Network error. Please check your connection and try again.',
            code: 'network_error'
         });
      } finally {
         await this.controller.store.delete(config.id);
      }
   }
}
document.addEventListener('DOMContentLoaded', async function() {
   window.auth.subscribe(event => {
      if (event === 'auth-loaded') {
         new FormBlock();
      }
   });
});
async function handleFormSubmission(event, data, form) {
   let headers = {
      'X-WP-Nonce': jvbSettings.nonce,
      'Content-Type': 'application/json'
   };
   data['form_type'] = form.dataset.formId;
   data['form_id'] = form.id;
   let block = form.closest('.jvb-form-block');
   let loading = window.getTemplate('spinner');
   form.hidden = true;
   block.classList.add('loading');
   block.prepend(loading);
   try {
      const response = await fetch (`${jvbSettings.api}forms`,
         {
            method: 'POST',
            headers,
            body: JSON.stringify(data)
         });
      if (!response.ok) {
         const errorData = await response.json().catch(() => ({}));
         throw new Error(errorData.message|| `Request failed with status ${response.status}`);
      }
      updateUI(await response.json(), block);
   } catch (error) {
      throw error;
   } finally {
      block.classList.remove('loading');
   }
}
function updateUI(response, block) {
   if (response.success === false) {
      let form = block.querySelector('form');
      form.removeAttribute('hidden');
      if ('required' in response) {
         response.required.forEach(required => {
            let item = form.querySelector(`[name=${required}]`);
            item.classList.add('required', 'error');
            item.scrollIntoView();
         });
      }
      return;
   }
   let summary = window.getTemplate('formSummary');
   summary.querySelector('h2').textContent = 'Success!';
   console.log('Form Response: ', response);
   for (let [key, value] of Object.entries(response)) {
      let item = summary.querySelector(`#${key}`);
      let title = item.querySelector('h4');
      if (title.innerText.includes('%s')) {
         title.innerHTML = title.replace('%s', '<b>'+value+'</b>');
      } else {
         item.querySelector('div').innerHTML = value;
      }
   }
   block.append(summary);
}