From 715e26a9eb219808d5c899d418e1d596f9318f61 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 22 Jun 2026 16:59:19 +0000
Subject: [PATCH] =Debugging and implementing the Checkout.js logic. Will be uploading to test integration with square next.
---
assets/js/concise/FormController.js | 197 +++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 175 insertions(+), 22 deletions(-)
diff --git a/assets/js/concise/FormController.js b/assets/js/concise/FormController.js
index 617c3a6..d5e5835 100644
--- a/assets/js/concise/FormController.js
+++ b/assets/js/concise/FormController.js
@@ -51,7 +51,7 @@
this.selectors = {
tabs: {
nav: 'nav.tabs',
- sections: '.tab.content', //querySelectorAll
+ sections: '.tab-content', //querySelectorAll
progress: {
progress: '.progress',
fill: '.progress .fill',
@@ -84,6 +84,8 @@
},
repeater: {
repeater: '.repeater', //querySelectorAll
+ row: '.repeater-row',
+ handle: '.drag-handle',
header: '.repeater-row-header',
remove: '.remove-row',
add: '.add-repeater-row',
@@ -356,6 +358,28 @@
case 'dismiss-restore':
form.ui.status.status.hidden = true;
break;
+ case 'next-step':
+ e.preventDefault();
+ form.tabs.ui.buttons.forEach((btn, index) => {
+ if (btn.dataset.tab === form.tabs.activeTab) {
+ let next = form.tabs.ui.buttons[index + 1]??false;
+ if (next) {
+ window.jvbTabs.switchTab(next.dataset.tab, window.jvbTabs.getConfig(next));
+ }
+ }
+ });
+ break;
+ case 'prev-step':
+ e.preventDefault();
+ form.tabs.ui.buttons.forEach((btn, index) => {
+ if (btn.dataset.tab === form.tabs.activeTab) {
+ let prev = form.tabs.ui.buttons[index - 1]??false;
+ if (prev) {
+ window.jvbTabs.switchTab(prev.dataset.tab, window.jvbTabs.getConfig(prev));
+ }
+ }
+ });
+ break;
}
}
}
@@ -376,6 +400,7 @@
});
}
const collectionName = collectionField.dataset.field;
+ this.maybeUpdateCollectionDisplay(field);
window.debouncer.schedule(
`collection:${collectionName}`,
() => this.updateCollectionField(collectionField),
@@ -460,7 +485,7 @@
data: this.changes.get(form.id)?.changes??{},
});
}
- if (form.options.endpoint) {
+ if (form.options.endpoint && !form.options.handled) {
console.log('Submitting form...');
await this.handleServerSave(form, this.collectFormData(form.form));
}
@@ -500,15 +525,79 @@
changes.changes[name] = value;
}
+ if (form.tabs) {
+ console.log('Updating step progress on change');
+ this.updateStepProgress(form);
+ }
+
this.changes.set(form.id, changes);
if (form.options.cache) {
this.scheduleBackup();
}
- if (form.options.endpoint) {
+ if (form.options.endpoint && !form.options.handled) {
this.scheduleServerSave(form, changes);
}
}
+ updateStepProgress(form) {
+ console.log('First check in updateStepProgress');
+ if (!form.tabs) return;
+ let currentStep = this.getCurrentStep(form);
+ console.log('Current step: ', currentStep);
+ if (!currentStep) return;
+
+ //The progress root will be 1 less than whatever the current step is
+ currentStep = currentStep - 1;
+
+ let section = this.getCurrentSection(form);
+ console.log('Current section: ', section);
+ if (!section) return;
+
+ let total = 0;
+ let filled = 0;
+ section.childNodes.forEach(f => {
+ console.log('Child node: ', f);
+ if (f.classList.contains('field')) {
+ total++;
+ let value = this.getFieldValue(f.querySelector(this.inputSelectors));
+ console.log('Value', value);
+ if (value.length > 0) {
+ filled++;
+ }
+ }
+ });
+ //Shouldn't happen, but simplifies things if it does
+ if (total === 0) return;
+ let current = currentStep + (filled / total);
+
+ let totalSteps = form.ui.tabs.sections.length;
+ window.showProgress(
+ form.ui.tabs.progress,
+ current,
+ totalSteps,
+ `Step ${currentStep +1} of ${totalSteps}`
+ );
+ }
+ getCurrentStep(form) {
+ let step = null;
+ form.tabs.ui.buttons.forEach(btn => {
+ if (btn.dataset.tab === form.tabs.activeTab) {
+ step = btn.dataset.step;
+ }
+ });
+ return step;
+ }
+
+ getCurrentSection(form) {
+ console.log('Getting current section: ', form.tabs.ui.sections);
+ let section = null;
+ form.tabs.ui.sections.forEach(s => {
+ if (s.dataset.tab === form.tabs.activeTab) {
+ section = s;
+ }
+ });
+ return section;
+ }
scheduleBackup() {
window.debouncer.schedule(
@@ -608,6 +697,7 @@
endpoint: Object.hasOwn(form.dataset, 'save') ? form.dataset.save: '',
showStatus: true,
showSummary: false,
+ handled: Object.hasOwn(form.dataset, 'handled'),
cache: true,
ignore: [],
... options
@@ -914,9 +1004,7 @@
}
}
checkForRepeaters(form) {
-
if (!form.querySelector(this.selectors.repeater.repeater)) return;
-
form.querySelectorAll(this.selectors.repeater.repeater).forEach(repeater => {
let config = {
id: repeater.querySelector('template').className??window.generateID('repeater'),
@@ -949,11 +1037,15 @@
);
if (window.Sortable) {
- config.sortable = new Sortable(repeater, {
- handle: this.selectors.repeater.header,
+ config.sortable = new Sortable(config.ui.items, {
animation: 150,
+ draggable: this.selectors.repeater.row,
+ handle: this.selectors.repeater.handle,
+ ghostClass: 'repeater-row-ghost',
+ chosenClass: 'repeater-row-chosen',
+ dragClass: 'dragging',
onEnd: () => {
- this.reindexList(repeater);
+ this.reindexList(config.ui.items);
}
});
}
@@ -992,12 +1084,17 @@
let form = this.getForm(repeater);
this.initializeFields(repeater, form);
+ this.reindexList(config.ui.items);
this.a11y.announce('Row added');
}
removeRepeaterRow(row) {
let repeater = row.closest('[data-repeater-id]');
row.remove();
- this.reindexList(repeater);
+ let items = this.repeaters.get(repeater);
+ if (items) {
+ this.reindexList(items.ui.items);
+ }
+
this.a11y.announce('Row removed');
}
checkForTagLists(form) {
@@ -1320,16 +1417,17 @@
checkForTabs(form, config) {
if (window.jvbTabs && form.querySelector('nav.tabs')) {
+
config.tabs = window.jvbTabs.registerTab(form, {
- preCheck: (section, tabConfig) => {
- return this.validateStep(section, config);
+ preCheck: (activeSection, switchTo, tabConfig) => {
+ return this.validateStep(activeSection, switchTo, tabConfig);
}
});
config.ui.tabs = window.uiFromSelectors(this.selectors.tabs, form);
config.ui.tabs.sections = Array.from(form.querySelectorAll(this.selectors.tabs.sections));
config.ui.tabs.inputs = {};
config.ui.tabs.sections.forEach(section => {
- config.ui.tabs.inputs[section.dataset.tab] = Array.from(section.querySelectorAll(this.inputs));
+ config.ui.tabs.inputs[section.dataset.tab] = Array.from(section.querySelectorAll(this.inputSelectors));
});
config.ui.tabs.buttons = Array.from(form.querySelectorAll(this.selectors.tabs.buttons));
@@ -1338,13 +1436,14 @@
if (config.ui.tabs.progress) {
const section = config.ui.tabs.sections.filter(section => section.dataset.tab === data.current)[0]??false;
if (!section) return;
- const step = section.dataset.step;
- const total = config.ui.sections.length;
+ const step = section.dataset.step - 1;
+ const total = config.ui.tabs.sections.length;
window.showProgress(
config.ui.tabs.progress,
step,
- total
+ total,
+ `Step ${step + 1} of ${total}`
);
}
}
@@ -1352,8 +1451,24 @@
this.forms.set(config.id, config);
}
}
- validateStep(section, config) {
- const formId = section.closest('[data-form-id]')?.dataset.formId;
+ validateStep(currentSection, switchTo, config) {
+ let doit = false;
+ if (config.ui.sections) {
+ config.ui.sections.forEach((section, index) => {
+ if (section === currentSection) {
+ let prev = index -1;
+ if (prev >= 0) {
+ let prevSection = config.ui.sections[prev];
+ if (prevSection && prevSection.dataset.tab === switchTo) {
+ doit = true;
+ }
+ }
+ }
+ });
+ }
+ if (doit) return true;
+
+ const formId = currentSection.closest('[data-form-id]')?.dataset.formId;
if (!formId) return true;
const form = this.forms.get(formId);
@@ -1363,7 +1478,7 @@
.filter(item =>
item &&
item.form === formId &&
- item.section === section.dataset.tab &&
+ item.section === currentSection.dataset.tab &&
!item.element.closest('[hidden]')
);
@@ -1377,11 +1492,21 @@
* @param {HTMLElement} container
*/
reindexList(container) {
- const fieldName = container.dataset.field || container.dataset.repeaterId || container.dataset.tagListId;
+ let fieldName = container.dataset.field || container.dataset.repeaterId || container.dataset.tagListId;
+ if (!fieldName) {
+ let parent = container.closest('[data-field]');
+ fieldName = parent.dataset.field || parent.dataset.repeaterId || parent.dataset.tagListId;
+ }
Array.from(container.children).forEach((item, index) => {
item.dataset.index = `${index}`;
+ let rowNum = item.querySelector('.row-number');
+ if (rowNum) {
+ rowNum.textContent = `#${index + 1}`;
+ }
+ let rowTitle = item.querySelector('.row-title');
+ let rowLabel = rowTitle ? rowTitle.dataset.label : null;
// Find ALL inputs within this item, not just direct children
const inputs = item.querySelectorAll('input, select, textarea');
@@ -1390,14 +1515,17 @@
if (input.type === 'file') return;
// Get the field name from the input's data-field or name
- const inputField = input.dataset.field || input.name.split(':').pop();
+ const inputField = Object.hasOwn(input.dataset, 'field') ? input.dataset.field : input.name.split(':').pop();
+ if (inputField === rowLabel) {
+ rowTitle.textContent = window.escapeHtml(input.value);
+ }
// Re-prefix with the new index, passing item as wrapper
window.prefixInput(
input,
- `${fieldName}:${index}:`,
+ `${fieldName}:${index}:${inputField}`,
item,
- false,
+ true,
true
);
});
@@ -1425,6 +1553,27 @@
const value = this.getFieldValue(field);
this.updateItem(field.dataset.field, value, form);
}
+
+ //TODO: Store these selectors in the repeater object maybe?
+ maybeUpdateCollectionDisplay(field) {
+ let parent = field.closest('[data-repeater-id],[data-tag-list-id]');
+ if (!parent) {
+ return;
+ }
+ if (parent.dataset.fieldType === 'repeater') {
+ let row = field.closest('.repeater-row');
+ if (!row) return;
+
+ let title = row.querySelector('.row-title');
+ if (!title || !title.dataset.label) return;
+ console.log(field.dataset.field.split(':').pop());
+ console.log(title.dataset.label);
+ if (field.dataset.field.split(':').pop() === title.dataset.label) {
+ title.textContent = this.getFieldValue(field.querySelector(this.inputSelectors));
+ }
+ }
+
+ }
/**********************************************************************
VALIDATION
**********************************************************************/
@@ -1767,6 +1916,10 @@
let value = [];
Array.from(items.children).forEach(row => {
let rowData = {};
+ row.querySelectorAll('input[type="hidden"]').forEach(hidden => {
+ rowData[hidden.name] = hidden.value;
+ });
+
row.querySelectorAll('[data-field]').forEach(field => {
if (!ignore.includes(field.dataset.field)) {
const input = this.getFieldInput(field);
--
Gitblit v1.10.0