From 27fb820ae9081fb56957cf75e79eccd8a99edd52 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sat, 14 Feb 2026 19:14:48 +0000
Subject: [PATCH] =minor css changes, queue merge logic restructured, and double checking changes actually changed in cRUD.js before sending to server

---
 assets/js/concise/CRUD.js |  128 +++++++++++++++++++++++++++++++++---------
 1 files changed, 100 insertions(+), 28 deletions(-)

diff --git a/assets/js/concise/CRUD.js b/assets/js/concise/CRUD.js
index 74d6cdb..a189ac9 100644
--- a/assets/js/concise/CRUD.js
+++ b/assets/js/concise/CRUD.js
@@ -350,6 +350,16 @@
 						});
 					}
 				}
+
+				if (event === 'sent-to-queue' && data.field) {
+					const fieldName = data.field.config.name;
+					const itemId = data.field.config.itemID;
+					if (itemId && fieldName) {
+						if (this.changes.has(itemId)) {
+							delete this.changes.get(itemId)[fieldName];
+						}
+					}
+				}
 			});
 		}
 		initModals() {
@@ -496,22 +506,19 @@
 				&& data.status === 'completed') {
 				this.store.clearCache();
 			}
-			console.log('CRUD.js queue subscription');
-			console.log(event, data);
+
+
 			if (event === 'operation-status'
 				&& data.status === 'completed'
 				&& data.endpoint === 'uploads/groups') {
-				console.log('Grouped Uploads completed');
 				if (data.result && data.result.group_mappings) {
 					this.handleGroupMappings(data.result.group_mappings);
 				}
-				console.log('Cleared local cache. Refresh to see changes');
 				this.store.clearCache();
 			}
 			if (event === 'operation-status'
 				&& data.status === 'completed'
 				&& data.type === 'content_update') {
-				console.log('Cleared local cache. Refresh to see changes');
 				this.store.clearCache();
 
 				// Check for result data (from ContentExecutor)
@@ -791,28 +798,50 @@
 	}
 
 	handleItemUpdate(e) {
-		let item = window.targetCheck(e, '[data-item-id]');
 
+		let item = window.targetCheck(e, '[data-item-id]');
 		if (!item) return;
+
+		let field = e.target.closest('[data-field-type="repeater"],[data-field-type="tag-list"],[data-field]');
+		let name = field.dataset.field;
+		let value = this.forms.getFieldValue(e.target);
 		item.dataset.itemId.split(',').forEach(itemId => {
-			let field = this.forms.getField(e.target);
-			if (['repeater', 'tag-list'].includes(field.dataset.fieldType)) {
-				return;
-			}
-			let name = field.dataset.field;
-			let value = this.forms.getFieldValue(e.target);
 			this.updateItem(itemId, name, value);
 		});
 	}
 	updateItem(itemId, name, value) {
+		if (this.isPopulating) {
+			return;
+		}
+
+		const stored = this.store.get(itemId);
+		if (stored) {
+			const storedValue = stored.fields?.[name] ?? stored[name];
+			const diff = window.getDifferences.map(storedValue, value);
+
+			if (diff === null) {
+				// Value matches stored — clean up any pending change for this field
+				if (this.changes.has(itemId)) {
+					delete this.changes.get(itemId)[name];
+					// If no real changes left, remove the item entirely
+					const remaining = Object.keys(this.changes.get(itemId))
+						.filter(k => k !== 'id' && k !== 'content');
+					if (remaining.length === 0) {
+						this.changes.delete(itemId);
+						this.changesStore.delete(itemId);
+					}
+				}
+				return;
+			}
+		}
+
 		if (!this.changes.has(itemId)) {
 			this.changes.set(itemId, { id: itemId, content: this.content });
 		}
 		this.changes.get(itemId)[name] = value;
 
 		this.scheduleBackup();
-		//Only send actual itemIds to server. If this is a recently uploaded item, just store changes for now
-		if (typeof itemId === 'number' || !itemId.includes('group')) {
+		if (typeof itemId === 'number' || !String(itemId).includes('group')) {
 			this.scheduleSave();
 		}
 	}
@@ -973,8 +1002,8 @@
 			this.forms.registerForm(this.ui.modals.create.form,{
 				cache: false,
 			});
-
 			this.ui.modals.create.modal.dataset.itemId = window.generateID('new');
+
 			this.modals.create.handleOpen();
 		}
 		handleActionButton(button) {
@@ -1185,12 +1214,17 @@
 		this.ui.modals.edit.h2.textContent = `Editing ${item.fields.post_title === '' ? this.singular : item.fields.post_title}`;
 		this.ui.modals.edit.form.dataset.formId = `edit-${itemID}`;
 
+
 		this.forms.registerForm(this.ui.modals.edit.form, {cache: false,
 			autoUpload: true,});
-
 		this.isPopulating = true;
 		this.populate.populate(this.ui.modals.edit.form, item);
-		this.isPopulating = false;
+		//For quill/taxonomy selector's async setups
+		requestAnimationFrame(() => {
+			requestAnimationFrame(() => {
+				this.isPopulating = false;
+			});
+		});
 
 		this.modals.edit.handleOpen();
 	}
@@ -1218,11 +1252,15 @@
 		}
 		this.modals.bulkEdit.handleOpen();
 
-		this.forms.registerForm(this.ui.modals.bulkEdit.form, {cache:false});
 
+		this.forms.registerForm(this.ui.modals.bulkEdit.form, {cache:false});
 		this.isPopulating = true;
 		this.populate.populate(this.ui.modals.edit.form, item);
-		this.isPopulating = false;
+		requestAnimationFrame(() => {
+			requestAnimationFrame(() => {
+				this.isPopulating = false;
+			});
+		});
 	}
 
 	/*****************************************************************
@@ -1234,8 +1272,11 @@
 			this.cancelBackup();
 			await this.handleBackup();
 		}
-		const changes = await this.changesStore.getAll();
-		console.log('Saving Changes: ', changes);
+		let changes = await this.changesStore.getAll();
+		if (changes.length === 0) return;
+
+		// Filter out false positives
+		changes = this.validateChanges(changes);
 		if (changes.length === 0) return;
 
 		if (title === '') {
@@ -1247,8 +1288,6 @@
 
 		changes.forEach(change => {
 			let itemId = change.id;
-
-			// Create a new object without the id field (don't mutate original!)
 			const { id, ...changeWithoutId } = change;
 			allChanges[itemId] = changeWithoutId;
 
@@ -1276,6 +1315,44 @@
 		this.queue.addToQueue(operation);
 	}
 
+	/**
+	 * Compare pending changes against the store, removing unchanged fields.
+	 * Returns cleaned array (may be empty if nothing actually changed).
+	 */
+	validateChanges(changes) {
+		return changes.reduce((valid, change) => {
+			const { id, content, ...fields } = change;
+			const stored = this.store.get(id);
+
+			if (!stored) {
+				valid.push(change);
+				return valid;
+			}
+
+			const realChanges = { id, content };
+			let hasRealChange = false;
+
+			for (const [name, value] of Object.entries(fields)) {
+				const storedValue = stored.fields?.[name] ?? stored[name];
+				const diff = window.getDifferences.map(storedValue, value);
+
+				if (diff !== null) {
+					realChanges[name] = value;
+					hasRealChange = true;
+				}
+			}
+
+			if (hasRealChange) {
+				valid.push(realChanges);
+			} else {
+				this.changes.delete(id);
+				this.changesStore.delete(id);
+			}
+
+			return valid;
+		}, []);
+	}
+
 
 	setBulkStatus(status) {
 		if (!['publish', 'draft', 'trash', 'delete'].includes(status)) return;
@@ -1472,10 +1549,6 @@
 		let uploader = window.jvbUploads;
 		let field = uploader.fields.get(fieldId);
 
-
-		console.log(posts);
-		console.log(field);
-
 		let added = [];
 		posts.forEach(post => {
 			const placeholderPost = {
@@ -1540,7 +1613,6 @@
 	}
 
 	handleGroupMappings(mappings) {
-		console.log('[CRUD] Applying group mappings:', mappings);
 		// mappings = { "group_abc123": 456, "group_def456": 789 }
 
 		for (const [groupId, postId] of Object.entries(mappings)) {

--
Gitblit v1.10.0