| | |
| | | this.error = window.jvbError; |
| | | this.index = -1; |
| | | |
| | | // DataStore instances per taxonomy |
| | | this.stores = new Map(); |
| | | this.storeSubscriptions = new Map(); |
| | | this.store = new window.jvbStore({ |
| | | name: `taxonomies`, |
| | | storeName: `terms`, |
| | | keyPath: 'id', |
| | | indexes: [ |
| | | {name: 'taxonomy', keyPath: 'taxonomy'}, |
| | | {name: 'parent', keyPath: 'parent'}, |
| | | {name: 'slug', keyPath: 'slug', unique: true}, |
| | | {name: 'count', keyPath: 'count'}, |
| | | ], |
| | | endpoint: 'terms', |
| | | TTL: 7200000, //2 hours |
| | | filters: { |
| | | taxonomy: '', |
| | | page: 1, |
| | | search: '', |
| | | parent: 0 |
| | | } |
| | | }); |
| | | |
| | | // Central field management |
| | | this.fields = new Map(); |
| | |
| | | this.initModal(); |
| | | this.scanExistingFields(); |
| | | this.initGlobalListeners(); |
| | | } |
| | | |
| | | /** |
| | | * Get or create a DataStore for a taxonomy |
| | | */ |
| | | getOrCreateStore(taxonomy) { |
| | | if (!this.stores.has(taxonomy)) { |
| | | const store = new window.jvbStore({ |
| | | name: `tax_${taxonomy}`, |
| | | endpoint: 'terms', |
| | | TTL: 3600000, // 1 hour cache |
| | | filters: { |
| | | taxonomy: taxonomy, |
| | | page: 1, |
| | | search: '', |
| | | parent: 0 |
| | | } |
| | | }); |
| | | |
| | | // Subscribe to store events |
| | | const unsubscribe = store.subscribe((event, data) => { |
| | | this.handleStoreEvent(taxonomy, event, data); |
| | | }); |
| | | |
| | | this.stores.set(taxonomy, store); |
| | | this.storeSubscriptions.set(taxonomy, unsubscribe); |
| | | } |
| | | |
| | | return this.stores.get(taxonomy); |
| | | this.store.subscribe(this.handleStoreEvent.bind(this)); |
| | | } |
| | | |
| | | /** |
| | |
| | | this.fields.set(fieldId, config); |
| | | |
| | | // Ensure store exists for this taxonomy |
| | | this.getOrCreateStore(config.taxonomy); |
| | | this.store.setFilter('taxonomy', config.taxonomy); |
| | | |
| | | // Initialize display for any pre-selected values |
| | | if (config.selectedTerms.size > 0) { |
| | |
| | | const field = this.fields.get(fieldId); |
| | | if (!field || field.selectedTerms.size === 0) return; |
| | | |
| | | const store = this.getOrCreateStore(field.taxonomy); |
| | | const selectedIds = Array.from(field.selectedTerms); |
| | | |
| | | // Check store for cached terms first |
| | |
| | | const needsFetch = []; |
| | | |
| | | selectedIds.forEach(termId => { |
| | | const term = store.getItem(termId); |
| | | const term = this.store.getItem(termId); |
| | | if (term) { |
| | | cachedTerms.push(term); |
| | | } else { |
| | |
| | | // Fetch missing terms if needed |
| | | if (needsFetch.length > 0) { |
| | | try { |
| | | const response = await store.fetch('terms', { |
| | | |
| | | const response = await this.store.fetch({ |
| | | filters: { |
| | | taxonomy: field.taxonomy, |
| | | termIDs: needsFetch.join(',') |
| | |
| | | |
| | | if (response.terms) { |
| | | response.terms.forEach(term => { |
| | | store.setItem(term.id, term); |
| | | this.store.setItem(term.id, term); |
| | | this.addTermToDisplay(fieldId, term.id, term.name, term.path); |
| | | }); |
| | | } |
| | |
| | | this.currentPlural = jvbSettings.labels[this.currentConfig.taxonomy].plural; |
| | | |
| | | // Get or create store for this taxonomy |
| | | this.activeStore = this.getOrCreateStore(this.currentConfig.taxonomy); |
| | | this.store.setFilter('taxonomy', this.currentConfig.taxonomy); |
| | | |
| | | // Clear modal selection state |
| | | this.selectedTerms.clear(); |
| | | |
| | | // Copy field's current selections to modal state |
| | | if (this.currentConfig.selectedTerms) { |
| | | let termsToFetch = []; |
| | | this.currentConfig.selectedTerms.forEach(termId => { |
| | | const term = this.activeStore.getItem(termId); |
| | | const term = this.store.getItem(termId); |
| | | if (term) { |
| | | this.selectedTerms.set(termId, { |
| | | id: termId, |
| | |
| | | path: term.path |
| | | }); |
| | | } else { |
| | | // If not in store, create minimal entry |
| | | this.selectedTerms.set(termId, { |
| | | id: termId, |
| | | name: `Term ${termId}`, |
| | | path: `Term ${termId}` |
| | | }); |
| | | termsToFetch.push(termId); |
| | | } |
| | | }); |
| | | if (termsToFetch.length > 0) { |
| | | let terms = this.fetchSpecificTerms(termsToFetch); |
| | | terms.forEach(term => { |
| | | this.selectedTerms.set(term.id, { |
| | | id: term.id, |
| | | name: term.name, |
| | | path: term.path |
| | | }); |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | |
| | | fetchSpecificTerms(terms) { |
| | | return []; |
| | | } |
| | | |
| | | /** |
| | | * Handle clicks within modal |
| | |
| | | // Clear intervals and cleanup |
| | | this.observer?.disconnect(); |
| | | |
| | | // Unsubscribe from all stores |
| | | this.storeSubscriptions.forEach(unsubscribe => unsubscribe()); |
| | | |
| | | // Destroy all stores |
| | | this.stores.forEach(store => store.destroy()); |
| | | this.store.destroy(); |
| | | |
| | | // Clear all maps |
| | | this.fields.clear(); |
| | | this.stores.clear(); |
| | | this.storeSubscriptions.clear(); |
| | | this.selectedTerms.clear(); |
| | | } |
| | | } |