From 552d48a1424417da160c4952650ea6f4a3d7bafa Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 04 Jan 2026 20:18:23 +0000
Subject: [PATCH] =Taxonomy Selector and Creator refactor

---
 assets/js/concise/TaxonomyCreator.js |  300 +++++++++++++++++++++++++++++++++++------------------------
 1 files changed, 179 insertions(+), 121 deletions(-)

diff --git a/assets/js/concise/TaxonomyCreator.js b/assets/js/concise/TaxonomyCreator.js
index 7307202..2ea387b 100644
--- a/assets/js/concise/TaxonomyCreator.js
+++ b/assets/js/concise/TaxonomyCreator.js
@@ -1,8 +1,7 @@
 /**
- * This separates out all create logic from the base TaxonomySelector.js
- * Updated to work with centralized DataStore architecture
+ * TaxonomyCreator - Handles term creation for TaxonomySelector
+ * Simplified to focus only on creation logic
  */
-
 class TaxonomyCreator {
 
 	constructor(selector) {
@@ -23,12 +22,19 @@
 		}
 	}
 
+	/**
+	 * Initialize event listeners
+	 */
 	initListeners() {
 		this.clickHandler = this.handleClick.bind(this);
 		document.addEventListener('click', this.clickHandler);
 	}
 
+	/**
+	 * Handle click events
+	 */
 	handleClick(e) {
+		// Handle opening create term form
 		if (window.targetCheck(e, '.create-new-term summary')) {
 			if (this.createNew.open) {
 				this.createNew.querySelector('input[name="term_name"]').focus();
@@ -36,16 +42,20 @@
 			this.resetParentOptions();
 		}
 
+		// Handle term creation submission
 		if (window.targetCheck(e, '.submit-term')) {
-			this.handleTermCreation(e).then(()=>{});
+			this.handleTermCreation(e).then(() => {});
 		}
 
 		// Handle autocomplete create button
 		if (window.targetCheck(e, '.create-term')) {
-			this.handleAutocompleteCreate(e).then(()=>{});
+			this.handleAutocompleteCreate(e).then(() => {});
 		}
 	}
 
+	/**
+	 * Handle term creation from modal form
+	 */
 	async handleTermCreation(e) {
 		const taxonomy = this.selector.currentConfig?.taxonomy;
 		if (!taxonomy) return;
@@ -55,61 +65,71 @@
 
 		if (!termName) return;
 
+		const submitButton = this.form.querySelector('button');
+
 		try {
-			const submitButton = this.form.querySelector('button');
 			if (submitButton) {
 				submitButton.disabled = true;
 			}
+
 			const response = await this.createTerm(termName, parentId, taxonomy);
 
 			if (response.success && response.term) {
-				let term = response.term;
-				const termPath = term.path || term.name;
-
-				this.createNew.open = false;
-				await this.selector.store.clearCache();
-
-				this.selector.store.data.set(term.id, {
-					id: term.id,
-					name: term.name,
-					path: termPath,
-					taxonomy: taxonomy,
-					parent: parentId,
-					count: 0,
-					hasChildren: false,
-					slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
-				});
-
-				this.selector.addSelectedTermToModal(term.id, term.name, termPath);
-
-				const currentParent = this.selector.store.filters.parent || 0;
-				if (currentParent === parentId) {
-					await this.selector.store.setFilters({
-						taxonomy,
-						parent: parentId,
-						page: 1,
-						search: ''
-					});
-				}
-
-				this.form.querySelector('input[name="term_name"]').value = '';
-				const suggestionContainer = this.createNew.querySelector('.term-suggestions');
-				if (suggestionContainer) {
-					suggestionContainer.hidden = true;
-				}
-
+				await this.handleSuccessfulCreation(response.term, taxonomy, parentId);
+				this.clearForm();
 			}
 		} catch (error) {
 			console.error('Error creating term:', error);
-			this.selector.error?.log(error, {
-				component: 'TaxonomyCreator',
-				action: 'handleTermCreation'
-			});
+			this.selector.handleError(error, 'handleTermCreation');
 		} finally {
-			this.form.querySelector('button').disabled = false;
+			if (submitButton) {
+				submitButton.disabled = false;
+			}
 		}
 	}
 
+	/**
+	 * Handle successful term creation
+	 */
+	async handleSuccessfulCreation(term, taxonomy, parentId) {
+		const termPath = term.path || term.name;
+
+		// Close create form
+		this.createNew.open = false;
+
+		// Clear cache to ensure fresh data
+		await this.selector.store.clearCache();
+
+		// Add to DataStore
+		this.selector.store.data.set(term.id, {
+			id: term.id,
+			name: term.name,
+			path: termPath,
+			taxonomy: taxonomy,
+			parent: parentId,
+			count: 0,
+			hasChildren: false,
+			slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
+		});
+
+		// Add to modal selection
+		this.selector.addSelectedTermToModal(term.id, term.name, termPath);
+
+		// Refresh current view if we're viewing the same parent
+		const currentParent = this.selector.store.filters.parent || 0;
+		if (currentParent === parentId) {
+			await this.selector.store.setFilters({
+				taxonomy,
+				parent: parentId,
+				page: 1,
+				search: ''
+			});
+		}
+	}
+
+	/**
+	 * Handle autocomplete create button
+	 */
 	async handleAutocompleteCreate(e) {
 		const button = e.target.closest('.create-term');
 		const fieldId = this.selector.getFieldId(button);
@@ -131,65 +151,74 @@
 			const response = await this.createTerm(termName, 0, field.taxonomy);
 
 			if (response.success && response.term) {
-				const term = response.term;
-				const termPath = term.path || term.name;
-
-				field.selectedTerms.add(parseInt(term.id));
-				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);
-
-				field.input.value = Array.from(field.selectedTerms).join(',');
-				field.input.dispatchEvent(new Event('change', { bubbles: true }));
-
-				field.autocompleteDropdown.hidden = true;
-				if (input) input.value = '';
-
-				// Clear cache AND refresh this taxonomy's data
-				this.selector.store.clearCache();
-
-				// Trigger a background refresh for this taxonomy
-				await this.selector.store.setFilters({
-					taxonomy: field.taxonomy,
-					page: 1,
-					search: '',
-					parent: 0
-				});
+				await this.handleAutocompleteSuccess(response.term, field, input);
 			} else if (response.reason === 'exists' && response.term) {
-				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 = '';
+				this.handleExistingTerm(response.term, field, input);
 			}
 		} catch (error) {
 			console.error('Error creating term:', error);
-			this.selector.error?.log(error, {
-				component: 'TaxonomyCreator',
-				action: 'handleAutocompleteCreate'
-			});
+			this.selector.handleError(error, 'handleAutocompleteCreate');
 		} finally {
 			button.innerHTML = originalHTML;
 			button.disabled = false;
 		}
 	}
 
+	/**
+	 * Handle successful autocomplete creation
+	 */
+	async handleAutocompleteSuccess(term, field, input) {
+		const termPath = term.path || term.name;
+
+		// Add to field
+		field.selectedTerms.add(parseInt(term.id));
+
+		// Add to DataStore
+		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 || term.name.toLowerCase().replace(/\s+/g, '-')
+		});
+
+		// Update display
+		this.selector.addTermDisplay(term.id, term.name, termPath, 'field', field.id);
+
+		// Update input and trigger change
+		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 = '';
+
+		// Clear cache for this taxonomy
+		await this.selector.store.clearCache();
+	}
+
+	/**
+	 * Handle selecting existing term from autocomplete
+	 */
+	handleExistingTerm(term, field, input) {
+		field.selectedTerms.add(parseInt(term.id));
+		this.selector.addTermDisplay(term.id, term.name, term.path || term.name, 'field', field.id);
+
+		field.input.value = Array.from(field.selectedTerms).join(',');
+		field.input.dispatchEvent(new Event('change', { bubbles: true }));
+
+		field.autocompleteDropdown.hidden = true;
+		if (input) input.value = '';
+	}
+
+	/**
+	 * Initialize term creation form
+	 */
 	initTermCreation() {
-		if (!this.form) {
-			return;
-		}
+		if (!this.form) return;
 
 		this.form.addEventListener('change', (e) => {
 			e.preventDefault();
@@ -197,6 +226,9 @@
 		});
 	}
 
+	/**
+	 * Reset parent options in create form
+	 */
 	resetParentOptions() {
 		const taxonomy = this.selector.currentConfig?.taxonomy;
 		if (!taxonomy) return;
@@ -225,7 +257,7 @@
 			}
 		}
 
-		// Add all terms currently visible in the taxonomy (from store cache)
+		// Add all terms currently visible in the taxonomy
 		const visibleTerms = [];
 		this.selector.store.data.forEach(term => {
 			if (term.taxonomy === taxonomy && term.parent === currentParent) {
@@ -246,9 +278,12 @@
 		});
 	}
 
+	/**
+	 * Create a new term
+	 */
 	async createTerm(name, parent = 0, taxonomy) {
 		try {
-			// Search to ensure we have latest data for duplicate check
+			// Search to check for duplicates
 			await this.selector.store.setFilters({
 				taxonomy: taxonomy,
 				search: name,
@@ -256,10 +291,10 @@
 				parent: 0
 			});
 
-			// Wait a bit for the data to load
+			// Wait for data to load
 			await new Promise(resolve => setTimeout(resolve, 100));
 
-			// Check if exact match exists in results
+			// Check if exact match exists
 			const exactMatch = Array.from(this.selector.store.data.values())
 				.find(term =>
 					term.taxonomy === taxonomy &&
@@ -323,27 +358,7 @@
 
 		suggestions.forEach(term => {
 			const item = document.createElement('li');
-
-			const button = document.createElement('button');
-			button.type = 'button';
-			button.className = 'use-existing-term';
-			button.setAttribute('data-id', term.id);
-			button.textContent = term.path || term.name;
-
-			button.addEventListener('click', () => {
-				// Add this term to modal selection
-				this.selector.addSelectedTermToModal(term.id, term.name, term.path || term.name);
-
-				// Close the create new section
-				this.createNew.open = false;
-
-				// Clear suggestions
-				suggestionContainer.hidden = true;
-
-				// Clear the form
-				this.form.querySelector('input[name="term_name"]').value = '';
-			});
-
+			const button = this.createSuggestionButton(term);
 			item.appendChild(button);
 			list.appendChild(item);
 		});
@@ -353,7 +368,36 @@
 	}
 
 	/**
-	 * Create container for term suggestions if it doesn't exist
+	 * Create suggestion button
+	 */
+	createSuggestionButton(term) {
+		const button = document.createElement('button');
+		button.type = 'button';
+		button.className = 'use-existing-term';
+		button.dataset.id = term.id;
+		button.textContent = term.path || term.name;
+
+		button.addEventListener('click', () => {
+			// Add this term to modal selection
+			this.selector.addSelectedTermToModal(term.id, term.name, term.path || term.name);
+
+			// Close the create new section
+			this.createNew.open = false;
+
+			// Clear suggestions and form
+			const suggestionContainer = this.createNew.querySelector('.term-suggestions');
+			if (suggestionContainer) {
+				suggestionContainer.hidden = true;
+			}
+
+			this.clearForm();
+		});
+
+		return button;
+	}
+
+	/**
+	 * Create container for term suggestions
 	 */
 	createSuggestionContainer() {
 		const container = document.createElement('div');
@@ -365,9 +409,23 @@
 		return container;
 	}
 
+	/**
+	 * Clear the creation form
+	 */
+	clearForm() {
+		const nameInput = this.form.querySelector('input[name="term_name"]');
+		if (nameInput) {
+			nameInput.value = '';
+		}
+
+		const suggestionContainer = this.createNew.querySelector('.term-suggestions');
+		if (suggestionContainer) {
+			suggestionContainer.hidden = true;
+		}
+	}
 
 	/**
-	 * Clean up when modal closes
+	 * Clean up when destroyed
 	 */
 	destroy() {
 		// Remove event listeners

--
Gitblit v1.10.0