Jake Vanderwerf
2026-01-04 552d48a1424417da160c4952650ea6f4a3d7bafa
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,6 +42,7 @@
         this.resetParentOptions();
      }
      // Handle term creation submission
      if (window.targetCheck(e, '.submit-term')) {
         this.handleTermCreation(e).then(()=>{});
      }
@@ -46,6 +53,9 @@
      }
   }
   /**
    * Handle term creation from modal form
    */
   async handleTermCreation(e) {
      const taxonomy = this.selector.currentConfig?.taxonomy;
      if (!taxonomy) return;
@@ -55,20 +65,42 @@
      if (!termName) return;
      try {
         const submitButton = this.form.querySelector('button');
      try {
         if (submitButton) {
            submitButton.disabled = true;
         }
         const response = await this.createTerm(termName, parentId, taxonomy);
         if (response.success && response.term) {
            let term = response.term;
            await this.handleSuccessfulCreation(response.term, taxonomy, parentId);
            this.clearForm();
         }
      } catch (error) {
         console.error('Error creating term:', error);
         this.selector.handleError(error, 'handleTermCreation');
      } finally {
         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,
@@ -80,8 +112,10 @@
               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({
@@ -91,25 +125,11 @@
                  search: ''
               });
            }
            this.form.querySelector('input[name="term_name"]').value = '';
            const suggestionContainer = this.createNew.querySelector('.term-suggestions');
            if (suggestionContainer) {
               suggestionContainer.hidden = true;
            }
         }
      } catch (error) {
         console.error('Error creating term:', error);
         this.selector.error?.log(error, {
            component: 'TaxonomyCreator',
            action: 'handleTermCreation'
         });
      } finally {
         this.form.querySelector('button').disabled = false;
      }
   }
   /**
    * Handle autocomplete create button
    */
   async handleAutocompleteCreate(e) {
      const button = e.target.closest('.create-term');
      const fieldId = this.selector.getFieldId(button);
@@ -131,12 +151,29 @@
         const response = await this.createTerm(termName, 0, field.taxonomy);
         if (response.success && response.term) {
            const term = response.term;
            await this.handleAutocompleteSuccess(response.term, field, input);
         } else if (response.reason === 'exists' && response.term) {
            this.handleExistingTerm(response.term, field, input);
         }
      } catch (error) {
         console.error('Error creating term:', error);
         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 store's data map
      // Add to DataStore
            this.selector.store.data.set(term.id, {
               id: term.id,
               name: term.name,
@@ -145,25 +182,30 @@
               parent: 0,
               count: 0,
               hasChildren: false,
               slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
         slug: term.slug || term.name.toLowerCase().replace(/\s+/g, '-')
            });
            this.selector.addTermToDisplay(field.id, term.id, term.name, termPath);
      // 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 ALL cache for this taxonomy
            // This forces next search to hit the server
      // Clear cache for this taxonomy
            await this.selector.store.clearCache();
   }
         } else if (response.reason === 'exists' && response.term) {
            const term = response.term;
   /**
    * Handle selecting existing term from autocomplete
    */
   handleExistingTerm(term, field, input) {
            field.selectedTerms.add(parseInt(term.id));
            this.selector.addTermToDisplay(field.id, term.id, term.name, term.path || term.name);
      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 }));
@@ -171,22 +213,12 @@
            field.autocompleteDropdown.hidden = true;
            if (input) input.value = '';
         }
      } catch (error) {
         console.error('Error creating term:', error);
         this.selector.error?.log(error, {
            component: 'TaxonomyCreator',
            action: 'handleAutocompleteCreate'
         });
      } finally {
         button.innerHTML = originalHTML;
         button.disabled = false;
      }
   }
   /**
    * Initialize term creation form
    */
   initTermCreation() {
      if (!this.form) {
         return;
      }
      if (!this.form) return;
      this.form.addEventListener('change', (e) => {
         e.preventDefault();
@@ -194,6 +226,9 @@
      });
   }
   /**
    * Reset parent options in create form
    */
   resetParentOptions() {
      const taxonomy = this.selector.currentConfig?.taxonomy;
      if (!taxonomy) return;
@@ -222,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) {
@@ -243,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,
@@ -253,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 &&
@@ -320,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);
      });
@@ -350,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');
@@ -362,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