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