feat(import_edit2): enhance grid UI with sticky columns and textarea conversion
- Fix grid-top alignment and datepicker positioning - Extend column resizer to grid-top cells - Add sticky columns for Actions and Sample Description - Implement input-to-textarea conversion on focus - Increase Select2 dropdown height - Improve grid-container CSS for sticky support
This commit is contained in:
parent
f60dc64b2d
commit
d8eddb3aa5
@ -156,7 +156,8 @@ function fixedDefaultValue(array $f): string
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ced4da;
|
||||
@ -166,10 +167,36 @@ function fixedDefaultValue(array $f): string
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
select,
|
||||
textarea {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 60px;
|
||||
border: 1px solid #ced4da !important;
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
textarea:active,
|
||||
textarea:hover {
|
||||
border: 1px solid #ced4da !important;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
textarea.auto-input {
|
||||
background-color: #d4edda;
|
||||
}
|
||||
|
||||
textarea.manual-input {
|
||||
background-color: #fff3cd;
|
||||
}
|
||||
|
||||
textarea.required-input {
|
||||
background-color: #f8d7da;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
@ -197,11 +224,12 @@ function fixedDefaultValue(array $f): string
|
||||
|
||||
.grid-container {
|
||||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid-row {
|
||||
@ -249,6 +277,55 @@ function fixedDefaultValue(array $f): string
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* Sticky columns - first column (Actions) */
|
||||
.grid-header.button-header,
|
||||
.grid-cell.button-cell {
|
||||
position: sticky !important;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
background: white;
|
||||
overflow: visible;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.grid-header.button-header {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.grid-row:nth-child(even) .grid-cell.button-cell {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.grid-row:hover .grid-cell.button-cell {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.grid-row .grid-header:nth-child(2),
|
||||
.grid-row .grid-cell:nth-child(2) {
|
||||
position: sticky !important;
|
||||
left: 210px;
|
||||
z-index: 8;
|
||||
background: white;
|
||||
overflow: visible;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.grid-row .grid-header:nth-child(2) {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.grid-row:nth-child(even) .grid-cell:nth-child(2) {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.grid-row:hover .grid-cell:nth-child(2) {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.grid-row {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid-cell.expanded {
|
||||
max-width: 500px !important;
|
||||
white-space: normal !important;
|
||||
@ -276,6 +353,8 @@ function fixedDefaultValue(array $f): string
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 10px 0;
|
||||
min-height: 0;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.grid-top .grid-cell {
|
||||
@ -285,6 +364,7 @@ function fixedDefaultValue(array $f): string
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.grid-top .save-all-cell {
|
||||
@ -292,6 +372,15 @@ function fixedDefaultValue(array $f): string
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.grid-top .grid-cell input,
|
||||
.grid-top .grid-cell select,
|
||||
.grid-top .grid-cell .date-picker,
|
||||
.grid-top .grid-cell .fixed-top {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.propagate-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
@ -489,6 +578,10 @@ function fixedDefaultValue(array $f): string
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.select2-results__options {
|
||||
max-height: 400px !important;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single {
|
||||
height: 31px;
|
||||
border: 1px solid #ced4da;
|
||||
@ -552,8 +645,12 @@ function fixedDefaultValue(array $f): string
|
||||
<div class="grid-cell save-all-cell">
|
||||
<button type="button" class="save-all-btn" title="Save All Rows"><i class="fas fa-save"></i> Save All</button>
|
||||
</div>
|
||||
<?php if ($mainFieldMapping): ?>
|
||||
<div class="grid-cell" style="flex: 0 0 150px;">
|
||||
<?php
|
||||
|
||||
$topColIndex = 1;
|
||||
|
||||
if ($mainFieldMapping): ?>
|
||||
<div class="grid-cell grid-top-cell" style="flex: 0 0 150px;" data-index="1">
|
||||
<?php
|
||||
$fieldValue = $mainFieldMapping['manual_default'] ?? '';
|
||||
if ($mainFieldMapping['data_type'] === 'Data' && $mainFieldMapping['manual_default'] === 'today') {
|
||||
@ -578,35 +675,45 @@ function fixedDefaultValue(array $f): string
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="grid-cell" style="flex: 0 0 300px;">
|
||||
<?php endif;
|
||||
|
||||
$topColIndex = $mainFieldMapping ? 2 : 1; ?>
|
||||
|
||||
<div class="grid-cell grid-top-cell" style="flex: 0 0 300px;" data-index="<?= $mainFieldMapping ? 2 : 1 ?>">
|
||||
<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>
|
||||
<div class="grid-cell grid-top-cell" style="flex: 0 0 150px;" data-index="<?= $mainFieldMapping ? 3 : 2 ?>"></div>
|
||||
<?php
|
||||
|
||||
$topColIndex = $mainFieldMapping ? 4 : 3;
|
||||
$autoIndex = 0;
|
||||
|
||||
foreach ($allMappings as $mapping) {
|
||||
if (!$mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
|
||||
$inputClass = 'auto-input';
|
||||
if ($mapping['is_required']) $inputClass .= ' required-input';
|
||||
if ($mapping['data_type'] === 'SceltaMultipla') {
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'>";
|
||||
echo "<select class='custom-field dropdown-select $inputClass' data-column='auto_$autoIndex' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'>";
|
||||
echo "<select class='custom-field dropdown-select $inputClass' data-column='auto_$autoIndex' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue ?? '') . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||
echo "<option value=''>Seleziona...</option>";
|
||||
echo "</select>";
|
||||
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>";
|
||||
echo "</div>";
|
||||
} else {
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'></div>";
|
||||
}
|
||||
|
||||
$autoIndex++;
|
||||
$topColIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
$manualIndex = 0;
|
||||
|
||||
foreach ($allMappings as $mapping) {
|
||||
if ($mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
|
||||
$fieldValue = $mapping['manual_default'] ?? '';
|
||||
@ -615,7 +722,7 @@ function fixedDefaultValue(array $f): string
|
||||
}
|
||||
$inputClass = 'manual-input';
|
||||
if ($mapping['is_required']) $inputClass .= ' required-input';
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'>";
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'>";
|
||||
if ($mapping['data_type'] === 'SceltaMultipla') {
|
||||
echo "<select class='custom-field dropdown-select $inputClass' data-column='manual_$manualIndex' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||
echo "<option value=''>Seleziona...</option>";
|
||||
@ -629,18 +736,21 @@ function fixedDefaultValue(array $f): string
|
||||
}
|
||||
echo "<button type='button' class='propagate-btn' data-column='manual_$manualIndex'><i class='fas fa-arrow-down'></i></button>";
|
||||
echo "</div>";
|
||||
|
||||
$manualIndex++;
|
||||
$topColIndex++;
|
||||
}
|
||||
}
|
||||
// Aggiunta per Tested Component (senza propagate)
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||
echo "<div class='grid-cell' style='flex: 0 0 200px;'></div>";
|
||||
echo "<div class='grid-cell' style='flex: 0 0 250px;'></div>";
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||
// ---------------- FIXED FIELDS TOP (propagate) ----------------
|
||||
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'></div>";
|
||||
$topColIndex++;
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 200px;' data-index='$topColIndex'></div>";
|
||||
$topColIndex++;
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 250px;' data-index='$topColIndex'></div>";
|
||||
$topColIndex++;
|
||||
// ---------------- FIXED FIELDS TOP (propagate) - same order as header: fixed cols, 3 empties after ConsegnaRichiesta ----------------
|
||||
if (!empty($fixedFields)) {
|
||||
$insertedAfterConsegnaTop = false;
|
||||
foreach ($fixedFields as $fx => $f) {
|
||||
|
||||
$key = $f['fixed_field_key']; // datadb column
|
||||
@ -651,7 +761,8 @@ function fixedDefaultValue(array $f): string
|
||||
$inputClass = 'manual-input' . ($isRequired ? ' required-input' : '');
|
||||
$topRequiredClass = ($isRequired && ($val === '' || $val === null)) ? 'missing-required' : '';
|
||||
|
||||
echo "<div class='grid-cell {$topRequiredClass}' style='flex: 0 0 180px;'>";
|
||||
echo "<div class='grid-cell grid-top-cell {$topRequiredClass}' style='flex: 0 0 180px;' data-index='$topColIndex'>";
|
||||
$topColIndex++;
|
||||
|
||||
// Forza DATE anche se per errore nel DB è diversa
|
||||
$isDate = ($f['data_type'] === 'DATE' || $key === 'ConsegnaRichiesta');
|
||||
@ -697,6 +808,16 @@ function fixedDefaultValue(array $f): string
|
||||
// UNA SOLA freccia
|
||||
echo "<button type='button' class='propagate-btn' data-column='fixed_{$fx}'><i class='fas fa-arrow-down'></i></button>";
|
||||
echo "</div>";
|
||||
|
||||
if ($key === 'ConsegnaRichiesta' && !$insertedAfterConsegnaTop) {
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'></div>";
|
||||
$topColIndex++;
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'></div>";
|
||||
$topColIndex++;
|
||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'></div>";
|
||||
$topColIndex++;
|
||||
$insertedAfterConsegnaTop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -880,7 +1001,7 @@ function fixedDefaultValue(array $f): string
|
||||
echo "</div>";
|
||||
$cellIndex++;
|
||||
?>
|
||||
<div class="grid-cell" style="flex: 0 0 200px;">
|
||||
<div class="grid-cell" data-index="<?= $cellIndex ?>" style="flex: 0 0 200px;">
|
||||
<select name="rows[<?= $index ?>][carrier]" class="carrier-select">
|
||||
<option value="tnt-it">TNT Italy</option>
|
||||
<option value="dhl">DHL</option>
|
||||
@ -891,10 +1012,12 @@ function fixedDefaultValue(array $f): string
|
||||
<input type="text" name="rows[<?= $index ?>][awb_number]" class="cell-input awb-input" placeholder="Inserisci AWB Number">
|
||||
<button type="button" class="go-btn" data-row="<?= $index ?>"><i class="fas fa-play"></i></button>
|
||||
</div>
|
||||
<div class="grid-cell tracking-info" data-row="<?= $index ?>" style="flex: 0 0 250px;">
|
||||
<?php $cellIndex++; ?>
|
||||
<div class="grid-cell tracking-info" data-row="<?= $index ?>" data-index="<?= $cellIndex ?>" style="flex: 0 0 250px;">
|
||||
<span class="tracking-result">Shipment Info</span>
|
||||
<input type="hidden" name="rows[<?= $index ?>][tracking_info]" class="tracking-hidden">
|
||||
</div>
|
||||
<?php $cellIndex++; ?>
|
||||
|
||||
<?php
|
||||
// ---------------- FIXED FIELDS CELLS ----------------
|
||||
@ -1365,14 +1488,110 @@ function fixedDefaultValue(array $f): string
|
||||
|
||||
// Gestione degli input
|
||||
inputs.forEach(input => {
|
||||
input.addEventListener('focus', function() {
|
||||
this.closest('.grid-cell').classList.add('expanded');
|
||||
});
|
||||
input.addEventListener('blur', function() {
|
||||
this.closest('.grid-cell').classList.remove('expanded');
|
||||
});
|
||||
if (input.tagName === 'SELECT' ||
|
||||
input.classList.contains('date-picker') ||
|
||||
input.type === 'number' ||
|
||||
input.type === 'date') {
|
||||
input.addEventListener('focus', function() {
|
||||
this.closest('.grid-cell').classList.add('expanded');
|
||||
});
|
||||
input.addEventListener('blur', function() {
|
||||
this.closest('.grid-cell').classList.remove('expanded');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.type === 'text') {
|
||||
input.addEventListener('focus', function() {
|
||||
const cell = this.closest('.grid-cell');
|
||||
|
||||
cell.classList.add('expanded');
|
||||
cell.dataset.convertingToTextarea = 'true';
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
|
||||
textarea.value = this.value;
|
||||
textarea.name = this.name;
|
||||
textarea.className = this.className;
|
||||
textarea.required = this.required;
|
||||
textarea.style.border = '1px solid #ced4da';
|
||||
textarea.setAttribute('data-mapping-id', this.getAttribute('data-mapping-id') || '');
|
||||
textarea.setAttribute('data-field-id', this.getAttribute('data-field-id') || '');
|
||||
textarea.setAttribute('data-selected-value', this.getAttribute('data-selected-value') || '');
|
||||
textarea.setAttribute('data-current-value', this.getAttribute('data-current-value') || '');
|
||||
textarea.setAttribute('data-fixed-key', this.getAttribute('data-fixed-key') || '');
|
||||
textarea.dataset.originalInput = 'true';
|
||||
|
||||
this.parentNode.replaceChild(textarea, this);
|
||||
|
||||
setTimeout(() => {
|
||||
textarea.focus();
|
||||
delete cell.dataset.convertingToTextarea;
|
||||
}, 0);
|
||||
});
|
||||
} else {
|
||||
input.addEventListener('focus', function() {
|
||||
this.closest('.grid-cell').classList.add('expanded');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('blur', function(e) {
|
||||
const element = e.target;
|
||||
if (element.tagName === 'TEXTAREA' && element.dataset.originalInput === 'true') {
|
||||
const cell = element.closest('.grid-cell');
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.value = element.value;
|
||||
input.name = element.name;
|
||||
input.className = element.className;
|
||||
input.required = element.required;
|
||||
input.setAttribute('data-mapping-id', element.getAttribute('data-mapping-id') || '');
|
||||
input.setAttribute('data-field-id', element.getAttribute('data-field-id') || '');
|
||||
input.setAttribute('data-selected-value', element.getAttribute('data-selected-value') || '');
|
||||
input.setAttribute('data-current-value', element.getAttribute('data-current-value') || '');
|
||||
input.setAttribute('data-fixed-key', element.getAttribute('data-fixed-key') || '');
|
||||
|
||||
element.parentNode.replaceChild(input, element);
|
||||
|
||||
cell.classList.remove('expanded');
|
||||
|
||||
input.addEventListener('focus', function() {
|
||||
const cell = this.closest('.grid-cell');
|
||||
cell.classList.add('expanded');
|
||||
cell.dataset.convertingToTextarea = 'true';
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = this.value;
|
||||
textarea.name = this.name;
|
||||
textarea.className = this.className;
|
||||
textarea.required = this.required;
|
||||
textarea.style.border = '1px solid #ced4da';
|
||||
textarea.setAttribute('data-mapping-id', this.getAttribute('data-mapping-id') || '');
|
||||
textarea.setAttribute('data-field-id', this.getAttribute('data-field-id') || '');
|
||||
textarea.setAttribute('data-selected-value', this.getAttribute('data-selected-value') || '');
|
||||
textarea.setAttribute('data-current-value', this.getAttribute('data-current-value') || '');
|
||||
textarea.setAttribute('data-fixed-key', this.getAttribute('data-fixed-key') || '');
|
||||
textarea.dataset.originalInput = 'true';
|
||||
|
||||
this.parentNode.replaceChild(textarea, this);
|
||||
setTimeout(() => {
|
||||
textarea.focus();
|
||||
delete cell.dataset.convertingToTextarea;
|
||||
}, 0);
|
||||
});
|
||||
|
||||
const changeEvent = new Event('change', { bubbles: true });
|
||||
input.dispatchEvent(changeEvent);
|
||||
} else if (element.tagName === 'INPUT' && element.type === 'text') {
|
||||
const cell = element.closest('.grid-cell');
|
||||
if (cell && !cell.dataset.convertingToTextarea) {
|
||||
cell.classList.remove('expanded');
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
// Gestione della propagazione
|
||||
const propagateButtons = document.querySelectorAll('.propagate-btn');
|
||||
propagateButtons.forEach(button => {
|
||||
@ -1453,12 +1672,14 @@ function fixedDefaultValue(array $f): string
|
||||
|
||||
function resize(e) {
|
||||
if (currentResizer) {
|
||||
const dx = e.pageX - startX;
|
||||
const newWidth = startWidth + dx;
|
||||
const deltaX = e.pageX - startX;
|
||||
const newWidth = Math.max(80, startWidth + deltaX);
|
||||
const headers = document.querySelectorAll(`.grid-header[data-index="${columnIndex}"]`);
|
||||
const cells = document.querySelectorAll(`.grid-cell[data-index="${columnIndex}"]`);
|
||||
const topCells = document.querySelectorAll(`.grid-top .grid-cell[data-index="${columnIndex}"]`);
|
||||
headers.forEach(header => header.style.flex = `0 0 ${newWidth}px`);
|
||||
cells.forEach(cell => cell.style.flex = `0 0 ${newWidth}px`);
|
||||
topCells.forEach(cell => cell.style.flex = `0 0 ${newWidth}px`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2009,6 +2230,17 @@ function fixedDefaultValue(array $f): string
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Fix for sticky columns
|
||||
const gridContainer = document.querySelector('.grid-container');
|
||||
if (gridContainer) {
|
||||
const rows = document.querySelectorAll('.grid-row');
|
||||
rows.forEach(row => {
|
||||
if (!row.style.minWidth) {
|
||||
row.style.minWidth = 'max-content';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- Modale di conferma per l'esportazione -->
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user