From dab8d9aebf257f7fb7325f9269086f1a7c9e2f1d Mon Sep 17 00:00:00 2001 From: solocla Date: Mon, 8 Jun 2026 08:47:17 +0200 Subject: [PATCH] fixed mapping for json --- .gitignore | 1 + .../userarea/mapping_template_xls_scheme2.php | 684 +++++++++++------- 2 files changed, 404 insertions(+), 281 deletions(-) diff --git a/.gitignore b/.gitignore index 9513359..0bb0088 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ yarn-error.log /public/userarea/class/curl_auth_debug.log /public/userarea/class/curl_request_debug.log /public/userarea/schema_dettagli_response.json +public/userarea/schemi_base_response.json # File XLSX temporanei importati /public/userarea/imported_trf/*.xlsx diff --git a/public/userarea/mapping_template_xls_scheme2.php b/public/userarea/mapping_template_xls_scheme2.php index 8dbd732..f36ca48 100644 --- a/public/userarea/mapping_template_xls_scheme2.php +++ b/public/userarea/mapping_template_xls_scheme2.php @@ -265,6 +265,47 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; font-family: Consolas, Monaco, monospace; font-size: 13px; } + + .select2-container--default .select2-results__option { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 520px; + } + + .select2-json-node { + font-weight: 600; + } + + .select2-json-value { + color: #6c757d; + font-size: 12px; + margin-left: 8px; + } + + .select2-json-row { + display: block; + margin: -6px; + padding: 6px; + } + + .select2-results__option:has(.select2-json-row.used-option) { + background-color: #fff3cd !important; + color: #856404 !important; + font-weight: 600; + } + + .select2-results__option:has(.select2-json-row.used-option).select2-results__option--highlighted { + background-color: #ffe69c !important; + color: #856404 !important; + } + + .select2-json-row.used-option .select2-json-node::after { + content: " (already used)"; + color: #856404; + font-size: 12px; + font-weight: 600; + } @@ -1018,6 +1059,42 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; return escapeHtmlText(str); } + let jsonNodeLabels = {}; + + function getLastJsonNodeName(path) { + return String(path || '') + .replace(/\[\]/g, '') + .split('.') + .pop(); + } + + function formatJsonSampleValue(value) { + if (value === null || value === undefined) return ''; + + let text = ''; + + if (Array.isArray(value)) { + text = '[Array]'; + } else if (typeof value === 'object') { + text = '{Object}'; + } else { + text = String(value); + } + + text = text + .replace(/[\r\n\t]+/g, ' ') + .replace(/\s+/g, ' ') + .trim(); + + if (text === '/' || text === '\\/') return ''; + + if (text.length > 38) { + text = text.substring(0, 38) + '...'; + } + + return text; + } + function flattenJsonNodes(obj, prefix = '') { let nodes = []; @@ -1036,6 +1113,14 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; if (value !== null && typeof value === 'object') { nodes = nodes.concat(flattenJsonNodes(value, path)); } else { + const sample = formatJsonSampleValue(value); + const shortName = getLastJsonNodeName(path); + + jsonNodeLabels[path] = { + shortName: shortName, + sample: sample + }; + nodes.push(path); } }); @@ -1061,18 +1146,128 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; if (!clean) return ''; const isUsed = uniqueUsedNodes.includes(clean) && clean !== currentValue; - const label = isUsed ? `⚠ ${clean} (already used)` : clean; + const info = jsonNodeLabels[clean] || {}; + const shortName = info.shortName || getLastJsonNodeName(clean); + const sample = info.sample || ''; + + let label = shortName; + if (sample) { + label += ` — ${sample}`; + } + + if (isUsed) { + label = `⚠ ${label} (already used)`; + } + const isSelected = clean === currentValue ? 'selected' : ''; - return ``; + return ` + + `; }) .join(''); select.innerHTML = '' + options; select.dataset.currentJson = currentValue; }); + + initSelect2ForJsonDropdowns(); } + function initSelect2ForJsonDropdowns() { + if (!(window.jQuery && $.fn.select2)) return; + + $('.json-nodes').filter(function() { + const tr = this.closest('tr'); + const mappingSelect = tr ? tr.querySelector('.mapping-select') : null; + return mappingSelect && mappingSelect.value === 'json'; + }).each(function() { + const $el = $(this); + if (this.style.display === 'none') { + return; + } + if ($el.hasClass('select2-hidden-accessible')) { + $el.select2('destroy'); + } + + $el.select2({ + width: '100%', + placeholder: 'Select JSON Node', + allowClear: true, + dropdownAutoWidth: false, + + templateResult: function(data) { + if (!data.id) return data.text; + + const option = data.element; + const shortName = option.getAttribute('data-short-name') || data.text; + const sample = option.getAttribute('data-sample') || ''; + const isUsed = option.getAttribute('data-used') === '1'; + + const $row = $(''); + if (isUsed) { + $row.addClass('used-option'); + } + $row.append(`${escapeHtmlText(shortName)}`); + + if (sample) { + $row.append(`${escapeHtmlText(sample)}`); + } + + return $row; + }, + + templateSelection: function(data) { + if (!data.id) return data.text; + + const option = data.element; + const shortName = option.getAttribute('data-short-name') || data.text; + const sample = option.getAttribute('data-sample') || ''; + + if (sample) { + return `${shortName} — ${sample}`; + } + + return shortName; + }, + + matcher: function(params, data) { + if ($.trim(params.term) === '') { + return data; + } + + const term = params.term.toLowerCase(); + const option = data.element; + + const fullPath = option?.getAttribute('data-full-path')?.toLowerCase() || ''; + const shortName = option?.getAttribute('data-short-name')?.toLowerCase() || ''; + const sample = option?.getAttribute('data-sample')?.toLowerCase() || ''; + + if ( + fullPath.includes(term) || + shortName.includes(term) || + sample.includes(term) + ) { + return data; + } + + return null; + } + }); + }); + } + + + function saveJsonNodes(sampleJson, nodes) { return fetch('update_api_json_nodes.php', { method: 'POST', @@ -1463,258 +1658,217 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; document.getElementById('updateSchemaButton').addEventListener('click', updateSchemaDetails); document.getElementById('schemaFieldsBody').addEventListener('change', function(event) { + if (!event.target.classList.contains('mapping-select')) return; - if (event.target.classList.contains('mapping-select')) { - let tr = event.target.closest('tr'); - let mappingId = event.target.getAttribute('data-id'); + const mappingSelect = event.target; + const tr = mappingSelect.closest('tr'); + const mappingId = mappingSelect.getAttribute('data-id'); - let xlsSelect = tr.querySelector('.xls-columns'); - let jsonSelect = tr.querySelector('.json-nodes'); - let manualInput = tr.querySelector('.manual-default'); - let autoSelect = tr.querySelector('.auto-value-select'); + const xlsSelect = tr.querySelector('.xls-columns'); + const jsonSelect = tr.querySelector('.json-nodes'); + const manualInput = tr.querySelector('.manual-default'); + const autoSelect = tr.querySelector('.auto-value-select'); - let mappedColumn = tr.querySelector('.mapped-column'); - let mappedJsonNode = tr.querySelector('.mapped-json-node'); + const mappedColumn = tr.querySelector('.mapped-column'); + const mappedJsonNode = tr.querySelector('.mapped-json-node'); - let removeBtn = tr.querySelector('.remove-xls'); - let removeJsonBtn = tr.querySelector('.remove-json'); + const removeBtn = tr.querySelector('.remove-xls'); + const removeJsonBtn = tr.querySelector('.remove-json'); - if (event.target.value === 'xls') { - if (xlsSelect) xlsSelect.style.display = 'block'; - if (jsonSelect) jsonSelect.style.display = 'none'; - if (autoSelect) autoSelect.style.display = 'none'; + function destroyJsonSelect2() { + if (jsonSelect && window.jQuery && $(jsonSelect).hasClass('select2-hidden-accessible')) { + $(jsonSelect).select2('destroy'); + } + } - if (manualInput) { - manualInput.style.display = 'none'; - manualInput.value = ''; - } + if (mappingSelect.value === 'xls') { + if (xlsSelect) xlsSelect.style.display = 'block'; - if (mappedColumn) mappedColumn.style.display = 'none'; - if (mappedJsonNode) mappedJsonNode.style.display = 'none'; - - if (removeBtn) removeBtn.style.display = xlsSelect && xlsSelect.value ? 'inline-block' : 'none'; - if (removeJsonBtn) removeJsonBtn.style.display = 'none'; - - } else if (event.target.value === 'json') { - if (xlsSelect) xlsSelect.style.display = 'none'; - if (jsonSelect) jsonSelect.style.display = 'block'; - if (autoSelect) autoSelect.style.display = 'none'; - - if (manualInput) { - manualInput.style.display = 'none'; - manualInput.value = ''; - } - - if (mappedColumn) mappedColumn.style.display = 'none'; - if (mappedJsonNode) mappedJsonNode.style.display = 'none'; - - if (removeBtn) removeBtn.style.display = 'none'; - if (removeJsonBtn) removeJsonBtn.style.display = jsonSelect && jsonSelect.value ? 'inline-block' : 'none'; - - } else if (event.target.value === 'manual') { - if (xlsSelect) xlsSelect.style.display = 'none'; - if (jsonSelect) jsonSelect.style.display = 'none'; - if (autoSelect) autoSelect.style.display = 'none'; - - if (manualInput) { - manualInput.style.display = 'block'; - } - - if (mappedColumn) mappedColumn.style.display = 'none'; - if (mappedJsonNode) mappedJsonNode.style.display = 'none'; - - if (removeBtn) removeBtn.style.display = 'none'; - if (removeJsonBtn) removeJsonBtn.style.display = 'none'; - - } else if (event.target.value === 'auto') { - if (xlsSelect) { - xlsSelect.style.display = 'none'; - xlsSelect.value = ''; - } - - if (jsonSelect) { - jsonSelect.style.display = 'none'; - jsonSelect.value = ''; - } - - if (manualInput) { - manualInput.style.display = 'none'; - manualInput.value = ''; - } - - if (mappedColumn) mappedColumn.style.display = 'none'; - if (mappedJsonNode) mappedJsonNode.style.display = 'none'; - - if (removeBtn) removeBtn.style.display = 'none'; - if (removeJsonBtn) removeJsonBtn.style.display = 'none'; - - if (autoSelect) autoSelect.style.display = 'block'; - - } else { - if (xlsSelect) xlsSelect.style.display = 'none'; - if (jsonSelect) jsonSelect.style.display = 'none'; - if (autoSelect) autoSelect.style.display = 'none'; - - if (manualInput) { - manualInput.style.display = 'none'; - manualInput.value = ''; - } - - if (mappedColumn) mappedColumn.style.display = 'none'; - if (mappedJsonNode) mappedJsonNode.style.display = 'none'; - - if (removeBtn) removeBtn.style.display = 'none'; - if (removeJsonBtn) removeJsonBtn.style.display = 'none'; + destroyJsonSelect2(); + if (jsonSelect) { + jsonSelect.style.display = 'none'; + jsonSelect.value = ''; } - saveMapping( - mappingId, - event.target.value, - manualInput ? manualInput.value : '', - xlsSelect ? xlsSelect.value : null, - autoSelect ? autoSelect.value : null, - jsonSelect ? jsonSelect.value : null - ); + if (autoSelect) autoSelect.style.display = 'none'; - if (sourceType === 'XLS') updateXlsDropdowns(); - if (sourceType === 'API') updateJsonDropdowns(); - - return; - } - - if (event.target.classList.contains('main-field-checkbox')) { - const checkbox = event.target; - const mappingId = checkbox.dataset.mappingId; - const value = checkbox.checked ? 1 : 0; - - // Count only the other Main fields already checked in this table - const otherCheckedMainFields = Array.from( - document.querySelectorAll('#schemaFieldsBody .main-field-checkbox') - ).filter(cb => cb !== checkbox && cb.checked); - - // If I am checking this one, I can have max 2 total: - // this checkbox + max 1 other already checked - if (checkbox.checked && otherCheckedMainFields.length >= 2) { - checkbox.checked = false; - alert('Puoi selezionare al massimo 2 campi Main.'); - return; + if (manualInput) { + manualInput.style.display = 'none'; + manualInput.value = ''; } - fetch('update_main_field.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - template_id: , - mapping_id: mappingId, - value: value - }) - }) - .then(response => response.json()) - .then(data => { - if (!data.success) { - console.error("❌ Error updating main_field:", data.message); + if (mappedColumn) mappedColumn.style.display = 'none'; + if (mappedJsonNode) mappedJsonNode.style.display = 'none'; - document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => { - cb.checked = cb.dataset.originalChecked === 'true'; - }); + if (removeBtn) removeBtn.style.display = xlsSelect && xlsSelect.value ? 'inline-block' : 'none'; + if (removeJsonBtn) removeJsonBtn.style.display = 'none'; - alert(data.message || 'Errore durante il salvataggio del campo Main.'); - return; - } - - document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => { - cb.dataset.originalChecked = cb.checked ? 'true' : 'false'; - }); - }) - .catch(error => { - console.error("❌ Fetch error:", error); - - document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => { - cb.checked = cb.dataset.originalChecked === 'true'; - }); - - alert('Errore di comunicazione durante il salvataggio del campo Main.'); - }); - - return; - } - if (event.target.classList.contains('visible-import-checkbox')) { - const checkbox = event.target; - const mappingId = checkbox.dataset.mappingId; - const value = checkbox.checked ? 1 : 0; - const prevChecked = checkbox.checked; - - fetch('update_visible_import.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - template_id: , - mapping_id: mappingId, - value: value - }) - }) - .then(r => r.json()) - .then(data => { - if (!data.success) { - console.error("❌ Error updating is_visible_import:", data.message); - checkbox.checked = !prevChecked; - alert(data.message || 'Errore durante il salvataggio del campo Import.'); - } - }) - .catch(error => { - console.error("❌ Fetch error:", error); - checkbox.checked = !prevChecked; - alert('Errore di comunicazione durante il salvataggio del campo Import.'); - }); - - return; - } - if (event.target.classList.contains('visible-parts-checkbox')) { - const checkbox = event.target; - const mappingId = checkbox.dataset.mappingId; - const value = checkbox.checked ? 1 : 0; - const prevChecked = checkbox.checked; - - if (value === 1) { - document.querySelectorAll('.visible-parts-checkbox').forEach(cb => { - if (cb !== checkbox) cb.checked = false; - }); + } else if (mappingSelect.value === 'json') { + if (xlsSelect) { + xlsSelect.style.display = 'none'; + xlsSelect.value = ''; } - fetch('update_visible_parts.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - template_id: , - mapping_id: mappingId, - value: value - }) - }) - .then(r => r.json()) - .then(data => { - if (!data.success) { - console.error("❌ Error updating is_visible_parts:", data.message); - checkbox.checked = !prevChecked; - location.reload(); - } - }) - .catch(error => { - console.error("❌ Fetch error:", error); - checkbox.checked = !prevChecked; - location.reload(); - }); + if (jsonSelect) { + jsonSelect.style.display = 'block'; + } - return; + if (autoSelect) autoSelect.style.display = 'none'; + + if (manualInput) { + manualInput.style.display = 'none'; + manualInput.value = ''; + } + + if (mappedColumn) mappedColumn.style.display = 'none'; + if (mappedJsonNode) mappedJsonNode.style.display = 'none'; + + if (removeBtn) removeBtn.style.display = 'none'; + if (removeJsonBtn) removeJsonBtn.style.display = jsonSelect && jsonSelect.value ? 'inline-block' : 'none'; + + updateJsonDropdowns(); + + } else if (mappingSelect.value === 'manual') { + if (xlsSelect) xlsSelect.style.display = 'none'; + + destroyJsonSelect2(); + if (jsonSelect) { + jsonSelect.style.display = 'none'; + jsonSelect.value = ''; + } + + if (autoSelect) autoSelect.style.display = 'none'; + + if (manualInput) { + manualInput.style.display = 'block'; + } + + if (mappedColumn) mappedColumn.style.display = 'none'; + if (mappedJsonNode) mappedJsonNode.style.display = 'none'; + + if (removeBtn) removeBtn.style.display = 'none'; + if (removeJsonBtn) removeJsonBtn.style.display = 'none'; + + } else if (mappingSelect.value === 'auto') { + if (xlsSelect) { + xlsSelect.style.display = 'none'; + xlsSelect.value = ''; + } + + destroyJsonSelect2(); + if (jsonSelect) { + jsonSelect.style.display = 'none'; + jsonSelect.value = ''; + } + + if (manualInput) { + manualInput.style.display = 'none'; + manualInput.value = ''; + } + + if (mappedColumn) mappedColumn.style.display = 'none'; + if (mappedJsonNode) mappedJsonNode.style.display = 'none'; + + if (removeBtn) removeBtn.style.display = 'none'; + if (removeJsonBtn) removeJsonBtn.style.display = 'none'; + + if (autoSelect) autoSelect.style.display = 'block'; + + } else { + if (xlsSelect) xlsSelect.style.display = 'none'; + + destroyJsonSelect2(); + if (jsonSelect) { + jsonSelect.style.display = 'none'; + jsonSelect.value = ''; + } + + if (autoSelect) autoSelect.style.display = 'none'; + + if (manualInput) { + manualInput.style.display = 'none'; + manualInput.value = ''; + } + + if (mappedColumn) mappedColumn.style.display = 'none'; + if (mappedJsonNode) mappedJsonNode.style.display = 'none'; + + if (removeBtn) removeBtn.style.display = 'none'; + if (removeJsonBtn) removeJsonBtn.style.display = 'none'; } + saveMapping( + mappingId, + mappingSelect.value, + manualInput ? manualInput.value : '', + xlsSelect ? xlsSelect.value : null, + autoSelect ? autoSelect.value : null, + jsonSelect ? jsonSelect.value : null + ); + + if (sourceType === 'XLS') updateXlsDropdowns(); }); + function saveJsonNodeSelection(jsonSelect) { + if (!jsonSelect) return; + + let tr = jsonSelect.closest('tr'); + let mappingId = jsonSelect.getAttribute('data-id'); + let manualInput = tr.querySelector('.manual-default'); + let mappedJsonNode = tr.querySelector('.mapped-json-node'); + let removeJsonBtn = tr.querySelector('.remove-json'); + let mappingSelect = tr.querySelector('.mapping-select'); + + if (mappingSelect && mappingSelect.value !== 'json') { + return; + } + + if (!mappedJsonNode) { + mappedJsonNode = document.createElement('span'); + mappedJsonNode.className = 'mapped-json-node'; + mappedJsonNode.style.marginLeft = '5px'; + tr.querySelector('td:nth-child(7)').appendChild(mappedJsonNode); + } + + if (!removeJsonBtn) { + removeJsonBtn = document.createElement('button'); + removeJsonBtn.className = 'btn btn-danger btn-sm remove-json'; + removeJsonBtn.textContent = 'X'; + removeJsonBtn.style.marginLeft = '5px'; + removeJsonBtn.setAttribute('data-id', mappingId); + tr.querySelector('td:nth-child(7)').appendChild(removeJsonBtn); + } + + mappedJsonNode.textContent = jsonSelect.value ? `(${jsonSelect.value})` : ''; + mappedJsonNode.style.display = jsonSelect.value ? 'inline' : 'none'; + removeJsonBtn.style.display = jsonSelect.value ? 'inline-block' : 'none'; + + console.log('[JSON NODE SAVE]', { + mappingId: mappingId, + value: jsonSelect.value + }); + + saveMapping( + mappingId, + 'json', + manualInput ? manualInput.value : '', + null, + null, + jsonSelect.value + ); + } + + document.getElementById('schemaFieldsBody').addEventListener('change', function(event) { + if (!event.target.classList.contains('json-nodes')) return; + saveJsonNodeSelection(event.target); + }); + + if (window.jQuery) { + $(document).on('select2:select select2:clear', 'select.json-nodes', function() { + saveJsonNodeSelection(this); + }); + } + // Save original Main checkbox state document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => { @@ -1797,51 +1951,6 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; } }); - document.getElementById('schemaFieldsBody').addEventListener('change', function(event) { - if (event.target.classList.contains('json-nodes')) { - let tr = event.target.closest('tr'); - let mappingId = event.target.getAttribute('data-id'); - let manualInput = tr.querySelector('.manual-default'); - let mappedJsonNode = tr.querySelector('.mapped-json-node'); - let removeJsonBtn = tr.querySelector('.remove-json'); - - if (!mappedJsonNode) { - mappedJsonNode = document.createElement('span'); - mappedJsonNode.className = 'mapped-json-node'; - mappedJsonNode.style.marginLeft = '5px'; - tr.querySelector('td:nth-child(7)').appendChild(mappedJsonNode); - } - - if (!removeJsonBtn) { - removeJsonBtn = document.createElement('button'); - removeJsonBtn.className = 'btn btn-danger btn-sm remove-json'; - removeJsonBtn.textContent = 'X'; - removeJsonBtn.style.marginLeft = '5px'; - removeJsonBtn.setAttribute('data-id', mappingId); - tr.querySelector('td:nth-child(7)').appendChild(removeJsonBtn); - } - - mappedJsonNode.textContent = event.target.value ? `(${event.target.value})` : ''; - mappedJsonNode.style.display = event.target.value ? 'inline' : 'none'; - removeJsonBtn.style.display = event.target.value ? 'inline-block' : 'none'; - - const mappingSelect = tr.querySelector('.mapping-select'); - if (mappingSelect && mappingSelect.value !== 'json') { - return; - } - - saveMapping( - mappingId, - 'json', - manualInput ? manualInput.value : '', - null, - null, - event.target.value - ); - - updateJsonDropdowns(); - } - }); document.getElementById('schemaFieldsBody').addEventListener('change', function(event) { if (event.target.classList.contains('manual-default') && event.target.tagName === 'SELECT') { @@ -1968,9 +2077,11 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; updateXlsDropdowns(); } - if (data.success && mappingType === 'json' && jsonNode) { - usedJsonNodesFromDB = usedJsonNodesFromDB.filter(node => node !== jsonNode); - usedJsonNodesFromDB.push(jsonNode); + if (data.success && mappingType === 'json') { + usedJsonNodesFromDB = Array.from(document.querySelectorAll('select.json-nodes')) + .map(select => select.value || select.dataset.currentJson || '') + .filter(Boolean); + updateJsonDropdowns(); } }) @@ -2205,6 +2316,17 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; } if (sourceType === 'API' && availableJsonNodes.length) { + const rawJson = document.getElementById('apiJsonExample')?.value || ''; + + if (rawJson.trim()) { + try { + jsonNodeLabels = {}; + flattenJsonNodes(JSON.parse(rawJson)); + } catch (e) { + console.warn('Cannot rebuild JSON labels from sample JSON', e); + } + } + updateJsonDropdowns(); }