From 7a9054bb3f033c98067b3196378311dae54c5fbf Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 20 Jan 2026 01:31:53 +0000
Subject: [PATCH] =OperationQueue refactor to the JVBase/managers/queue namespace

---
 assets/js/concise/FormController.js |  183 +++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 173 insertions(+), 10 deletions(-)

diff --git a/assets/js/concise/FormController.js b/assets/js/concise/FormController.js
index 76ab6c3..894e58b 100644
--- a/assets/js/concise/FormController.js
+++ b/assets/js/concise/FormController.js
@@ -320,8 +320,6 @@
 		}
 
 		let form = this.getForm(e.target);
-		//Autosave
-		if (!form || !form.options.cache) return;
 		this.updateItem(field.dataset.field, this.getFieldValue(e.target), form);
 	}
 
@@ -335,9 +333,7 @@
 		window.debouncer.cancel(`form:${form.id}:validate:${fieldName}`);
 		this.validateField(e.target);
 
-		if (form.options.cache) {
-			this.updateItem(fieldName, this.getFieldValue(e.target), form);
-		}
+		this.updateItem(fieldName, this.getFieldValue(e.target), form);
 	}
 
 	handleInput(e){
@@ -363,10 +359,18 @@
 			e.preventDefault();
 			const storedData = await this.store.get(form.id);
 
-			this.notify('form-submit', {
-				config: form,
-				data: storedData?.changes || {}
-			});
+			if (form.options.cache) {
+				this.notify('form-submit', {
+					config: form,
+					data: storedData.changes
+				});
+			} else {
+				this.notify('form-submit', {
+					config: form,
+					data: this.changes.get(form.id)?.changes??{},
+				});
+			}
+
 		}
 
 		if (form.options.showSummary) {
@@ -396,7 +400,9 @@
 		let changes = this.changes.get(form.id);
 		changes.changes[name] = value;
 		this.changes.set(form.id, changes);
-		this.scheduleBackup();
+		if (form.options.cache) {
+			this.scheduleBackup();
+		}
 	}
 
 	scheduleBackup()  {
@@ -1194,6 +1200,146 @@
 		}
 	}
 
+	handleFormSuccess(form, data) {
+		// Clear previous errors
+		form.querySelectorAll('.error-message').forEach(el => el.remove());
+		form.querySelectorAll('.field-error').forEach(el =>
+			el.classList.remove('field-error')
+		);
+
+		// Add success class to form
+		form.classList.add('form-success');
+
+		// Show success message if provided
+		if (data.message) {
+			const success = document.createElement('div');
+			success.className = 'form-success-message success-message';
+			success.textContent = data.message;
+			form.insertBefore(success, form.firstChild);
+
+			const icon = window.getIcon?.('check-circle');
+			if (icon) {
+				icon.classList.add('success-icon');
+				success.prepend(icon);
+			}
+		}
+
+		// If there's a title/description (for registration success)
+		if (data.title || data.description) {
+			const successBox = document.createElement('div');
+			successBox.className = 'success-box';
+
+			if (data.title) {
+				const title = document.createElement('h3');
+				title.textContent = data.title;
+				successBox.appendChild(title);
+			}
+
+			if (data.description) {
+				const descriptions = Array.isArray(data.description)
+					? data.description
+					: [data.description];
+
+				descriptions.forEach(desc => {
+					const p = document.createElement('p');
+					p.textContent = desc;
+					successBox.appendChild(p);
+				});
+			}
+
+			form.insertBefore(successBox, form.firstChild);
+		}
+
+		//  DELETE CACHED FORM DATA ON SUCCESS
+		if (form.dataset.formId) {
+			this.store.delete(form.dataset.formId).catch(err => {
+				console.warn('Failed to clear form cache:', err);
+			});
+
+			// Clear form config dirty state
+			const formConfig = this.forms.get(form.dataset.formId);
+			if (formConfig) {
+				formConfig.isDirty = false;
+				formConfig.lastSaved = Date.now();
+				formConfig.data = {}; // Clear cached data
+			}
+		}
+
+		// Announce success for accessibility
+		if (window.jvbA11y) {
+			window.jvbA11y.announce(data.message || 'Form submitted successfully');
+		}
+
+		// Trigger custom event
+		form.dispatchEvent(new CustomEvent('jvb-form-success', {
+			detail: data
+		}));
+	}
+
+	handleFormError(form, data) {
+		// Clear all previous errors
+		form.querySelectorAll('.error-message').forEach(el => el.remove());
+		form.querySelectorAll('.field-error, .has-error').forEach(el => {
+			el.classList.remove('field-error', 'has-error');
+		});
+
+		// Clear validation states using existing method
+		form.querySelectorAll('.field').forEach(fieldWrapper => {
+			this.clearValidation(fieldWrapper);
+		});
+
+		// Handle field-specific errors
+		if (data.field) {
+			const fieldWrapper = form.querySelector(`[data-field="${data.field}"]`);
+			if (fieldWrapper) {
+				// Use existing showError method for consistency
+				this.showError(fieldWrapper, data.message);
+
+				// Mark as touched so validation persists
+				this.touchedFields.add(data.field);
+
+				// Scroll to error
+				fieldWrapper.scrollIntoView({ behavior: 'smooth', block: 'center' });
+
+				// Focus the input for better UX
+				const input = fieldWrapper.querySelector('input, textarea, select');
+				if (input) {
+					input.focus();
+				}
+			}
+		} else {
+			// General form error (not field-specific)
+			const error = document.createElement('div');
+			error.className = 'form-error error-message';
+			error.textContent = data.message;
+
+			// Add icon for consistency
+			const icon = window.getIcon?.('close-circle');
+			if (icon) {
+				icon.classList.add('error-icon');
+				error.prepend(icon);
+			}
+
+			form.insertBefore(error, form.firstChild);
+
+			// Scroll to top to show the error
+			form.scrollIntoView({ behavior: 'smooth', block: 'start' });
+		}
+
+		// Announce error for accessibility
+		if (window.jvbA11y) {
+			const announcement = data.field
+				? `Error in ${data.field}: ${data.message}`
+				: `Form error: ${data.message}`;
+			window.jvbA11y.announce(announcement);
+		}
+
+		// Trigger custom event
+		form.dispatchEvent(new CustomEvent('jvb-form-error', {
+			detail: data
+		}));
+	}
+
 	/**********************************************************************
 	STATUS
 	 **********************************************************************/
@@ -1375,6 +1521,23 @@
 		this.inputs.set(config.id, config);
 	}
 	/**********************************************************************
+	 Subscription
+	**********************************************************************/
+	subscribe(callback) {
+		this.subscribers.add(callback);
+		return () => this.subscribers.delete(callback);
+	}
+
+	notify(event, data) {
+		this.subscribers.forEach(cb => {
+			try {
+				cb(event, data);
+			} catch (e) {
+				console.error('HandleSelection subscriber error:', e);
+			}
+		});
+	}
+	/**********************************************************************
 	 Cleanup
 	**********************************************************************/
 	destroy() {

--
Gitblit v1.10.0