/** * view.js * Frontend JavaScript for the Form Block * Handles form validation and submission */ class FormBlock { constructor() { this.controller = new window.jvbForm(); document.querySelectorAll('.jvb-form-block form').forEach(form => { this.controller.registerForm(form, {autosave: true,autoUpload: false}); }); this.controller.subscribe((event, data) => { if (event === 'form-submit') { this.handleFormSubmission(data); } }); } async handleFormSubmission(data) { let formId = data.formId; let formConfig = data.config; let formData = data.fullData; let form = formConfig.element; // Create FormData instead of JSON const submitData = new FormData(); // Add all regular form fields for (const [key, value] of Object.entries(formData)) { // Skip the nonce field - we don't need it for REST API with __return_true if (key === '_wpnonce' || key === '_wp_http_referer') { continue; } if (Array.isArray(value)) { value.forEach(v => submitData.append(`${key}[]`, v)); } else if (typeof value === 'object' && value !== null) { // For nested objects, you might want to JSON.stringify them submitData.append(key, JSON.stringify(value)); } else { submitData.append(key, value); } } // Get uploaded files from UploadManager if (window.jvbUploads) { const files = await window.jvbUploads.getFilesForForm(form); // Append files with their field names files.forEach(({file, fieldName, uploadId, meta}) => { // Use fieldName[] for multiple files per field submitData.append(`${fieldName}[]`, file); }); } let headers = { // 'X-WP-Nonce': window.auth.getNonce() }; let block = form.closest('.jvb-form-block'); this.controller.showFormStatus(formId, 'uploading'); try { const response = await fetch(`${jvbSettings.api}forms`, { method: 'POST', credentials: 'same-origin', headers, body: submitData // Send FormData instead of JSON }); const result = await response.json(); if (!response.ok) { this.controller.showFormStatus(formId, 'error'); this.controller.handleFormError(form, result); // ✓ Show error to user return; // Stop processing } this.controller.showFormStatus(formId, 'submitted'); this.controller.showSummary(formId, '.jvb-form-block'); // Clean up uploaded files from IndexedDB after successful submission 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(formId, 'error'); // Show user-friendly network error - IMPROVED this.controller.handleFormError(form, { message: 'Network error. Please check your connection and try again.', code: 'network_error' }); } finally { this.controller.store.delete(formId); } } } document.addEventListener('DOMContentLoaded', async function() { window.auth.subscribe(event => { if (event === 'auth-loaded') { new FormBlock(); } }); });