change clienti to datadb and fixed column pages

This commit is contained in:
2025-10-09 15:30:44 +02:00
parent a9827e4e81
commit 68c867a3f4
6 changed files with 435 additions and 82 deletions
+347 -57
View File
@@ -40,6 +40,12 @@ foreach ($allMappings as $mapping) {
}
}
// Recupera l'idclient di default dal template (se presente)
$template_stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
$template_stmt->execute([$template_id]);
$template = $template_stmt->fetch(PDO::FETCH_ASSOC);
$default_idclient = $template['idclient'] ?? null;
$insertedIds = $_POST['inserted_ids'] ?? $_SESSION['inserted_ids'];
// Recupera i dati appena inseriti con i nomi degli utenti
@@ -410,6 +416,46 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
.flatpickr-input {
cursor: pointer;
}
.client-input {
width: 100%;
box-sizing: border-box;
border: 1px solid #ced4da;
border-radius: 4px;
padding: 5px;
font-size: 14px;
color: #333;
}
.client-input:focus {
outline: none;
border-color: #80bdff;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
}
.select2-container {
width: 100% !important;
}
.select2-dropdown-smaller {
font-size: 14px;
}
.select2-container--default .select2-selection--single {
height: 31px;
border: 1px solid #ced4da;
border-radius: 4px;
padding: 5px;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 20px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 31px;
}
</style>
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head>
@@ -471,6 +517,13 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
?>
</div>
<?php endif; ?>
<div class="grid-cell" style="flex: 0 0 300px;">
<select class="custom-field dropdown-select client-select searchable-client" data-column="idclient" id="clientSelect" name="idclient">
<option value="">Select a client...</option>
</select>
<button type="button" class="propagate-btn" data-column="idclient"><i class="fas fa-arrow-down"></i></button>
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
</div>
<div class="grid-cell" style="flex: 0 0 150px;"></div>
<?php
$autoIndex = 0;
@@ -535,10 +588,12 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<div class="resizer"></div>
</div>
<?php endif; ?>
<div class="grid-header" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px; position: relative;">Status<div class="resizer"></div>
<div class="grid-header" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 300px; position: relative;">Client<div class="resizer"></div>
</div>
<div class="grid-header" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 150px; position: relative;">Status<div class="resizer"></div>
</div>
<?php
$headerIndex = $mainFieldMapping ? 3 : 2;
$headerIndex = $mainFieldMapping ? 4 : 3;
foreach ($allMappings as $mapping) {
if (!$mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . htmlspecialchars($mapping['field_label']) . "<div class='resizer'></div></div>";
@@ -604,14 +659,19 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<?php endif; ?>
</div>
<?php endif; ?>
<div class="grid-cell editable-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px;">
<div class="grid-cell editable-cell" data-col="idclient" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 300px;">
<select name="rows[<?= $index ?>][idclient]" class="cell-input dropdown-select client-select searchable-client" data-current-value="<?= htmlspecialchars($row['idclient'] ?? $default_idclient) ?>">
<option value="">Select a client...</option>
</select>
</div>
<div class="grid-cell editable-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 150px;">
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
</span>
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
</div>
<?php
$cellIndex = $mainFieldMapping ? 3 : 2;
$cellIndex = $mainFieldMapping ? 4 : 3;
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
$autoIndex = 0;
foreach ($allMappings as $mapping) {
@@ -840,11 +900,16 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if (matches) {
const mappingId = matches[1];
formData.append(`details${mappingId}field_value`, input.value);
if (input.tagName === 'SELECT' && input.classList.contains('dropdown-select')) {
if (input.tagName === 'SELECT') {
input.setAttribute('data-selected-value', input.value);
}
}
});
const idclientSelect = row.querySelector(`select[name="rows[${rowIndex}][idclient]"]`);
if (idclientSelect) {
formData.append('idclient', idclientSelect.value);
}
formData.append('iddatadb', iddatadb);
fetch('save_edited_row.php', {
@@ -912,6 +977,11 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
}
}
});
const idclientSelect = row.querySelector(`select[name="rows[${rowIndex}][idclient]"]`);
if (idclientSelect) {
formData.append('idclient', idclientSelect.value);
}
formData.append('iddatadb', iddatadb);
try {
@@ -984,6 +1054,90 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<script>
document.addEventListener("DOMContentLoaded", function() {
const inputs = document.querySelectorAll('.cell-input');
let clientData = []; // Dichiarazione di clientData qui
// Funzione per caricare i client
async function loadClients(retryCount = 0, maxRetries = 3) {
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
try {
clientLoadingStatus.style.display = 'inline';
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
const response = await fetch("get_clienti.php", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (!response.ok) {
if (response.status === 500 && data.error.includes('Cannot persist the object') && retryCount < maxRetries) {
console.log(`Tentativo ${retryCount + 1}/${maxRetries}: Riprovo a caricare i clienti...`);
await new Promise(resolve => setTimeout(resolve, 1000));
return loadClients(retryCount + 1, maxRetries);
}
throw new Error(data.error || `Errore HTTP: ${response.status}`);
}
clientData = data.value || [];
const select = document.getElementById("clientSelect");
select.innerHTML = '<option value="">Select a client...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
option.selected = true;
}
select.add(option);
});
populateClientDropdowns();
clientLoadingStatus.textContent = "Clienti caricati.";
} catch (error) {
clientLoadingStatus.textContent = "Errore nel caricamento.";
console.error("Errore nel caricamento dei client:", error);
Swal.fire({
title: "Errore!",
text: "Impossibile caricare i clienti: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
}
}
// Funzione per popolare i dropdown dei client
function populateClientDropdowns() {
const clientDropdowns = document.querySelectorAll('select[name^="rows"][name$="[idclient]"]');
clientDropdowns.forEach(dropdown => {
const currentValue = dropdown.getAttribute('data-current-value') || '';
dropdown.innerHTML = '<option value="">Select a client...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
if (String(id) === String(currentValue)) {
option.selected = true;
}
dropdown.add(option);
});
// Ripristina il valore corrente
if (currentValue) {
dropdown.value = currentValue;
const event = new Event('change', {
bubbles: true
});
dropdown.dispatchEvent(event);
}
});
}
// Carica i client all'avvio
loadClients();
// Gestione degli input
inputs.forEach(input => {
input.addEventListener('focus', function() {
this.closest('.grid-cell').classList.add('expanded');
@@ -993,42 +1147,60 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
});
});
// Gestione della propagazione
const propagateButtons = document.querySelectorAll('.propagate-btn');
propagateButtons.forEach(button => {
button.addEventListener('click', function() {
button.addEventListener('click', async function() {
const column = this.getAttribute('data-column');
const input = this.previousElementSibling;
const value = input.value;
const value = input.tagName === 'SELECT' ? input.value : input.value;
console.log('Propagate clicked for column:', column, 'with value:', value); // Debug
// Assicurati che i dropdown dei client siano popolati
if (column === 'idclient' && clientData.length === 0) {
await loadClients(); // Carica i client se non ancora caricati
}
const gridTopCells = document.querySelector('.grid-top').querySelectorAll('.grid-cell');
const targetTopIndex = Array.from(gridTopCells).findIndex(cell =>
cell.querySelector('.propagate-btn[data-column="' + column + '"]')
);
console.log('Target index found:', targetTopIndex); // Debug
if (targetTopIndex !== -1) {
const rows = document.querySelectorAll('.grid-row');
rows.forEach(row => {
const cells = row.querySelectorAll('.grid-cell');
if (cells.length > targetTopIndex) {
const targetInput = cells[targetTopIndex].querySelector('input, select');
const targetInput = cells[targetTopIndex].querySelector('select, input');
if (targetInput) {
targetInput.value = value;
console.log('Setting value on target input:', targetInput, 'with value:', value); // Debug
if (targetInput.tagName === 'SELECT') {
targetInput.setAttribute('data-selected-value', value);
const event = new Event('change');
targetInput.value = value;
const event = new Event('change', {
bubbles: true
});
targetInput.dispatchEvent(event);
} else if (targetInput.classList.contains('date-picker')) {
// Update Flatpickr instance
const flatpickrInstance = targetInput._flatpickr;
if (flatpickrInstance && value) {
flatpickrInstance.setDate(value, true);
}
const event = new Event('change');
const event = new Event('change', {
bubbles: true
});
targetInput.dispatchEvent(event);
} else {
const event = new Event('change');
targetInput.value = value;
const event = new Event('change', {
bubbles: true
});
targetInput.dispatchEvent(event);
}
} else {
console.warn('No target input found in cell'); // Debug
}
}
});
@@ -1036,6 +1208,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
});
});
// Gestione del ridimensionamento delle colonne
const resizers = document.querySelectorAll('.resizer');
let currentResizer = null;
let startX = 0;
@@ -1078,7 +1251,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
const dropdownData = {};
async function populateDropdowns() {
const dropdowns = document.querySelectorAll('.dropdown-select');
const dropdowns = document.querySelectorAll('.dropdown-select:not(.client-select)');
if (dropdowns.length === 0) {
return;
}
@@ -1102,13 +1275,15 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
dropdownData[fieldId] = data[fieldId] || [];
}
}
} catch (error) {}
} catch (error) {
console.error('Errore nel caricamento dei valori per dropdown:', error);
}
}
dropdowns.forEach(dropdown => {
const fieldId = dropdown.getAttribute('data-field-id');
const selectedValue = dropdown.getAttribute('data-selected-value') || '';
const currentValue = dropdown.value;
const currentValue = dropdown.value || '';
if (!fieldId || !dropdownData[fieldId]) {
dropdown.innerHTML = '<option value="">Errore nel caricamento</option>';
@@ -1128,63 +1303,178 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
dropdown.appendChild(option);
});
if ((currentValue || selectedValue) && dropdown.value !== (currentValue || selectedValue)) {
dropdown.value = '';
// Ripristina il valore corrente
if (currentValue || selectedValue) {
dropdown.value = currentValue || selectedValue;
const event = new Event('change', {
bubbles: true
});
dropdown.dispatchEvent(event);
}
});
}
populateDropdowns();
});
</script>
<script>
$(document).on('click', '.add-part-btn', function() {
const rowIndex = $(this).data('row');
const row = $(this).closest('.grid-row');
const iddatadb = row.data('id');
const input = row.find(`input[name="rows[${rowIndex}][tested_component]"]`);
const description = input.val().trim();
if (!description) {
alert('Inserisci un valore per Tested Component');
return;
document.addEventListener("DOMContentLoaded", function() {
let clientData = [];
async function loadClients(retryCount = 0, maxRetries = 3) {
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
try {
clientLoadingStatus.style.display = 'inline';
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
const response = await fetch("get_clienti.php", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (!response.ok) {
if (response.status === 500 && data.error.includes('Cannot persist the object') && retryCount < maxRetries) {
console.log(`Tentativo ${retryCount + 1}/${maxRetries}: Riprovo a caricare i clienti...`);
await new Promise(resolve => setTimeout(resolve, 1000));
return loadClients(retryCount + 1, maxRetries);
}
throw new Error(data.error || `Errore HTTP: ${response.status}`);
}
clientData = data.value || [];
const select = document.getElementById("clientSelect");
select.innerHTML = '<option value="">Select a client...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
option.selected = true;
}
select.add(option);
});
populateClientDropdowns();
clientLoadingStatus.textContent = "Clienti caricati.";
} catch (error) {
clientLoadingStatus.textContent = "Errore nel caricamento.";
Swal.fire({
title: "Errore!",
text: "Impossibile caricare i clienti: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
}
}
const data = {
iddatadb: iddatadb,
parts: [{
part_number: '1', // Imposta part_number a '1'
part_description: description,
mix: 'N'
}]
};
$.ajax({
url: 'save_parts.php',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(data),
success: function(response) {
if (response.success) {
alert('Parte aggiunta con successo!');
input.val(''); // Pulisci l'input dopo l'aggiunta
// Opzionale: aggiorna la tabella delle parti se il modal è aperto
const partsModal = $('#partsModal');
if (partsModal.hasClass('show')) {
loadParts(iddatadb);
function populateClientDropdowns() {
const clientDropdowns = document.querySelectorAll('select[name^="rows"][name$="[idclient]"]');
clientDropdowns.forEach(dropdown => {
const currentValue = dropdown.getAttribute('data-current-value') || '';
dropdown.innerHTML = '<option value="">Select a client...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
if (String(id) === String(currentValue)) {
option.selected = true;
}
} else {
alert('Errore: ' + response.message);
dropdown.add(option);
});
// Ripristina il valore corrente
if (currentValue) {
dropdown.value = currentValue;
const event = new Event('change', {
bubbles: true
});
dropdown.dispatchEvent(event);
}
},
error: function() {
alert('Errore durante la richiesta AJAX');
});
}
loadClients();
document.getElementById('clientSelect').addEventListener('change', function() {
const gridCell = this.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
});
document.addEventListener('change', function(e) {
if (e.target.matches('select[name^="rows"][name$="[idclient]"]')) {
const gridCell = e.target.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Gestione del cambio valore per il dropdown principale dei client
document.getElementById('clientSelect').addEventListener('change', function() {
const gridCell = this.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
});
// Gestione del cambio valore per i dropdown dei client nelle righe
document.addEventListener('change', function(e) {
if (e.target.matches('select[name^="rows"][name$="[idclient]"]')) {
const gridCell = e.target.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Initialize Select2 for searchable client dropdowns
$('.searchable-client').select2({
placeholder: "Select a client...",
allowClear: true,
width: '100%',
dropdownCssClass: 'select2-dropdown-smaller',
minimumInputLength: 1
});
// Ensure Select2 dropdowns trigger change events for unsaved changes tracking
$('.searchable-client').on('select2:select select2:clear', function(e) {
const gridCell = this.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
});
// Update propagate functionality for client dropdown
$('.propagate-btn[data-column="idclient"]').on('click', async function() {
const value = $('#clientSelect').val();
const rows = document.querySelectorAll('.grid-row');
rows.forEach(row => {
const clientSelect = row.querySelector('select[name$="[idclient]"]');
if (clientSelect) {
$(clientSelect).val(value).trigger('change.select2');
const event = new Event('change', {
bubbles: true
});
clientSelect.closest('.grid-cell').dispatchEvent(event);
}
});
});
$(document).on('click', '.parts-btn', function() {
const iddatadb = $(this).data('iddatadb') || null;
const idquotations = $(this).data('idquotations') || null;