From 2127b1bdd73ecd2423e443992da4b442f5a3c1a3 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 04 Feb 2026 21:19:25 +0000
Subject: [PATCH] =Major overhaul of MetaManager.php -> Meta.php and RestRouteManager.php -> Rest.php. Seems to work for JakeVan

---
 assets/js/concise/TaxonomySelector.js |  118 +++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 67 insertions(+), 51 deletions(-)

diff --git a/assets/js/concise/TaxonomySelector.js b/assets/js/concise/TaxonomySelector.js
index bdbe06b..8faa8d7 100644
--- a/assets/js/concise/TaxonomySelector.js
+++ b/assets/js/concise/TaxonomySelector.js
@@ -44,7 +44,7 @@
 				indexes: [
 					{name: 'taxonomy', keyPath: 'taxonomy'},
 					{name: 'parent', keyPath: 'parent'},
-					{name: 'slug', keyPath: 'slug', unique: true},
+					{name: 'slug', keyPath: 'slug'},
 					{name: 'count', keyPath: 'count'},
 				],
 				endpoint: 'terms',
@@ -96,14 +96,14 @@
 
 				if (refs.checkbox) {
 					refs.checkbox.dataset.id = data.id;
-					refs.checkbox.id = `${field.element.id}-${data.id}`;
-					refs.checkbox.name = `${field.element.id}-${field.taxonomy}-select`;
+					refs.checkbox.id = `${field.id}-${data.id}`;
+					refs.checkbox.name = `${field.id}-${field.taxonomy}-select`;
 					refs.checkbox.value = data.id;
 					refs.checkbox.disabled = !isSelected && limitReached;
 					refs.checkbox.checked = isSelected;
 				}
 				if (refs.label) {
-					refs.label.htmlFor = `${field.element.id}-${data.id}`;
+					refs.label.htmlFor = `${field.id}-${data.id}`;
 					refs.label.title = data.path??data.name;
 					refs.label.dataset.path = data.path;
 				}
@@ -184,7 +184,7 @@
 			},
 			favourites: '.favourite-terms',
 			field: {
-				toggle: 'button.taxonomy-toggle, [data-filter="taxonomy"]',
+				toggle: 'button.selector-toggle, [data-filter="taxonomy"]',
 				value: 'input[type="hidden"]',
 				selected: '.selected-items',
 				dropdown: {
@@ -239,6 +239,13 @@
 		const field = this.fields.get(fieldId);
 		if (!fieldId || !field) return;
 
+		if (this.creator) {
+			let button = window.targetCheck(e, this.selectors.create.button);
+			if (button) {
+				this.maybeCreateTerm(e).then(()=>{});
+			}
+		}
+
 		const autocomplete = window.targetCheck(e, '.item.autocomplete');
 
 		if (autocomplete) {
@@ -320,14 +327,6 @@
 				this.ui.search.input.value = '';
 			}
 		}
-
-		if (this.creator) {
-			let button = window.targetCheck(e, this.selectors.create.button);
-			if (button) {
-				this.maybeCreateTerm(e).then(()=>{});
-			}
-		}
-
 	}
 	handleChange(e) {
 		if (!this.container.contains(e.target) && !e.target.closest('[data-type="selector"], [data-field-type="selector"]')) {
@@ -365,7 +364,7 @@
 		}
 
 		let query = e.target.value.trim();
-		this.setMessage(true, `Searching for "${query}" in ${field.plural??'items'}`);
+		this.setMessage(field,true, `Searching for "${query}" in ${field.plural??'items'}`);
 		window.debouncer.schedule(
 			`${fieldId}-search`,
 			async () => {
@@ -390,7 +389,7 @@
 			return;
 		}
 		this.activeField = fieldId;
-		this.setMessage(true, `Loading ${field.plural}...`);
+		this.setMessage(field,true, `Loading ${field.plural}...`);
 		this.resetFilters({taxonomy: field.taxonomy});
 	}
 
@@ -841,8 +840,10 @@
 		const field = this.fields.get(fieldId);
 		if (!field) return;
 		let selected = Array.from(this.selectedTerms.get(fieldId));
-		field.ui.value.value = selected.join(',')??'';
-		field.ui.value.dispatchEvent(new Event('change', { bubbles: true }));
+		if (field.ui.value) {
+			field.ui.value.value = selected.join(',')??'';
+			field.ui.value.dispatchEvent(new Event('change', { bubbles: true }));
+		}
 	}
 
 	checkLimits(fieldId) {
@@ -875,9 +876,9 @@
 
 	updateFieldsForTaxonomy(taxonomy) {
 		let fields = Array.from(this.fields.values())
-			.filter(field => !field.checked && field.taxonomy === taxonomy);
+			.filter(field => field.taxonomy === taxonomy);
 		const hasItems = Array.from(this.store.data.values())
-			.some(term=>term.taxonomy === taxonomy);
+			.some(term => term && term.taxonomy === taxonomy);
 
 		fields.forEach(field => {
 			if (!field.toggle) return;
@@ -897,7 +898,7 @@
 			if (this.store.filters.page??1 === 1) {
 				window.removeChildren(this.ui.terms.list);
 			}
-			this.setMessage(true, this.store.filters.search === ''
+			this.setMessage(field,true, this.store.filters.search === ''
 				? `No matching ${field.plural}.`
 				: `No ${field.plural} found.`,
 				false);
@@ -907,7 +908,7 @@
 			return;
 		}
 
-		this.setCreateButton(true);
+		this.setCreateButton(field,true);
 
 		if (this.ui.terms.sentinel) {
 			if (this.store.lastResponse?.has_more) {
@@ -928,7 +929,7 @@
 		).then(()=>{});
 
 		if (terms.length > 0) {
-			this.setMessage(false);
+			this.setMessage(field,false);
 		}
 	}
 	createTermElement(term) {
@@ -938,15 +939,13 @@
 
 	showAutocompleteTerms() {
 		const field = this.currentField();
-		const terms = this.currentTerms();
-		if (!field) return;
-
+		if (!field || !field.hasAutocomplete || !field.ui.dropdown?.list) return;
 		const dropdown = field.ui.dropdown.list;
-		if (!dropdown) return;
+		const terms = this.currentTerms();
 
 		window.removeChildren(dropdown);
 		if (terms.length === 0) {
-			this.setMessage(true, `No ${field.plural} found.`, false);
+			this.setMessage(field,true, `No ${field.plural} found.`, false);
 		} else {
 			window.chunkIt(
 				terms,
@@ -954,9 +953,9 @@
 				(fragment) => dropdown.append(fragment)
 			).then(()=>{});
 
-			this.setMessage(false);
+			this.setMessage(field,false);
 		}
-		this.setCreateButton(true);
+		this.setCreateButton(field,true);
 
 		if (field.ui.dropdown.wrapper) {
 			field.ui.dropdown.wrapper.hidden = false;
@@ -1084,13 +1083,11 @@
 			handlers[event]?.(data);
 		} catch (error) {
 			console.error(`Error handling store event "${event}":`, error);
-			this.setMessage(true, 'An error occurred loading data', false);
 		}
 	}
 	handleDataLoaded() {
 		const taxonomy = this.store.filters.taxonomy;
 
-		// Always update fields for loaded taxonomies (handles both single and batch)
 		if (taxonomy) {
 			const taxonomies = taxonomy.split(',').map(t => t.trim());
 			taxonomies.forEach(tax => this.updateFieldsForTaxonomy(tax));
@@ -1104,11 +1101,9 @@
 			this.showResults(true);
 			return;
 		}
-		this.setMessage(false);
 	}
 
 	showResults(isAutoComplete = false) {
-		this.setMessage(false);
 		const terms = this.store.getFiltered();
 		const filters = this.store.filters;
 		const isSearch = filters.search && filters.search.length > 0;
@@ -1118,7 +1113,10 @@
 			filters
 		});
 
-
+		if (!this.activeField && isAutoComplete) {
+			return;
+		}
+		this.setMessage(this.currentField(), false);
 		if (isAutoComplete) {
 			this.showAutocompleteTerms();
 		} else {
@@ -1138,15 +1136,13 @@
 			? `Failed to load ${field.plural}`
 			: 'Failed to load data';
 
-		this.setMessage(true, message, false);
+		this.setMessage(field,true, message, false);
 		console.error('Store fetch error:', error);
 	}
 	async batchFetchTaxonomies() {
 		if (this.batchFetch.size === 0) return;
-
 		const taxonomies = Array.from(this.batchFetch);
 		this.batchFetch.clear();
-
 		try {
 			await this.store.setFilters({
 				taxonomy: taxonomies.join(','),
@@ -1171,14 +1167,14 @@
 	/**************************************************
 	 LOADING
 	**************************************************/
-	setCreateButton(show = true) {
-		const field = this.currentField();
-		if (!field || !field.canCreate || !this.creator) return;
+	setCreateButton(field, show = true) {
+		if (!field.canCreate || !this.creator) return;
 
 		const conf = (this.container.open) ? this.ui : field.ui;
 		if (!conf.create?.button || !conf.create?.span) return;
 
 		const createButton = conf.create.button;
+		createButton.hidden = !show;
 		const buttonSpan = conf.create.span;
 		const input = (this.container.open) ? conf.search.input : conf.search;
 		if (!input) return;
@@ -1198,46 +1194,66 @@
 		if (!field) return;
 
 		window.debouncer.cancel(`${field.id}-search-results`);
+
 		let data = {
 			taxonomy: field.taxonomy,
 			parent: this.store.filters.parent??0
 		}
-		//If it's autocomplete or the selector's search input, we just need the name
+
 		if (!this.container.open || this.ui.search.input.value !== '') {
 			data.name = (this.container.open) ? this.ui.search.input.value : field.ui.search.value;
 		} else {
-			//Otherwise, we've created it from the details element
 			data.parent = this.creator.ui.parent.value??data.parent;
 			data.name = this.creator.ui.name.value??false;
 		}
+
 		if (data.parent !== undefined && data.name) {
-			this.setMessage(true, `Creating "${data.name}"...`);
-			this.setCreateButton(false);
+			this.setMessage(field,true, `Creating "${data.name}"...`);
+			this.setCreateButton(field,false);
+
 			if (this.container.open) {
 				window.removeChildren(this.ui.terms.list);
 			} else {
 				field.ui.search.disabled = true;
-				window.removeChildren(field.ui.dropdown.list);
 				if (field.ui.dropdown.wrapper) {
 					field.ui.dropdown.wrapper.hidden = false;
 				}
 			}
+
 			let term = await this.creator.handleTermCreation(data);
+
 			if (term) {
+				// Stop any typeLoop animation and show success message WITHOUT typeLoop
+				this.setMessage(field,true, `"${term.name}" created!`, false);
+
 				this.addSelected(term.id, field.id);
+				this.updateFieldValue(field.id);
+				// For autocomplete, show the newly created term in dropdown
+				if (!this.container.open && field.ui.dropdown.list) {
+					window.removeChildren(field.ui.dropdown.list);
+					const termElement = this.createAutocompleteTerm(term);
+					if (termElement) {
+						termElement.classList.add('newly-created');
+						field.ui.dropdown.list.append(termElement);
+					}
+				}
+				this.scheduleHideDropdown(field.id, 300);
+				this.setMessage(field,false);
+			} else {
+				// Creation failed - hide immediately
+				this.setMessage(field,false);
+				if (!this.container.open && field.ui.dropdown.wrapper) {
+					field.ui.dropdown.wrapper.hidden = true;
+				}
 			}
+
 			if (!this.container.open) {
 				field.ui.search.disabled = false;
 				field.ui.search.value = '';
 			}
-			this.scheduleHideDropdown(field.id);
-			this.setMessage(false);
 		}
 	}
-	setMessage(show = true, message = '', type = true) {
-		const field = this.currentField();
-		if (!field) return;
-
+	setMessage(field, show = true, message = '', type = true) {
 		const conf = this.container.open||field.isFilter ? this.ui : (field.isFilter ? null : field.ui);
 		if (!conf?.message?.message) return;
 

--
Gitblit v1.10.0