From 4089ba01e0881c89a72332e13bc3a80b6bddec2a Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 29 Jun 2026 22:15:55 +0000
Subject: [PATCH] =DashboardManager overhaul. A bit easier to modify the output of pages. Still have to get the account pages to work as expected, as well as verify the integrations and others are working - but registrar/content pages work as expected. Also fixed up the table generation in CRUD.js and CRUDSkeleton.php

---
 assets/js/concise/CRUD.js |  152 ++++++++++++++++++++++++++++++--------------------
 1 files changed, 92 insertions(+), 60 deletions(-)

diff --git a/assets/js/concise/CRUD.js b/assets/js/concise/CRUD.js
index 3a61fd5..03ebd98 100644
--- a/assets/js/concise/CRUD.js
+++ b/assets/js/concise/CRUD.js
@@ -131,17 +131,17 @@
 			setup({ el, refs, manyRefs, data }) {
 				baseSetup(el, refs, data);
 
-				manyRefs?.inputs?.forEach(el => {
-					let wrapper = el.closest('[data-field]');
-					window.prefixInput(el, `${data.id}-`, wrapper);
-				});
+				data.prefix = `${data.id}-`;
 
-				manyRefs?.status?.forEach(el => {
-					if (el.value === data.status) {
-						el.checked = true;
-					}
-				});
+				window.jvbPopulate.populate(el, data);
 
+				//
+				// manyRefs?.status?.forEach(el => {
+				// 	if (el.value === data.status) {
+				// 		el.checked = true;
+				// 	}
+				// });
+				//
 				if (crud.isTimeline) {
 					if (refs.sharedRow) {
 						refs.sharedRow.querySelectorAll('input,select,textarea').forEach(input => {
@@ -287,18 +287,20 @@
 					month: 'dialog.date-range .month-select',
 				}
 			},
+			wrap: '#item-wrap',
 			grid: `.${this.content}.item-grid`,
 			table: {
 				nav: '#vertical',
 				form: 'form.table',
 				table: 'form.table table',
-				body: 'form.table body',
+				body: 'form.table tbody',
 				head: 'form.table thead',
 				foot: 'form.table tfoot',
 				selectedColumns: '.all-filters .multi-select',
 				columns: 'thead th',
 			},
 			bulk: {
+				hasEdit: '.bulk-action-select [value="edit"]',
 				action: '.bulk-action-select',
 				count: '.bulk-controls .selected-count',
 				control: '.bulk-controls .bulk-actions',
@@ -324,7 +326,11 @@
 				},
 				date: '[data-filter="date"]'
 			},
-			uploader: 'details.uploader'
+			uploader: {
+				details: 'details.uploader',
+				form: 'details.uploader form',
+				uploader: 'details.uploader [data-field-type="upload"]'
+			}
 		}
 
 		this.ui = window.uiFromSelectors(this.selectors);
@@ -340,12 +346,17 @@
 		this.isTimeline = !!document.querySelector('[data-timeline]');
 	}
 		initUploader() {
-			if (!this.ui.uploader) return;
+			if (!this.ui.uploader.form) return;
+			this.uploadForm = this.forms.registerForm(this.ui.uploader.form).id??false;
 
-			window.jvbUploads.scanFields(this.ui.uploader);
+			// window.jvbUploads.scanFields(this.ui.uploader);
 			window.jvbUploads.subscribe((event, data) => {
 				if (event === 'sent-to-queue') {
-					if (data === this.ui.uploader.dataset.uploader) {
+					if (data.field.id === this.ui.uploader.uploader.dataset.uploader) {
+						if (this.uploadForm ) {
+							this.forms.store.delete(this.uploadForm);
+						}
+
 						window.debouncer.schedule('crud-complete', ()=> {
 							this.store.clearCache();
 						});
@@ -422,6 +433,7 @@
 						{ name: 'modified', keyPath: 'modified'},
 						{ name: 'title', keyPath: 'title'},
 					],
+					isAuth: true,
 					filters: filters,
 					ignore: ['content', 'user'],
 					TTL: 60 * 60 * 1000, 		//1 hour cache
@@ -469,12 +481,12 @@
 				span: '.bulk-select label span'
 			},
 			wrapper: {
-				wrapper: '.wrap'
+				wrapper: this.selectors.wrap,
 			},
 			item: {
 				idAttribute: 'itemId'
 			}
-		});
+		}, this.ui.views.table !== null);
 		this.selectionHandler.subscribe((event, data) => {
 			this.selected = new Set([...data.selectedItems].map(id => parseInt(id)));
 			this.ui.bulk.control.hidden = this.selected.size === 0;
@@ -513,8 +525,10 @@
 				&& data.status === 'completed'
 				&& data.endpoint === 'uploads/groups') {
 				if (data.result && data.result.group_mappings) {
+					console.log('Handling group mapping from queue response');
 					this.handleGroupMappings(data.result.group_mappings);
 				}
+
 				this.store.clearCache();
 			}
 
@@ -539,39 +553,55 @@
 				}
 
 				if (Object.keys(data.result.success).length === 0) {
-					this.changesStore.delete(id);
+					console.log(data.result.success);
+					data.result.success.forEach(id => this.changesStore.delete(id));
+
 					this.store.clearCache();
 				}
 			}
 
+			if (event === 'sent-to-server' && data.type === 'content_update') {
+				if (data instanceof FormData) return;
+
+				for ( let [id, changes] of Object.entries(data.posts)) {
+					this.compareStored(id, changes);
+				}
+			}
+
 		});
 	}
 	checkCompletedChanges(items) {
 		for (let [id, data] of items) {
-
+			this.compareStored(id, data);
+		}
+	}
+		compareStored(id, data) {
 			let stored = this.changesStore.get(id);
-			if (!stored) continue;
+			if (!stored) return;
 
 			for (let [field, value] of Object.entries(data)) {
 				if (Object.hasOwn(stored, field)) {
 					let changes = window.getDifferences.map(stored[field], value);
-
 					if (!changes) {
 						delete stored[field];
+					} else {
+						stored[field] = changes;
 					}
-
 				}
 			}
 
-			//It'll have the id and the content still
-			if (Object.values(stored).length === 2) {
+			let hasID = Object.hasOwn(stored, 'id');
+			let hasContent = Object.hasOwn(stored, 'content');
+			if ((hasID && hasContent && Object.keys(stored).length === 2)
+				|| ((hasID || hasContent) && Object.keys(stored).length === 1)
+				|| Object.keys(stored).length === 0
+			) {
 				this.changesStore.delete(id);
 				this.store.clearCache();
 			} else {
 				this.changesStore.save(stored);
 			}
 		}
-	}
 	checkFailedChanges(items) {
 		//TODO do something.
 	}
@@ -591,6 +621,10 @@
 		//current view (defaults to grid)
 		let defaultView = this.container.dataset.view??'grid'
 		this.view = this.cache.get('view')??defaultView;
+
+		if (!this.view) {
+			this.view = defaultView;
+		}
 		if (this.view !== defaultView) {
 			this.ui.views[this.view].checked = true;
 		}
@@ -627,6 +661,7 @@
 		if (this.ui.table.nav && tabDirection === 'vertical') {
 			this.ui.table.nav.checked = true;
 		}
+		this.tabNav = tabDirection !== 'horizontal';
 
 
 
@@ -637,7 +672,7 @@
 				default: 'closed',
 			},
 			showUploader: {
-				element: this.ui.uploader,
+				element: this.ui.uploader.details,
 				default: 'open'
 			}
 		};
@@ -689,6 +724,7 @@
 		let title = `Saving changes for multiple ${this.plural}`;
 
 		this.scheduleSave(0);
+		this.modals.edit.handleClose();
 	}
 
 	async handleCreateSubmit(modal) {
@@ -743,6 +779,9 @@
 		const isBulkAction = e.target.matches('.bulk-action-select');
 		const isView = e.target.matches('[data-view]');
 
+		if (this.view === 'table') {
+			this.handleTableChange(e);
+		}
 		if (!inItem && !isFilter && !isBulkAction && !isView) return;
 
 		if (!this.isPopulating && inItem && !e.target.closest('[data-ignore], .select-item')) {
@@ -766,19 +805,21 @@
 			return;
 		}
 
-		// Table-specific handlers
-		if (this.view === 'table') {
-			if (e.target.matches('details.multi-select')) {
-				this.toggleColumn(e.target.id, e.target.checked);
-				return;
-			}
+	}
+		handleTableChange(e) {
+			// Table-specific handlers
+			if (this.view === 'table') {
+				if (e.target.matches('details.multi-select')) {
+					this.toggleColumn(e.target.id, e.target.checked);
+					return;
+				}
 
-			if (e.target.matches(this.selectors.table.nav)) {
-				this.tabNav = e.target.checked;
-				this.cache.set('tabNav', e.target.checked ? 'vertical' : 'horizontal');
+				if (e.target.matches(this.selectors.table.nav)) {
+					this.tabNav = e.target.checked;
+					this.cache.set('tabNav', e.target.checked ? 'vertical' : 'horizontal');
+				}
 			}
 		}
-	}
 		handleBulkAction(bulkAction) {
 			if (bulkAction.value.startsWith('tax-')) {
 				const selectedOption = bulkAction.options[bulkAction.selectedIndex];
@@ -856,6 +897,7 @@
 		if (this.isPopulating) {
 			return;
 		}
+		name.replace(`[${itemId}]`, '');
 
 		const stored = this.store.get(itemId);
 		if (stored) {
@@ -1037,7 +1079,7 @@
 			return;
 		}
 
-		if (e.target.matches(this.selectors.buttons.create)) {
+		if (e.target.matches(this.selectors.buttons.create) || e.target.closest(this.selectors.buttons.create)) {
 			this.openCreateModal();
 		}
 	}
@@ -1188,7 +1230,7 @@
 			// For regular tables, use all rows in tbody
 			if (!this.ui.table.body) return null;
 
-			const rows = Array.from(this.ui.table.body.querySelectorAll('tr'))
+			const rows = Array.from(this.ui.table.body.children)
 				.filter(row => this.isEditableRow(row));
 
 			return goBackward ? rows[rows.length - 1] : rows[0];
@@ -1264,18 +1306,23 @@
 		this.ui.modals.edit.form.dataset.formId = `edit-${itemID}`;
 
 
+		this.isPopulating = true;
+
+		this.modals.edit.handleOpen();
 		this.forms.registerForm(this.ui.modals.edit.form, {cache: false,
 			autoUpload: true,});
-		this.isPopulating = true;
+
+
 		this.populate.populate(this.ui.modals.edit.form, item);
 		//For quill/taxonomy selector's async setups
 		requestAnimationFrame(() => {
 			requestAnimationFrame(() => {
-				this.isPopulating = false;
+				setTimeout(() => {
+					this.isPopulating = false;
+				}, 50);
 			});
 		});
 
-		this.modals.edit.handleOpen();
 	}
 	openBulkEditModal() {
 		window.removeChildren(this.ui.modals.bulkEdit.selected);
@@ -1352,6 +1399,7 @@
 		let operation = {
 			endpoint: this.endpoint,
 			headers: {
+				'X-WP-Nonce': window.auth.getNonce(),
 				'X-Action-Nonce': window.auth.getNonce('dash'),
 			},
 			data: {
@@ -1490,7 +1538,7 @@
 
 		if (on && !this.ui.table.form) {
 			let table = window.jvbTemplates.create('contentTable');
-			this.container.append(table);
+			this.ui.wrap.append(table);
 			this.ui.table = window.uiFromSelectors(this.selectors.table);
 			this.ui.table.columns = this.container.querySelectorAll(this.selectors.table.columns);
 		}
@@ -1544,15 +1592,12 @@
 		this.toggleTable();
 		window.removeChildren(this.ui.grid);
 
+
 		await window.chunkIt(
 			items,
 			(item) => this.renderTableItem(item),
 			(fragment) => {
-				if (this.ui.table.body) {
-					this.ui.table.body.append(fragment);
-				} else {
-					this.ui.table.table.insertBefore(fragment, this.ui.table.foot);
-				}
+				this.ui.table.body.append(fragment);
 			},
 			5
 		);
@@ -1790,20 +1835,7 @@
 	 CLEANUP
 	***************************************************************/
 	resetForm(form) {
-		// Clear text inputs, textareas
-		form.querySelectorAll('input[type="hidden"], input[type="text"], input[type="number"], input[type="email"], input[type="url"], textarea').forEach(input => {
-			input.value = '';
-		});
-
-		// Uncheck checkboxes and radios
-		form.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(input => {
-			input.checked = false;
-		});
-
-		// Reset selects to first option
-		form.querySelectorAll('select').forEach(select => {
-			select.selectedIndex = 0;
-		});
+		form.reset();
 
 		// Clear any selected items displays
 		form.querySelectorAll('.selected-items').forEach(container => {

--
Gitblit v1.10.0