jQuery(function($){ $('a[href="admin.php?page=formipay-products"]').addClass('current').closest('li').addClass('current'); function autoset_variation_name() { var repeater_single = $('.product_variation_attributes.repeater [parent_repeater="parent"] > .wpcfto-field-content > .wpcfto-repeater-single'); $.each(repeater_single, function(key, parent){ var repeater_child = $(parent).find('[field_native_name="product_variation_attributes"]'); var attribute_name = repeater_child.find(`[name="product_variation_attributes_${key}_attribute_name"]`).val(); var attribute_type = repeater_child.find(`[name="product_variation_attributes_${key}_attribute_type"]`).val(); var repeater_child_single = repeater_child.find('.wpcfto-repeater-single'); $.each(repeater_child_single, function(index, child){ var label_field = $(`input[name="product_variation_attributes_${key}_attribute_variations_${index}_variation_label"]`); var name_field = $(`input[name="product_variation_attributes_${key}_attribute_variations_${index}_variation_name"]`); var color_field = $(`input[name="product_variation_attributes_${key}_attribute_variations_${index}_variation_color"]`); var color_field_row = color_field.closest('.wpcfto-repeater-field'); if(attribute_type == 'color'){ color_field_row.show(); }else{ color_field_row.hide(); } }); }); } $(document).on('change blur', '[field_native_name_inner="variation_label"] input', function(){ autoset_variation_name(); }); $(document).on('click', '.stm_metaboxes_grid .stm_metaboxes_grid__inner .wpcfto-repeater .addArea', function() { autoset_variation_name(); }); var onMetaboxLoaded = setInterval(() => { var repeater_single = $('.product_variation_attributes.repeater [parent_repeater="parent"] > .wpcfto-field-content > .wpcfto-repeater-single'); if(repeater_single.length > 0){ autoset_variation_name(); clearInterval(onMetaboxLoaded); } }, 250); var waitForTable = setInterval(() => { if ($('#product-variables-table').length > 0) { clearInterval(waitForTable); new Vue({ el: '#product-variables-table', data() { return { tableRows: [], deletedKeys: [], isPhysical: window.product_details?.product_is_physical || false, jsonValue: '[]', attributeRepeaterWatcher: null, _debounceTimer: null, productType: window.product_details?.product_type || 'digital', // default, will update on mount productCurrencyRaw: window.product_details?.product_currency || 'USD:::United States dollar:::$', currencyDecimalDigits: parseInt(window.product_details?.product_currency_decimal_digits) || 2, currencyDecimalSymbol: window.product_details?.product_currency_decimal_symbol || '.', currencyThousandSeparator: window.product_details?.product_currency_thousand_separator || ',', }; }, async mounted() { await this.loadProductVariables(); this.setupAttributeRepeaterSync(); // Listen for changes on product_type radios to update reactive data const radios = document.querySelectorAll('input[name="product_type"]'); radios.forEach(radio => { radio.addEventListener('change', e => { this.productType = e.target.value; this.isPhysical = e.target.value == 'physical'; this.rebuildTable(); // Rebuild the table on type change }); }); // Listen for changes on product_currency select const currencySelect = document.querySelector('select[name="product_currency"]'); if (currencySelect) { currencySelect.addEventListener('change', (e) => { this.productCurrencyRaw = e.target.value; }); } // Initialize currency values this.productCurrencyRaw = window.product_details?.product_currency || 'USD:::United States dollar:::$'; }, beforeDestroy() { if (this.attributeRepeaterWatcher) { this.attributeRepeaterWatcher.disconnect(); } }, methods: { async loadProductVariables() { const postId = window.formipayProductId || $('input[name="post_ID"]').val(); if (!postId) { await this.buildFromAttributes(); return; } try { const res = await $.ajax({ url: ajaxurl, method: 'POST', data: { action: 'get_product_variables', post_id: postId } }); if (res.success && Array.isArray(res.data) && res.data.length) { this.tableRows = res.data; this.deletedKeys = []; this.updateJson(); } else { await this.buildFromAttributes(); } } catch { await this.buildFromAttributes(); } }, async buildFromAttributes() { try { const attributes = await this.getAttributeRepeaterData(); if (!attributes.length) { this.tableRows = []; this.updateJson(); return; } const combinations = this.getAllCombinations(attributes); const filtered = combinations.filter(c => !this.deletedKeys.includes(c.key)); const newRows = filtered.map(c => { const existing = this.tableRows.find(r => r.key === c.key); return existing ? Object.assign(existing, { name: c.label }) : { key: c.key, name: c.label, stock: '', price: 0, sale: 0, weight: 0, active: true, }; }); this.tableRows = newRows; this.updateJson(); } catch (e) { this.tableRows = []; this.updateJson(); } }, getAttributeRepeaterData() { return new Promise((resolve, reject) => { let attempts = 0; const maxAttempts = 100; const interval = setInterval(() => { const el = $('#variation-product_variation_attributes'); if (el.length && el.val()) { try { const data = JSON.parse(el.val()); clearInterval(interval); resolve(data); } catch { clearInterval(interval); reject(new Error('Invalid JSON in attribute repeater')); } } else if (++attempts >= maxAttempts) { clearInterval(interval); reject(new Error('Attribute repeater data not found')); } }, 50); }); }, getAllCombinations(attributes) { if (!attributes.length) return []; const attrVars = attributes.map(attr => (attr.attribute_variations || []).map(v => ({ label: v.variation_label })) ).filter(arr => arr.length > 0); if (!attrVars.length) return []; const combine = (arrs) => arrs.reduce((a, b) => a.flatMap(d => b.map(e => [].concat(d, e)))); const combos = combine(attrVars); return combos.map(combo => { const labels = Array.isArray(combo) ? combo.map(c => c.label) : [combo.label]; return { key: labels.join('||'), label: labels.join(' - ') }; }); }, setupAttributeRepeaterSync() { const target = document.getElementById('variation-product_variation_attributes'); if (!target) return; const observer = new MutationObserver(mutations => { for (const m of mutations) { if (m.type === 'attributes' && m.attributeName === 'value') { clearTimeout(this._debounceTimer); this._debounceTimer = setTimeout(() => this.buildFromAttributes(), 200); } } }); observer.observe(target, { attributes: true, attributeFilter: ['value'] }); this.attributeRepeaterWatcher = observer; }, updateJson() { this.jsonValue = JSON.stringify(this.tableRows); }, rebuildTable() { this.loadProductVariables(); } }, watch: { tableRows: { handler() { this.updateJson(); }, deep: true } } }); } }, 250); });