From d7dbe7fee362d587dfc334135d9581b6216a4295 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 23 Nov 2025 04:13:56 +0000
Subject: [PATCH] =Timeline block, and feed block updated. DataStore.js refactored to not block rendering

---
 assets/js/dash/TaxonomyCreator.js |  242 +++++++++++++++++++-----------------------------
 1 files changed, 97 insertions(+), 145 deletions(-)

diff --git a/assets/js/dash/TaxonomyCreator.js b/assets/js/dash/TaxonomyCreator.js
index 8f27b17..37b5960 100644
--- a/assets/js/dash/TaxonomyCreator.js
+++ b/assets/js/dash/TaxonomyCreator.js
@@ -8,19 +8,19 @@
 	constructor(selector) {
 		this.selector = selector;
 
-		// Get taxonomy from current active field config
-		this.taxonomy = selector.currentConfig?.taxonomy;
-		if (!this.taxonomy) {
-			console.error('TaxonomyCreator: No active field or taxonomy found');
-			return;
+		// Only initialize modal elements if modal exists
+		if (selector.modal) {
+			this.createNew = selector.modal.querySelector('.create-new-term');
+			this.toggle = selector.modal.querySelector('.new-term-toggle');
+			this.form = this.createNew?.querySelector('.create-new-term-section');
 		}
 
-		this.createNew = selector.modal.querySelector('.create-new-term');
-		this.toggle = selector.modal.querySelector('.new-term-toggle');
-		this.form = this.createNew.querySelector('.create-new-term-section');
-
 		this.initListeners();
-		this.initTermCreation();
+
+		// Only init term creation UI if we have modal elements
+		if (this.form) {
+			this.initTermCreation();
+		}
 	}
 
 	initListeners() {
@@ -47,6 +47,9 @@
 	}
 
 	async handleTermCreation(e) {
+		const taxonomy = this.selector.currentConfig?.taxonomy;
+		if (!taxonomy) return;
+
 		const termName = this.form.querySelector('input[name="term_name"]').value.trim();
 		const parentId = parseInt(this.form.querySelector('input#select_parent')?.value) || 0;
 
@@ -54,98 +57,127 @@
 
 		try {
 			this.form.querySelector('button').disabled = true;
-			const response = await this.createTerm(termName, parentId);
+			const response = await this.createTerm(termName, parentId, taxonomy);
 
 			if (response.success && response.term) {
 				let term = response.term;
 
-				// Close the create new section
 				this.createNew.open = false;
+				await this.selector.store.clearCache();
 
-				// Invalidate the cache for this taxonomy
-				await this.selector.store.invalidate({ taxonomy: this.taxonomy });
+				// Add to store's data BEFORE display update
+				this.selector.store.data.set(term.id, {
+					id: term.id,
+					name: term.name,
+					path: termPath,
+					taxonomy: field.taxonomy,
+					parent: 0,
+					count: 0,
+					hasChildren: false,
+					slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
+				});
 
-				// Add to current modal selection
 				this.selector.addSelectedTermToModal(term.id, term.name, term.path || term.name);
 
-				// If we're viewing the parent category where this was created, refresh the list
 				const currentParent = this.selector.store.filters.parent || 0;
 				if (currentParent === parentId) {
 					await this.selector.store.setFilters({
-						taxonomy: this.taxonomy,
+						taxonomy,
 						parent: parentId,
 						page: 1,
 						search: ''
 					});
 				}
 
-				// Clear the form
 				this.form.querySelector('input[name="term_name"]').value = '';
-
-				// Clear suggestions
 				const suggestionContainer = this.createNew.querySelector('.term-suggestions');
 				if (suggestionContainer) {
 					suggestionContainer.hidden = true;
 				}
+				this.selector.store.cache.clear();
 			}
 		} catch (error) {
 			console.error('Error creating term:', error);
 			this.selector.error?.log(error, {
 				component: 'TaxonomyCreator',
 				action: 'handleTermCreation'
-			}) || console.error('Failed to create term');
+			});
 		} finally {
 			this.form.querySelector('button').disabled = false;
 		}
 	}
 
 	async handleAutocompleteCreate(e) {
-		const button = e.target;
+		const button = e.target.closest('.create-term');
 		const fieldId = this.selector.getFieldId(button);
 		const field = this.selector.fields.get(fieldId);
 
 		if (!field) return;
 
-		// Get the current input value (not the debounced query)
 		const input = field.container.querySelector('input[data-autocomplete]');
 		const termName = input?.value.trim() || button.dataset.query;
 
 		if (!termName) return;
 
+		const originalHTML = button.innerHTML;
+
 		try {
 			button.disabled = true;
-			const originalText = button.innerHTML;
 			button.textContent = 'Creating...';
 
-			const response = await this.createTerm(termName, 0);
+			const response = await this.createTerm(termName, 0, field.taxonomy);
 
 			if (response.success && response.term) {
 				const term = response.term;
+				const termPath = term.path || term.name;
 
-				// Add term to field
 				field.selectedTerms.add(parseInt(term.id));
-				this.selector.addTermToDisplay(field.id, term.id, term.name, term.path || term.name);
+				// Add to store's data BEFORE display update
+				this.selector.store.data.set(term.id, {
+					id: term.id,
+					name: term.name,
+					path: termPath,
+					taxonomy: field.taxonomy,
+					parent: 0,
+					count: 0,
+					hasChildren: false,
+					slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
+				});
+				this.selector.addTermToDisplay(field.id, term.id, term.name, termPath);
 
-				// Update input
 				field.input.value = Array.from(field.selectedTerms).join(',');
 				field.input.dispatchEvent(new Event('change', { bubbles: true }));
 
-				// Clear and hide dropdown
 				field.autocompleteDropdown.hidden = true;
 				if (input) input.value = '';
 
-				// Refresh the store to include the new term
-				await this.selector.store.setFilter('taxonomy', field.taxonomy);
-			} else {
-				button.innerHTML = originalText;
+				this.selector.store.clearCache();
+				await this.selector.store.setFilters({
+					taxonomy: field.taxonomy,
+					page: 1,
+					search: '',
+					parent: 0
+				});
+			} else if (response.reason === 'exists' && response.term) {
+				// Term already exists - just add it
+				const term = response.term;
+				field.selectedTerms.add(parseInt(term.id));
+				this.selector.addTermToDisplay(field.id, term.id, term.name, term.path || term.name);
+
+				field.input.value = Array.from(field.selectedTerms).join(',');
+				field.input.dispatchEvent(new Event('change', { bubbles: true }));
+
+				field.autocompleteDropdown.hidden = true;
+				if (input) input.value = '';
 			}
 		} catch (error) {
 			console.error('Error creating term:', error);
-			button.textContent = 'Failed - Try again';
-			setTimeout(() => {
-				button.innerHTML = `<strong>Create:</strong> "${termName}"`;
-				button.disabled = false;
-			}, 2000);
+			button.innerHTML = originalHTML;
+			button.disabled = false;
+			this.selector.error?.log(error, {
+				component: 'TaxonomyCreator',
+				action: 'handleAutocompleteCreate'
+			});
 		}
 	}
 
@@ -161,6 +193,9 @@
 	}
 
 	resetParentOptions() {
+		const taxonomy = this.selector.currentConfig?.taxonomy;
+		if (!taxonomy) return;
+
 		let select = this.createNew.querySelector('#select_parent');
 		if (!select) return;
 
@@ -188,7 +223,7 @@
 		// Add all terms currently visible in the taxonomy (from store cache)
 		const visibleTerms = [];
 		this.selector.store.data.forEach(term => {
-			if (term.taxonomy === this.taxonomy && term.parent === currentParent) {
+			if (term.taxonomy === taxonomy && term.parent === currentParent) {
 				visibleTerms.push(term);
 			}
 		});
@@ -206,45 +241,32 @@
 		});
 	}
 
-	async createTerm(name, parent = 0) {
-		let loadingMessage = this.createNew.querySelector('.loading-message.create-term');
-		let text = loadingMessage?.querySelector('span');
-
+	async createTerm(name, parent = 0, taxonomy) {
 		try {
-			if (loadingMessage) {
-				loadingMessage.hidden = false;
-			}
+			// Search for the exact term first
+			await this.selector.store.setFilters({
+				taxonomy: taxonomy,
+				search: name,
+				page: 1,
+				parent: 0
+			});
 
-			if (text && window.typeText) {
-				window.typeText(text, 'Checking term...');
-			} else if (text) {
-				text.textContent = 'Checking term...';
-			}
+			// Check if exact match exists in results
+			const exactMatch = Array.from(this.selector.store.data.values())
+				.find(term =>
+					term.taxonomy === taxonomy &&
+					term.name.toLowerCase() === name.toLowerCase()
+				);
 
-			// Search for existing terms with this name
-			const searchResults = await this.searchExistingTerms(name);
-
-			// Check for exact matches
-			const exactMatches = searchResults.filter(term =>
-				term.name.toLowerCase() === name.toLowerCase()
-			);
-
-			if (exactMatches.length > 0) {
-				this.showTermSuggestions(exactMatches, true);
-				return { success: false, reason: 'exists' };
-			}
-
-			// Show similar terms if found
-			if (searchResults.length > 0) {
-				this.showTermSuggestions(searchResults, false);
-				return { success: false, reason: 'similar' };
+			if (exactMatch) {
+				// For modal context, show suggestions
+				if (this.createNew) {
+					this.showTermSuggestions([exactMatch], true);
+				}
+				return { success: false, reason: 'exists', term: exactMatch };
 			}
 
 			// Term doesn't exist, create it
-			if (text) {
-				text.textContent = 'Creating term...';
-			}
-
 			const response = await fetch(`${jvbSettings.api}terms`, {
 				method: 'POST',
 				headers: {
@@ -252,7 +274,7 @@
 					'X-WP-Nonce': jvbSettings.nonce
 				},
 				body: JSON.stringify({
-					taxonomy: this.taxonomy,
+					taxonomy: taxonomy,
 					name: name,
 					parent: parent
 				})
@@ -267,18 +289,13 @@
 		} catch (error) {
 			console.error('Error creating term:', error);
 			throw error;
-		} finally {
-			this.form.querySelector('button').disabled = false;
-			if (loadingMessage) {
-				loadingMessage.hidden = true;
-			}
 		}
 	}
 
 	/**
 	 * Search for existing terms using the store
 	 */
-	async searchExistingTerms(searchQuery) {
+	async searchExistingTerms(searchQuery, taxonomy) {
 		return new Promise((resolve) => {
 			// Set up a one-time listener for the search results
 			const handleSearchResults = (event, data) => {
@@ -292,7 +309,7 @@
 
 			// Trigger search
 			this.selector.store.setFilters({
-				taxonomy: this.taxonomy,
+				taxonomy: taxonomy,
 				search: searchQuery,
 				page: 1,
 				parent: 0
@@ -365,71 +382,6 @@
 		return container;
 	}
 
-	/**
-	 * Create "Create new term" option for autocomplete dropdown
-	 */
-	createAutocompleteOption(query, field) {
-		const button = document.createElement('button');
-		button.type = 'button';
-		button.className = 'autocomplete-item create-term';
-		button.innerHTML = `<span>Create "${query}"</span>`;
-		button.dataset.query = query;
-		button.dataset.fieldId = field.id;
-
-		button.addEventListener('click', async () => {
-			await this.handleAutocompleteCreate(button, query, field);
-		});
-
-		return button;
-	}
-
-
-	/**
-	 * Handle term creation from autocomplete
-	 */
-	async handleAutocompleteCreate(button, termName, field) {
-		if (!field) return;
-
-		const originalHTML = button.innerHTML;
-
-		try {
-			button.disabled = true;
-			button.innerHTML = '<span>Creating...</span>';
-
-			const parentId = 0; // Autocomplete always creates at root level
-			const result = await this.createTerm(termName, parentId);
-
-			if (result.success && result.term) {
-				const term = result.term;
-
-				// Add to field
-				field.selectedTerms.add(parseInt(term.id));
-				this.selector.addTermToDisplay(field.id, term.id, term.name, term.path || term.name);
-
-				// Update input
-				field.input.value = Array.from(field.selectedTerms).join(',');
-				field.input.dispatchEvent(new Event('change', { bubbles: true }));
-
-				// Invalidate cache
-				await this.selector.store.invalidate({ taxonomy: field.taxonomy });
-
-				// Clear and hide dropdown
-				field.autocompleteDropdown.hidden = true;
-				const input = field.container.querySelector('input[data-autocomplete]');
-				if (input) input.value = '';
-			}
-			// If result.success is false, suggestions are already shown
-
-		} catch (error) {
-			console.error('Error creating term:', error);
-			button.innerHTML = originalHTML;
-			button.disabled = false;
-			this.selector.error?.log(error, {
-				component: 'TaxonomyCreator',
-				action: 'handleAutocompleteCreate'
-			});
-		}
-	}
 
 	/**
 	 * Clean up when modal closes

--
Gitblit v1.10.0