| | |
| | | * Works with DataStore for CRUD operations and standalone for front-end forms |
| | | */ |
| | | class FormController { |
| | | constructor(store = null) { |
| | | this.store = store; // Optional - for CRUD operations |
| | | if (!store) { |
| | | this.store = new window.jvbStore({name:'forms', TTL: 604800}); |
| | | } |
| | | constructor() { |
| | | this.store = new window.jvbStore({ |
| | | name:'forms', |
| | | storeName: 'forms', |
| | | keyPath: 'formId', |
| | | indexes: [ |
| | | { name: 'status', keyPath: 'status' }, |
| | | { name: 'operationId', keyPath: 'operationId' }, |
| | | { name: 'timestamp', keyPath: 'timestamp' }, |
| | | { name: 'formType', keyPath: 'type' } |
| | | ], |
| | | TTL: 604800000, //7 days |
| | | }); |
| | | |
| | | this.debouncer = window.debouncer; |
| | | |
| | | this.ignore = []; |
| | |
| | | // Check for pending operations on page load |
| | | await this.checkPendingOperations(); |
| | | |
| | | this.store.subscribe(this.handleStoreEvent.bind(this)); |
| | | |
| | | // Set up global form handlers for standalone forms |
| | | this.initListeners(); |
| | | } |
| | | |
| | | handleStoreEvent(event, data) { |
| | | switch(event) { |
| | | case 'item-saved': |
| | | if (data.item.status === 'autosave') { |
| | | this.showFormStatus(data.item.formId, 'autosave'); |
| | | } |
| | | break; |
| | | case 'data-loaded': |
| | | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Check for pending operations from previous session |
| | | */ |
| | | async checkPendingOperations() { |
| | | if (!this.store) return; |
| | | try { |
| | | let pending = this.store.getAllForms(); |
| | | const pendingForms = await this.store.query('status', 'pending'); |
| | | |
| | | } catch (error) { |
| | | console.error('Failed to load pending forms:', error); |
| | | } |
| | | if (pendingForms.length === 0) return; |
| | | |
| | | // Group by form type or page |
| | | const grouped = this.groupPendingForms(pendingForms); |
| | | |
| | | // Show consolidated notification |
| | | this.showPendingNotification(grouped); |
| | | } |
| | | |
| | | /** |
| | |
| | | * Discard pending form data |
| | | */ |
| | | async discardPendingForm(formId) { |
| | | this.store.clearForm(formId); |
| | | this.store.delete(formId); |
| | | |
| | | if (window.jvbA11y) { |
| | | window.jvbA11y.announce('Previous changes discarded'); |
| | |
| | | /** |
| | | * Initialize image upload fields |
| | | */ |
| | | initImageUploadFields() { |
| | | window.jvbUploads.scanFields(); |
| | | initImageUploadFields(form) { |
| | | window.jvbUploads.scanFields(form); |
| | | } |
| | | |
| | | /* ========== Event Handlers ========== */ |
| | | |
| | | handleSubmit(event) { |
| | | //TODO: submit data, if successful, delete from store |
| | | if (this.subscribers.size > 0 ){ |
| | | const form = event.target; |
| | | if (!form.dataset.formId) return; |
| | | this.store.delete(form.dataset.formId); |
| | | |
| | | event.preventDefault(); |
| | | |
| | |
| | | * Get appropriate delay based on field type and context |
| | | */ |
| | | getDelayForField(field) { |
| | | console.log('Get Delay for Field', field); |
| | | // Text fields get longer delay for typing |
| | | if (field.type === 'text' || field.type === 'textarea') { |
| | | return this.autoSaveDefaults.typingDelay; |
| | |
| | | |
| | | async autosave(formConfig) { |
| | | const formData = this.collectFormData(formConfig.element); |
| | | this.cacheFormData(formConfig, formData); |
| | | |
| | | await this.store.save({ |
| | | formId: formConfig.id, |
| | | data: formData, |
| | | status: 'draft', |
| | | timestamp: Date.now() |
| | | }); |
| | | this.showFormStatus(formConfig.id, 'saved'); |
| | | |
| | | // Get only changed fields |
| | | const changes = this.getChangedFields(formConfig.data, formData); |
| | |
| | | }); |
| | | } |
| | | |
| | | cacheFormData(formConfig, formData) { |
| | | try { |
| | | this.store.storeForm(formConfig.id, { |
| | | formId: formConfig.id, |
| | | formData: formData, |
| | | timestamp: Date.now(), |
| | | status: 'pending', |
| | | operationId: null |
| | | }); |
| | | } catch (error) { |
| | | console.error('Failed to cache form data:', error); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Check if form has unsaved changes |
| | | */ |
| | |
| | | cleanupForm(formId) { |
| | | const formConfig = this.forms.get(formId); |
| | | if (!formConfig) return; |
| | | console.log('Cleaning up form', formConfig); |
| | | |
| | | // Check for unsaved changes |
| | | if (this.hasUnsavedChanges(formId)) { |