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 | 187 ++++++++++++++++++++++++++++++++++++++--------
1 files changed, 154 insertions(+), 33 deletions(-)
diff --git a/assets/js/concise/CRUD.js b/assets/js/concise/CRUD.js
index 8c29ab2..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)
@@ -639,14 +646,62 @@
const form = e.target;
const modal = form.closest('dialog');
if (!modal) return;
- let title = `Saving changes for multiple ${this.plural}`;
- if (modal.classList.contains('edit')) {
- title = 'Saving your edits...';
- } else if (modal.classList.contains('create')) {
- title = `Creating your new ${this.singular}`;
+
+ if (modal.classList.contains('create')) {
+ this.handleCreateSubmit(modal);
+ return;
}
+
+ let title = `Saving changes for multiple ${this.plural}`;
+
this.scheduleSave(0);
}
+
+ async handleCreateSubmit(modal) {
+ const itemId = modal.dataset.itemId;
+
+ // 1. Flush changes to store
+ if (this.changes.size > 0) {
+ this.cancelBackup();
+ await this.handleBackup();
+ }
+
+ const changes = await this.changesStore.getAll();
+ if (changes.length === 0) return;
+
+ let allChanges = {};
+ changes.forEach(change => {
+ const { id, ...rest } = change;
+ allChanges[id] = rest;
+ });
+
+ // 2. Queue content creation, get operationId
+ let contentOpId = this.queue.addToQueue({
+ endpoint: this.endpoint,
+ headers: {
+ 'X-Action-Nonce': window.auth.getNonce('dash'),
+ },
+ data: {
+ posts: allChanges,
+ },
+ popup: `Creating your new ${this.singular}`,
+ title: `Creating your new ${this.singular}`,
+ });
+
+ if (!contentOpId) return;
+
+ // 3. Queue any pending uploads with dependency on content creation
+ const uploadFields = modal.querySelectorAll('[data-upload-field]');
+ for (const fieldEl of uploadFields) {
+ const fieldId = fieldEl.dataset.uploader;
+ if (!fieldId) continue;
+
+ const uploads = window.jvbUploads.stores.uploads.filterByIndex({ field: fieldId });
+ if (uploads.length === 0) continue;
+
+ await window.jvbUploads.queueUploads('uploads', fieldId, contentOpId);
+ }
+ }
handleChange(e) {
// Early bailout - target must be in an item or be a filter
const inItem = e.target.closest('[data-item-id]');
@@ -743,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();
}
}
@@ -925,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) {
@@ -1137,11 +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});
+ 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();
}
@@ -1169,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;
+ });
+ });
}
/*****************************************************************
@@ -1185,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 === '') {
@@ -1198,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;
@@ -1227,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;
@@ -1423,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 = {
@@ -1491,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