Merge branch 'main' into feature/milestone3

# Conflicts:
#	public/userarea/import_insert.php
#	public/userarea/mapping_template_xls_scheme2.php
#	public/userarea/process_import_xls2.php
This commit is contained in:
2026-04-02 12:29:17 +03:00
22 changed files with 2121 additions and 321 deletions
@@ -163,6 +163,12 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
width: 100% !important;
}
.xls-columns option.used-option {
background-color: #fff3cd;
color: #856404;
font-weight: 600;
}
/* Make Title column narrower + ellipsis */
#schemaFieldsTable td.title-col {
max-width: 320px;
@@ -749,30 +755,33 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
function updateXlsDropdowns() {
let usedColumns = Array.from(document.querySelectorAll('select.xls-columns'))
.filter(select => select.style.display === 'block' && select.value)
.map(select => select.value)
.map(select => select.value || select.dataset.currentXls || '')
.filter(Boolean)
.concat(usedColumnsFromDB);
let uniqueUsedColumns = [...new Set(usedColumns)];
document.querySelectorAll('select.xls-columns').forEach(select => {
let currentValue = select.value || select.dataset.currentXls || '';
let options = availableXlsColumns
.map((col, origIdx) => ({ col, origIdx }))
.filter(({ col }) => !usedColumns.includes(col) || col === currentValue)
.map(({ col, origIdx }) => {
const clean = col.replace(/[\r\n\t]+/g, ' ').trim();
const isEmpty = clean === '';
const colNum = origIdx + 1;
const val = isEmpty ? `__empty_${colNum}__` : clean;
const label = isEmpty ? `(empty column ${colNum})` : clean;
const isUsed = uniqueUsedColumns.includes(col) && col !== currentValue;
const label = isEmpty
? `(empty column ${colNum})`
: (isUsed ? `⚠ ${clean} (already used)` : clean);
const isSelected = (isEmpty ? val === currentValue : col === currentValue) ? 'selected' : '';
return `<option value="${val}" ${isSelected}>${label}</option>`;
return `<option value="${val}" class="${isUsed ? 'used-option' : ''}" ${isSelected}>${label}</option>`;
})
.join('');
select.innerHTML = '<option value="">Select XLS Column</option>' + options;
select.dataset.currentXls = currentValue;
if (currentValue && !options.includes(currentValue)) {
select.value = '';
}
});
}
@@ -1219,33 +1228,6 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
checkbox.checked = !prevChecked;
location.reload();
});
} else if (event.target.classList.contains('visible-parts-checkbox')) {
const checkbox = event.target;
const mappingId = checkbox.dataset.mappingId;
const value = checkbox.checked ? 1 : 0;
fetch('update_visible_parts.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: <?php echo $id; ?>,
mapping_id: mappingId,
value: value
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error updating is_visible_parts:", data.message);
checkbox.checked = !checkbox.checked;
}
})
.catch(error => {
console.error("❌ Fetch error:", error);
checkbox.checked = !checkbox.checked;
});
}
});
@@ -1285,7 +1267,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
mappedColumn = document.createElement('span');
mappedColumn.className = 'mapped-column';
mappedColumn.style.marginLeft = '5px';
tr.querySelector('td:nth-child(5)').appendChild(mappedColumn);
tr.querySelector('td:nth-child(6)').appendChild(mappedColumn);
}
if (!removeBtn) {
removeBtn = document.createElement('button');
@@ -1293,7 +1275,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
removeBtn.textContent = 'X';
removeBtn.style.marginLeft = '5px';
removeBtn.setAttribute('data-id', mappingId);
tr.querySelector('td:nth-child(5)').appendChild(removeBtn);
tr.querySelector('td:nth-child(6)').appendChild(removeBtn);
removeBtn.addEventListener('click', function(e) {
let tr = e.target.closest('tr');