/** * TaxonomyCreator - Handles term creation for TaxonomySelector */ class TaxonomyCreator { constructor(selector) { this.selector = selector; this.queue = window.jvbQueue; this.initElements(); this.initListeners(); } initElements() { this.selectors = { details: 'details.create-term', parent: '#select_parent', summary: '.create-term summary', suggestion: '.term-suggestions', name: '#term_name', button: '.submit-term', form: 'form.create-term', label: { name: '[for="term_name"]', parent: '[for="select_parent"]' }, loading: '.loading-message.create-term' }; this.ui = window.uiFromSelectors(this.selectors, this.selector.container); } handleOpen(field) { this.field = field; if (this.ui.details) { this.ui.details.hidden = !field.canCreate; if (this.ui.summary) { this.ui.summary.textContent = `Add new ${field.singular}`; } if (this.ui.label.name) { this.ui.label.name.textContent = `Name this ${field.singular}`; } if (this.ui.label.parent) { this.ui.label.parent.textContent = `Nest it under`; } if (this.ui.button) { this.ui.button.textContent = `Add ${field.singular}`; } } } /** * Initialize event listeners */ initListeners() { this.clickHandler = this.handleClick.bind(this); document.addEventListener('click', this.clickHandler); if (this.ui.form) { this.ui.form.addEventListener('change', (e) => { console.log('Creator form change, prevents default'); e.preventDefault(); e.stopPropagation(); }); } } /** * Handle click events */ handleClick(e) { // Handle opening create term form if (window.targetCheck(e, this.selectors.summary)) { if (this.ui.details.open) { this.ui.name?.focus(); } this.resetParentOptions(); return; } } /** * Handle term creation from modal form */ async handleTermCreation(data) { if (!data.name || data.name.length < 2) return false; try { const response = await this.createTerm(data); if (!response.success) { // Term already exists - still add it if (response.term && response.term.id) { this.selector.setMessage(true, `Using existing "${response.term.name}"`); return response.term; } // Other failure this.selector.setMessage(true, response.message || 'Creation failed', false); return false; } if (response.term?.pending) { // Term requires approval this.selector.setMessage( true, `"${data.name}" submitted for approval`, false ); // Don't add to selection since it's pending return false; } if (response.success && response.term) { await this.handleSuccessfulCreation(response.term, data); this.clearForm(); } return response.term; } catch (error) { console.error('Error creating term:', error); return false; } } /** * Handle successful term creation */ async handleSuccessfulCreation(term, data) { // Close create form this.ui.details.open = false; // Clear cache to ensure fresh data this.selector.store.clearCache(); await this.selector.store.fetch(); } /** * Reset parent options in create form */ resetParentOptions() { const field = this.selector.currentField(); if (!field) return; const taxonomy = field.taxonomy; if (!taxonomy) return; if (!this.ui.parent) return; let defaultOption = this.ui.parent.querySelector('option'); if (!defaultOption) return; // Clear existing options window.removeChildren(this.ui.parent); this.ui.parent.append(defaultOption.cloneNode(true)); // Get current parent from store filters const currentParent = this.selector.store.filters.parent || 0; // If we're in a sub-category, add the current parent as an option if (currentParent !== 0) { const parentTerm = this.selector.store.get(currentParent); if (parentTerm) { let parentOption = defaultOption.cloneNode(true); parentOption.value = parentTerm.id; parentOption.textContent = parentTerm.name; this.ui.parent.append(parentOption); } } // Add all terms currently visible in the taxonomy const visibleTerms = []; this.selector.store.getFiltered().forEach(term => { if (term.taxonomy === taxonomy && term.parent === currentParent) { visibleTerms.push(term); } }); // Sort by name visibleTerms.sort((a, b) => a.name.localeCompare(b.name)); // Add to select visibleTerms.forEach(term => { let option = defaultOption.cloneNode(true); option.id = `select-parent-${term.id}`; option.value = term.id; option.textContent = ' — ' + term.name; this.ui.parent.append(option); }); } /** * Create a new term */ async createTerm(data) { if (!data.name || data.parent === undefined || !data.taxonomy) return; try { const response = await fetch(`${jvbSettings.api}terms`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': window.auth.getNonce() }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return await response.json(); } catch (error) { console.error('Error creating term:', error); throw error; } } /** * Clear the creation form */ clearForm() { if (this.ui.name) { this.ui.name.value = ''; } if (this.selector.ui.search.input){ this.selector.ui.search.input.value = ''; } } /** * Clean up when destroyed */ destroy() { // Remove event listeners if (this.clickHandler) { document.removeEventListener('click', this.clickHandler); } // Clear any pending operations if (this.ui.loading) { this.ui.loading.hidden = true; } } } window.jvbTaxCreator = TaxonomyCreator;