1921 lines
91 KiB
PHP
1921 lines
91 KiB
PHP
<?php
|
||
include('include/headscript.php');
|
||
|
||
// ✅ FIX timezone (Rome)
|
||
ini_set('date.timezone', 'Europe/Rome');
|
||
date_default_timezone_set('Europe/Rome');
|
||
|
||
$template_id = intval($_GET['id'] ?? 0);
|
||
if (!$template_id) {
|
||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Template ID mancante"));
|
||
exit;
|
||
}
|
||
|
||
$user_id = $iduserlogin ?? 1;
|
||
$is_readonly = true;
|
||
$show_all_users = isset($_GET['all_users']) && $_GET['all_users'] == '1';
|
||
$allowedLimits = [20, 40, 60, 100];
|
||
$rawLimit = (int)($_GET['limit'] ?? 20);
|
||
$perPage = in_array($rawLimit, $allowedLimits) ? $rawLimit : 20;
|
||
$page = max(1, (int)($_GET['page'] ?? 1));
|
||
|
||
$db = DBHandlerSelect::getInstance();
|
||
$pdo = $db->getConnection();
|
||
|
||
// Recupera tutti i mapping dal template, includendo is_visible_import
|
||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, is_visible_import, auto_value
|
||
FROM template_mapping
|
||
WHERE template_id = ?");
|
||
$stmt->execute([$template_id]);
|
||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
$timeLabels = [
|
||
'Sample Arrival Time:',
|
||
'Sample Unlock Time:',
|
||
'Login Time:',
|
||
'Presa in carico, Orario:',
|
||
];
|
||
|
||
// Returns the auto value for current session (import) based on mapping.auto_value
|
||
function getImportAutoValue(array $mapping): string
|
||
{
|
||
$auto = $mapping['auto_value'] ?? 'none';
|
||
|
||
if ($auto === 'import_date') {
|
||
return date('Y-m-d');
|
||
}
|
||
|
||
if ($auto === 'import_time') {
|
||
// HTML <input type="time"> expects HH:MM (seconds optional)
|
||
return date('H:i');
|
||
}
|
||
|
||
return '';
|
||
}
|
||
|
||
if (empty($allMappings)) {
|
||
header("Location: import_xls.php?id=$template_id&status=error&message=" . urlencode("Nessun mapping trovato per il template"));
|
||
exit;
|
||
}
|
||
|
||
// Trova il campo main_field
|
||
$mainFieldMapping = null;
|
||
foreach ($allMappings as $mapping) {
|
||
if ($mapping['main_field'] == 1 && $mapping['is_visible_import'] == 1) {
|
||
$mainFieldMapping = $mapping;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 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;
|
||
|
||
// Maps logical fixed_field_key → real datadb column name
|
||
$fixedAliasMap = [
|
||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||
'ClienteFornitore' => 'cliente_fornitore_id',
|
||
'ClienteAnalisi' => 'clienteAnalisi',
|
||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||
];
|
||
|
||
// Fetch records with status='l' (exported to LIMS) for this template
|
||
$userFilter = $show_all_users ? '' : 'AND d.user_id = ?';
|
||
$filters = $_GET['f'] ?? [];
|
||
if (!is_array($filters)) $filters = [];
|
||
|
||
// Map of filter keys → datadb columns (direct columns)
|
||
$directColumnMap = [
|
||
'commessaweb' => 'd.commessaweb',
|
||
'idclient' => 'd.idclient',
|
||
'importreferencecode' => 'd.importreferencecode',
|
||
'importdate' => 'd.importdate',
|
||
'filename_import' => 'd.filename_import',
|
||
'user_name' => "CONCAT(u.first_name, ' ', u.last_name)",
|
||
];
|
||
// Add fixed field columns
|
||
foreach ($fixedAliasMap as $fixedKey => $dbCol) {
|
||
$directColumnMap[$fixedKey] = 'd.' . $dbCol;
|
||
}
|
||
|
||
$filterSQL = '';
|
||
$filterParams = [];
|
||
foreach ($filters as $key => $val) {
|
||
$val = trim($val);
|
||
if ($val === '') continue;
|
||
|
||
if (isset($directColumnMap[$key])) {
|
||
// Direct datadb column
|
||
$filterSQL .= " AND {$directColumnMap[$key]} LIKE ?";
|
||
$filterParams[] = '%' . $val . '%';
|
||
} elseif (str_starts_with($key, 'detail_')) {
|
||
// import_data_details field: detail_{mapping_id}
|
||
$mappingId = (int)substr($key, 7);
|
||
if ($mappingId > 0) {
|
||
$filterSQL .= " AND EXISTS (SELECT 1 FROM import_data_details dd WHERE dd.id = d.iddatadb AND dd.mapping_id = ? AND dd.field_value LIKE ?)";
|
||
$filterParams[] = $mappingId;
|
||
$filterParams[] = '%' . $val . '%';
|
||
}
|
||
}
|
||
}
|
||
|
||
$baseWhere = "WHERE d.templateid = ? AND d.status = 'l' {$userFilter} {$filterSQL}";
|
||
$baseParams = [$template_id];
|
||
if (!$show_all_users) $baseParams[] = $user_id;
|
||
$baseParams = array_merge($baseParams, $filterParams);
|
||
|
||
// Check if any filter is active
|
||
$hasActiveFilters = !empty(array_filter($filters, fn($v) => trim($v) !== ''));
|
||
|
||
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM datadb d LEFT JOIN auth_users u ON d.user_id = u.id {$baseWhere}");
|
||
$countStmt->execute($baseParams);
|
||
$totalRows = (int)$countStmt->fetchColumn();
|
||
$totalPages = max(1, (int)ceil($totalRows / $perPage));
|
||
if ($page > $totalPages) $page = $totalPages;
|
||
|
||
$stmt = $pdo->prepare("
|
||
SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name
|
||
FROM datadb d
|
||
LEFT JOIN auth_users u ON d.user_id = u.id
|
||
{$baseWhere}
|
||
ORDER BY d.iddatadb DESC
|
||
LIMIT {$perPage} OFFSET " . (($page - 1) * $perPage) . "
|
||
");
|
||
$stmt->execute($baseParams);
|
||
$importedData = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
$insertedIds = array_column($importedData, 'iddatadb');
|
||
|
||
// Fetch custom field details
|
||
$manualDetails = [];
|
||
if (!empty($insertedIds)) {
|
||
$placeholders = implode(',', array_fill(0, count($insertedIds), '?'));
|
||
$stmt = $pdo->prepare("
|
||
SELECT d.id AS detail_id, d.id AS datadb_id, d.mapping_id, d.field_value,
|
||
m.field_id, m.field_label, m.data_type, m.is_required, m.manual_default
|
||
FROM import_data_details d
|
||
JOIN template_mapping m ON d.mapping_id = m.id
|
||
WHERE d.id IN ({$placeholders})
|
||
");
|
||
$stmt->execute($insertedIds);
|
||
$manualDetails = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
}
|
||
|
||
// Recupera il mapping globale per mostrare gli slug leggibili
|
||
$stmt = $pdo->query("SELECT mysql_column_name, user_friendly_slug FROM column_mapping");
|
||
$slugMapping = [];
|
||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||
$slugMapping[$row['mysql_column_name']] = $row['user_friendly_slug'];
|
||
}
|
||
// --- FIX LABELS ONLY (do not change keys) ---
|
||
$slugMapping['AnagraficaCertestObject'] = 'Object';
|
||
$slugMapping['AnagraficaCertestService'] = 'Service';
|
||
|
||
// ---------------- FIXED FIELDS (from template_fixed_mapping) ----------------
|
||
$fixedStmt = $pdo->prepare("
|
||
SELECT id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
||
FROM template_fixed_mapping
|
||
WHERE template_id = ? AND is_visible_import = 1
|
||
ORDER BY id
|
||
");
|
||
$fixedStmt->execute([$template_id]);
|
||
$fixedFieldsRaw = $fixedStmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
// Ordine desiderato: Cliente Responsabile prima, poi gli altri, ConsegnaRichiesta prima delle 3 speciali
|
||
$desiredOrder = [
|
||
'ClienteResponsabile',
|
||
'ClienteFornitore',
|
||
'ClienteAnalisi',
|
||
'AnagraficaCertestObject',
|
||
'AnagraficaCertestService',
|
||
'MoltiplicatorePrezzo',
|
||
'ConsegnaRichiesta',
|
||
// se ci sono altri campi fixed li mettiamo dopo
|
||
];
|
||
|
||
// No exclusions: fixed fields will be rendered together at the end
|
||
$excludeFromFixed = [];
|
||
|
||
$fixedFields = [];
|
||
$tempMap = [];
|
||
foreach ($fixedFieldsRaw as $f) {
|
||
if (in_array($f['fixed_field_key'], $excludeFromFixed, true)) continue;
|
||
$tempMap[$f['fixed_field_key']] = $f;
|
||
}
|
||
|
||
foreach ($desiredOrder as $key) {
|
||
if (isset($tempMap[$key])) {
|
||
$fixedFields[] = $tempMap[$key];
|
||
unset($tempMap[$key]);
|
||
}
|
||
}
|
||
|
||
// Aggiunge eventuali campi fixed non elencati sopra (alla fine)
|
||
foreach ($tempMap as $f) {
|
||
$fixedFields[] = $f;
|
||
}
|
||
|
||
// helper default (DATE can use 'today' if you already use it elsewhere)
|
||
function fixedDefaultValue(array $f): string
|
||
{
|
||
$v = $f['default_value'] ?? '';
|
||
if ($f['data_type'] === 'DATE' && $v === 'today') return date('Y-m-d');
|
||
return (string)$v;
|
||
}
|
||
|
||
// ── Build lookup maps: id → label for fixed fields ──
|
||
$fixedLookup = []; // e.g. $fixedLookup['MoltiplicatorePrezzo'][2] = 'Urgente (1.5x)'
|
||
|
||
function loadCacheJson(string $path): ?array {
|
||
if (file_exists($path) && (time() - filemtime($path) < 3600)) {
|
||
return json_decode(file_get_contents($path), true);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// MoltiplicatorePrezzo
|
||
$cached = loadCacheJson(__DIR__ . '/cache/moltiplicatori_prezzo.json');
|
||
if ($cached) {
|
||
$items = $cached['value'] ?? $cached;
|
||
foreach ($items as $item) {
|
||
$id = $item['IdMoltiplicatorePrezzo'] ?? null;
|
||
if ($id !== null) $fixedLookup['MoltiplicatorePrezzo'][$id] = $item['Descrizione'] ?? $item['Codice'] ?? $id;
|
||
}
|
||
}
|
||
|
||
// AnagraficaCertestObject
|
||
$cached = loadCacheJson(__DIR__ . '/cache/anagrafica_certest_object.json');
|
||
if ($cached) {
|
||
$items = $cached['value'] ?? $cached;
|
||
foreach ($items as $item) {
|
||
$id = $item['IdAnagrafica'] ?? null;
|
||
if ($id !== null) $fixedLookup['AnagraficaCertestObject'][$id] = $item['NomeAnagrafica'] ?? $item['Codice'] ?? $id;
|
||
}
|
||
}
|
||
|
||
// AnagraficaCertestService
|
||
$cached = loadCacheJson(__DIR__ . '/cache/anagrafica_certest_service.json');
|
||
if ($cached) {
|
||
$items = $cached['value'] ?? $cached;
|
||
foreach ($items as $item) {
|
||
$id = $item['IdAnagrafica'] ?? null;
|
||
if ($id !== null) $fixedLookup['AnagraficaCertestService'][$id] = $item['NomeAnagrafica'] ?? $item['Codice'] ?? $id;
|
||
}
|
||
}
|
||
|
||
// ClienteResponsabile — per-client cache files
|
||
$clienteIds = array_unique(array_filter(array_column($importedData, 'idclient')));
|
||
foreach ($clienteIds as $cid) {
|
||
$cached = loadCacheJson(__DIR__ . '/cache/cliente_responsabili_' . (int)$cid . '.json');
|
||
if ($cached) {
|
||
$items = $cached['Responsabili'] ?? [];
|
||
foreach ($items as $item) {
|
||
$id = $item['IdClienteResponsabile'] ?? null;
|
||
if ($id !== null) $fixedLookup['ClienteResponsabile'][$id] = $item['Nominativo'] ?? $id;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Helper: resolve fixed field id → label
|
||
function resolveFixedValue(string $key, $val, array $fixedLookup): string {
|
||
if ($val === '' || $val === null) return '';
|
||
if (isset($fixedLookup[$key][(int)$val])) return $fixedLookup[$key][(int)$val];
|
||
return (string)$val;
|
||
}
|
||
|
||
?>
|
||
|
||
<!doctype html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||
<?php include('cssinclude.php'); ?>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2Lw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css">
|
||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
|
||
<style>
|
||
.cell-changed {
|
||
background-color: #fff3b0 !important;
|
||
transition: background-color 0.3s ease;
|
||
}
|
||
|
||
input.auto-input,
|
||
select.auto-input {
|
||
background-color: #d4edda;
|
||
}
|
||
|
||
input.manual-input,
|
||
select.manual-input {
|
||
background-color: #fff3cd;
|
||
}
|
||
|
||
input.required-input,
|
||
select.required-input {
|
||
background-color: #f8d7da;
|
||
}
|
||
|
||
input.required-input,
|
||
select.required-input {
|
||
background-color: #f8d7da;
|
||
border-color: #ced4da !important;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
input,
|
||
select,
|
||
textarea {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
border: 1px solid #ced4da;
|
||
border-radius: 4px;
|
||
padding: 5px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
input,
|
||
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;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
border-radius: 12px;
|
||
text-align: center;
|
||
min-width: 60px;
|
||
}
|
||
|
||
.status-i {
|
||
background-color: #ffc107;
|
||
color: #212529;
|
||
}
|
||
|
||
.status-P {
|
||
background-color: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.status-l {
|
||
background-color: #28a745;
|
||
color: white;
|
||
}
|
||
|
||
.grid-container {
|
||
overflow-x: auto;
|
||
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 {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0;
|
||
border-bottom: 1px solid #dee2e6;
|
||
min-width: fit-content;
|
||
}
|
||
|
||
.grid-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.grid-row:nth-child(even) {
|
||
background-color: #f8f9fa;
|
||
}
|
||
|
||
.grid-row:hover {
|
||
background-color: #e9ecef;
|
||
}
|
||
|
||
.grid-row.batch-exporting {
|
||
background: linear-gradient(90deg, #fff0f0 0%, #ffe0e0 50%, #fff0f0 100%) !important;
|
||
background-size: 200% 100% !important;
|
||
animation: batch-pulse 1.5s ease-in-out infinite;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.grid-row.batch-exporting .button-cell {
|
||
background: transparent !important;
|
||
}
|
||
|
||
@keyframes batch-pulse {
|
||
|
||
0%,
|
||
100% {
|
||
background-position: 0% 50%;
|
||
}
|
||
|
||
50% {
|
||
background-position: 100% 50%;
|
||
}
|
||
}
|
||
|
||
.batch-row-spinner {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 12px;
|
||
color: #eb0b0b;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.batch-row-spinner i {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.grid-row.batch-disabled {
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.grid-row.batch-disabled .action-btn {
|
||
pointer-events: none;
|
||
}
|
||
|
||
.grid-row.batch-row-error {
|
||
background: #fff3f3 !important;
|
||
border-left: 3px solid #dc3545;
|
||
}
|
||
|
||
.grid-row.batch-row-error .button-cell {
|
||
background: #fff3f3 !important;
|
||
}
|
||
|
||
.batch-error-msg {
|
||
color: #dc3545;
|
||
font-size: 10px;
|
||
line-height: 1.2;
|
||
display: block;
|
||
padding: 2px 0;
|
||
max-width: 200px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.batch-error-msg:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.grid-row.validation-row-error {
|
||
background: #fff3f3 !important;
|
||
border-left: 3px solid #dc3545;
|
||
}
|
||
|
||
.grid-cell.validation-error {
|
||
background-color: #f8d7da !important;
|
||
position: relative;
|
||
}
|
||
|
||
.input-validation-error,
|
||
.input-validation-error:focus {
|
||
border: 2px solid #dc3545 !important;
|
||
background-color: #fff5f5 !important;
|
||
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.25) !important;
|
||
outline: none !important;
|
||
}
|
||
|
||
.validation-tooltip {
|
||
display: none;
|
||
position: absolute;
|
||
bottom: 100%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: #dc3545;
|
||
color: #fff;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
white-space: nowrap;
|
||
z-index: 1050;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.validation-tooltip::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 100%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
border: 5px solid transparent;
|
||
border-top-color: #dc3545;
|
||
}
|
||
|
||
.grid-cell.validation-error:hover .validation-tooltip {
|
||
display: block;
|
||
}
|
||
|
||
.grid-header,
|
||
.grid-cell {
|
||
flex: 1;
|
||
min-width: 70px;
|
||
padding: 12px 15px;
|
||
border-right: 1px solid #dee2e6;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
transition: max-width 0.3s ease;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.grid-header {
|
||
font-weight: 600;
|
||
background-color: #e9ecef;
|
||
color: #495057;
|
||
border-bottom: 2px solid #dee2e6;
|
||
position: relative;
|
||
}
|
||
|
||
.grid-header:last-child,
|
||
.grid-cell:last-child {
|
||
border-right: none;
|
||
}
|
||
|
||
/* Sticky columns - first column (Actions) */
|
||
.grid-top .grid-cell.save-all-cell,
|
||
.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-top .grid-cell:nth-child(2),
|
||
.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,
|
||
.grid-header.expanded {
|
||
max-width: 500px !important;
|
||
white-space: normal !important;
|
||
overflow-wrap: break-word !important;
|
||
background-color: #e0f7fa !important;
|
||
flex: 0 0 500px !important;
|
||
}
|
||
|
||
.grid-header.expanded {
|
||
background-color: #e0f7fa !important;
|
||
}
|
||
|
||
.resizer {
|
||
width: 5px;
|
||
height: 100%;
|
||
background: #ddd;
|
||
cursor: col-resize;
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
}
|
||
|
||
.resizer:hover {
|
||
background: #999;
|
||
}
|
||
|
||
.grid-top {
|
||
display: none !important;
|
||
}
|
||
.grid-top-original {
|
||
display: flex;
|
||
align-items: stretch;
|
||
padding: 10px 0;
|
||
min-height: 0;
|
||
flex-wrap: nowrap;
|
||
}
|
||
|
||
.grid-top .grid-cell {
|
||
padding: 5px 10px;
|
||
flex: 0 0 150px;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
overflow: visible;
|
||
}
|
||
|
||
.grid-top .save-all-cell {
|
||
flex: 0 0 210px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: visible;
|
||
}
|
||
|
||
.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;
|
||
cursor: pointer;
|
||
color: #666;
|
||
font-size: 14px;
|
||
margin-top: 5px;
|
||
padding: 2px 5px;
|
||
border-radius: 3px;
|
||
transition: color 0.3s ease;
|
||
}
|
||
|
||
.propagate-btn:hover {
|
||
color: #28a745;
|
||
}
|
||
|
||
.awb-input {
|
||
width: 40% !important;
|
||
display: inline-block;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.carrier-select {
|
||
width: 40% !important;
|
||
display: inline-block;
|
||
border: 1px solid #ced4da;
|
||
border-radius: 4px;
|
||
padding: 5px;
|
||
font-size: 14px;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.go-btn {
|
||
width: 15% !important;
|
||
display: inline-block;
|
||
}
|
||
|
||
.tracking-info .tracking-result {
|
||
font-size: 12px;
|
||
color: #495057;
|
||
}
|
||
|
||
.modal {
|
||
display: none;
|
||
position: fixed;
|
||
z-index: 1050;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: auto;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
#partsModal {
|
||
z-index: 1200 !important;
|
||
}
|
||
|
||
.modal-content {
|
||
background-color: #fefefe;
|
||
margin: 15% auto;
|
||
padding: 20px;
|
||
border: 1px solid #888;
|
||
width: 80%;
|
||
max-width: 600px;
|
||
border-radius: 8px;
|
||
position: relative;
|
||
}
|
||
|
||
.close-btn {
|
||
color: #aaa;
|
||
float: right;
|
||
font-size: 28px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.close-btn:hover,
|
||
.close-btn:focus {
|
||
color: #000;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.overlay {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
z-index: 1049;
|
||
}
|
||
|
||
/* Required empty cell: keep light red background BUT NO red border */
|
||
.grid-cell.missing-required {
|
||
background-color: #ffe6e6 !important;
|
||
|
||
/* remove the red border you set */
|
||
border: 0 !important;
|
||
|
||
/* keep grid separators */
|
||
border-right: 1px solid #dee2e6 !important;
|
||
}
|
||
|
||
/* if it is the last column, don't force the right border */
|
||
.grid-cell.missing-required:last-child {
|
||
border-right: none !important;
|
||
}
|
||
|
||
|
||
.dropdown-select {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
border: 1px solid #ced4da;
|
||
border-radius: 4px;
|
||
padding: 5px;
|
||
font-size: 14px;
|
||
appearance: none;
|
||
background: white url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="%23333"><path d="M7.293 4.293a1 1 0 011.414 0L10 6.586l1.293-1.293a1 1 0 111.414 1.414l-2 2a1 1 0 01-1.414 0l-2-2a1 1 0 010-1.414z"/></svg>') no-repeat right 5px center;
|
||
}
|
||
|
||
.dropdown-select:focus {
|
||
outline: none;
|
||
border-color: #80bdff;
|
||
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
|
||
}
|
||
|
||
.grid-cell.button-cell,
|
||
.grid-header.button-header {
|
||
min-width: 210px !important;
|
||
flex: 0 0 210px !important;
|
||
}
|
||
|
||
.action-btn {
|
||
padding: 6px 8px;
|
||
margin-right: 5px;
|
||
border: none;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
width: 35px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.flash-success {
|
||
background-color: #d4edda !important;
|
||
transition: background-color 0.3s ease;
|
||
}
|
||
|
||
.actions-dropdown .dropdown-toggle {
|
||
background-color: #6c757d;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 5px;
|
||
padding: 6px 14px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.actions-dropdown .dropdown-toggle:hover,
|
||
.actions-dropdown .dropdown-toggle:focus {
|
||
background-color: #5a6268;
|
||
}
|
||
|
||
.actions-dropdown .dropdown-menu {
|
||
min-width: 160px;
|
||
font-size: 13px;
|
||
z-index: 1050;
|
||
}
|
||
|
||
|
||
.actions-dropdown .dropdown-item i {
|
||
width: 18px;
|
||
text-align: center;
|
||
margin-right: 6px;
|
||
}
|
||
|
||
#exportConfirmModal,
|
||
#exportResponseModal,
|
||
#exportUnsavedModal,
|
||
#exportBatchConfirmModal,
|
||
#exportBatchUnsavedModal,
|
||
#saveAllConfirmModal,
|
||
#saveAllResultModal {
|
||
z-index: 1300 !important;
|
||
}
|
||
|
||
#exportConfirmModal .modal-backdrop,
|
||
#exportResponseModal .modal-backdrop,
|
||
#exportUnsavedModal .modal-backdrop,
|
||
#exportBatchConfirmModal .modal-backdrop,
|
||
#exportBatchUnsavedModal .modal-backdrop,
|
||
#saveAllConfirmModal .modal-backdrop,
|
||
#saveAllResultModal .modal-backdrop {
|
||
z-index: 1299 !important;
|
||
}
|
||
|
||
.add-part-btn {
|
||
padding: 2px 5px;
|
||
font-size: 10px;
|
||
line-height: 1;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.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-results__options {
|
||
max-height: 400px !important;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.api-fixed-select+.select2-container .select2-selection__rendered {
|
||
color: #333;
|
||
}
|
||
|
||
.api-fixed-select option[value=""] {
|
||
color: #999;
|
||
font-style: italic;
|
||
}
|
||
|
||
.api-fixed-select.required-input:invalid,
|
||
.api-fixed-select[required]:not([value]):not([data-select2-id]) {
|
||
background-color: #f8d7da !important;
|
||
border-color: #dc3545 !important;
|
||
}
|
||
|
||
/* Hide grid-top (propagate row) */
|
||
.grid-top { display: none !important; }
|
||
/* Text cells styling */
|
||
.grid-row .grid-cell span { font-size: 12px; padding: 2px 4px; }
|
||
.propagate-btn { display: none !important; }
|
||
.button-cell { flex: 0 0 120px !important; }
|
||
|
||
/* View modals — ensure they sit above backdrop */
|
||
#partsViewModal, #photosViewModal { z-index: 1060 !important; }
|
||
.modal-backdrop { z-index: 1050 !important; }
|
||
/* ── Grid filter row ── */
|
||
.grid-filter-row {
|
||
background: #f0f4f8 !important;
|
||
border-bottom: 2px solid #dee2e6 !important;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 5;
|
||
}
|
||
.grid-filter-row:hover {
|
||
background: #f0f4f8 !important;
|
||
}
|
||
.grid-filter-row .grid-cell {
|
||
padding: 4px 6px !important;
|
||
}
|
||
.grid-filter-input {
|
||
width: 100% !important;
|
||
padding: 3px 6px !important;
|
||
font-size: 11px !important;
|
||
border: 1px solid #ced4da !important;
|
||
border-radius: 3px !important;
|
||
background: #fff !important;
|
||
color: #333 !important;
|
||
box-sizing: border-box;
|
||
}
|
||
.grid-filter-input:focus {
|
||
border-color: #80bdff !important;
|
||
box-shadow: 0 0 0 2px rgba(0,123,255,.15) !important;
|
||
outline: none !important;
|
||
}
|
||
.grid-filter-input::placeholder {
|
||
color: #adb5bd;
|
||
font-style: italic;
|
||
}
|
||
.grid-filter-row .grid-filter-input.has-value {
|
||
border-color: #0d6efd !important;
|
||
background: #f0f7ff !important;
|
||
}
|
||
select.grid-filter-input {
|
||
cursor: pointer;
|
||
padding-right: 18px !important;
|
||
appearance: auto;
|
||
}
|
||
.filter-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
width: 100%;
|
||
}
|
||
.filter-wrap .grid-filter-input {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.filter-clear-btn {
|
||
flex-shrink: 0;
|
||
border: none;
|
||
background: #dc3545;
|
||
color: #fff;
|
||
font-size: 10px;
|
||
cursor: pointer;
|
||
padding: 2px 4px;
|
||
line-height: 1;
|
||
border-radius: 3px;
|
||
}
|
||
.filter-clear-btn:hover {
|
||
background: #c82333;
|
||
}
|
||
.grid-filter-row .grid-cell {
|
||
background: #f0f4f8 !important;
|
||
}
|
||
/* First sticky cell fills full row height */
|
||
.grid-filter-row .grid-cell.button-cell {
|
||
align-self: stretch;
|
||
}
|
||
|
||
/* ── Pagination bar ── */
|
||
.pager-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background: #f8f9fa;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 6px;
|
||
padding: 6px 14px;
|
||
font-size: 13px;
|
||
color: #495057;
|
||
}
|
||
.pager-rows-per-page {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.pager-label {
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
}
|
||
.pager-limit-group {
|
||
display: inline-flex;
|
||
border: 1px solid #ced4da;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
.pager-limit-btn {
|
||
padding: 3px 10px;
|
||
text-decoration: none;
|
||
color: #495057;
|
||
border-right: 1px solid #ced4da;
|
||
transition: background .15s, color .15s;
|
||
font-weight: 500;
|
||
}
|
||
.pager-limit-btn:last-child { border-right: none; }
|
||
.pager-limit-btn:hover { background: #e9ecef; text-decoration: none; color: #212529; }
|
||
.pager-limit-btn.active {
|
||
background: #0d6efd;
|
||
color: #fff;
|
||
}
|
||
.pager-limit-btn.active:hover { background: #0b5ed7; color: #fff; }
|
||
.pager-nav {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
.pager-info {
|
||
margin-right: 8px;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
}
|
||
.pager-btn, .pager-num {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 28px;
|
||
height: 28px;
|
||
padding: 0 6px;
|
||
border: 1px solid #ced4da;
|
||
border-radius: 4px;
|
||
text-decoration: none;
|
||
color: #495057;
|
||
font-weight: 500;
|
||
transition: background .15s, color .15s, border-color .15s;
|
||
}
|
||
.pager-btn:hover, .pager-num:hover { background: #e9ecef; text-decoration: none; color: #212529; }
|
||
.pager-num.active {
|
||
background: #0d6efd;
|
||
color: #fff;
|
||
border-color: #0d6efd;
|
||
}
|
||
.pager-btn.disabled {
|
||
pointer-events: none;
|
||
opacity: .4;
|
||
}
|
||
.pager-dots {
|
||
padding: 0 2px;
|
||
color: #adb5bd;
|
||
user-select: none;
|
||
}
|
||
</style>
|
||
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="wrapper">
|
||
<?php include('include/navbar.php'); ?>
|
||
<?php include('include/topbar.php'); ?>
|
||
<div class="page-wrapper">
|
||
<div class="page-content">
|
||
<div class="mb-3 text d-flex align-items-center gap-2">
|
||
<a href="imported.php?id=<?= $template_id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||
<a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success">To LIMS (l)</a>
|
||
<span class="ms-3">
|
||
<label class="form-check-label" style="font-size: 13px; cursor: pointer;">
|
||
<?php
|
||
$toggleParams = $_GET;
|
||
unset($toggleParams['all_users']);
|
||
$toggleBase = 'tolims.php?' . http_build_query($toggleParams);
|
||
?>
|
||
<input type="checkbox" class="form-check-input" id="showAllUsers" <?= $show_all_users ? 'checked' : '' ?>
|
||
onchange="window.location.href='<?= htmlspecialchars($toggleBase) ?>' + (this.checked ? '&all_users=1' : '')">
|
||
Show all users
|
||
</label>
|
||
</span>
|
||
<span class="text-muted" style="font-size: 12px;">
|
||
(<?= count($importedData) ?> of <?= $totalRows ?> records<?= !$show_all_users ? ' — my records' : '' ?>)
|
||
</span>
|
||
</div>
|
||
<?php
|
||
$baseQuery = $_GET;
|
||
unset($baseQuery['limit'], $baseQuery['page']);
|
||
$pageQuery = $_GET;
|
||
unset($pageQuery['page']);
|
||
$pageBase = 'tolims.php?' . http_build_query($pageQuery);
|
||
$fromRow = ($page - 1) * $perPage + 1;
|
||
$toRow = min($page * $perPage, $totalRows);
|
||
?>
|
||
<div class="pager-bar mb-2">
|
||
<div class="pager-rows-per-page">
|
||
<span class="pager-label">Rows per page</span>
|
||
<div class="pager-limit-group">
|
||
<?php foreach ($allowedLimits as $lim):
|
||
$isActive = ($perPage === $lim);
|
||
$url = 'tolims.php?' . http_build_query(array_merge($baseQuery, ['limit' => $lim]));
|
||
?>
|
||
<a href="<?= htmlspecialchars($url) ?>" class="pager-limit-btn <?= $isActive ? 'active' : '' ?>"><?= $lim ?></a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
<?php if ($totalPages > 1): ?>
|
||
<div class="pager-nav">
|
||
<span class="pager-info"><?= $fromRow ?>–<?= $toRow ?> of <?= $totalRows ?></span>
|
||
<a href="<?= htmlspecialchars($pageBase . '&page=1') ?>" class="pager-btn <?= $page <= 1 ? 'disabled' : '' ?>" title="First"><i class="fas fa-angle-double-left"></i></a>
|
||
<a href="<?= htmlspecialchars($pageBase . '&page=' . ($page - 1)) ?>" class="pager-btn <?= $page <= 1 ? 'disabled' : '' ?>" title="Previous"><i class="fas fa-angle-left"></i></a>
|
||
<?php
|
||
$startPage = max(1, $page - 2);
|
||
$endPage = min($totalPages, $page + 2);
|
||
if ($startPage > 1): ?>
|
||
<a href="<?= htmlspecialchars($pageBase . '&page=1') ?>" class="pager-num">1</a>
|
||
<?php if ($startPage > 2): ?><span class="pager-dots">...</span><?php endif; ?>
|
||
<?php endif;
|
||
for ($p = $startPage; $p <= $endPage; $p++): ?>
|
||
<a href="<?= htmlspecialchars($pageBase . '&page=' . $p) ?>" class="pager-num <?= $p === $page ? 'active' : '' ?>"><?= $p ?></a>
|
||
<?php endfor;
|
||
if ($endPage < $totalPages): ?>
|
||
<?php if ($endPage < $totalPages - 1): ?><span class="pager-dots">...</span><?php endif; ?>
|
||
<a href="<?= htmlspecialchars($pageBase . '&page=' . $totalPages) ?>" class="pager-num"><?= $totalPages ?></a>
|
||
<?php endif; ?>
|
||
<a href="<?= htmlspecialchars($pageBase . '&page=' . ($page + 1)) ?>" class="pager-btn <?= $page >= $totalPages ? 'disabled' : '' ?>" title="Next"><i class="fas fa-angle-right"></i></a>
|
||
<a href="<?= htmlspecialchars($pageBase . '&page=' . $totalPages) ?>" class="pager-btn <?= $page >= $totalPages ? 'disabled' : '' ?>" title="Last"><i class="fas fa-angle-double-right"></i></a>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="card radius-10">
|
||
<div class="card-header">
|
||
<div class="d-flex align-items-center" style="min-height: 42px; gap: 12px;">
|
||
<span style="font-weight: 600; font-size: 14px;"><i class="fas fa-check-circle" style="color: #28a745;"></i> Exported to LIMS</span>
|
||
<?php if ($hasActiveFilters): ?>
|
||
<a href="tolims.php?id=<?= $template_id ?><?= $show_all_users ? '&all_users=1' : '' ?><?= $perPage !== 20 ? '&limit=' . $perPage : '' ?>" class="btn btn-outline-danger btn-sm" style="font-size: 12px;"><i class="fas fa-times"></i> Clear filters</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<form id="editForm">
|
||
<div class="grid-container">
|
||
<div class="grid-top">
|
||
<div class="grid-cell save-all-cell"></div>
|
||
<?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') {
|
||
$fieldValue = date('Y-m-d');
|
||
}
|
||
$inputClass = $mainFieldMapping['is_manual'] ? 'manual-input' : 'auto-input';
|
||
if ($mainFieldMapping['is_required']) $inputClass .= ' required-input';
|
||
if ($mainFieldMapping['data_type'] === 'SceltaMultipla') {
|
||
echo "<select class='custom-field dropdown-select $inputClass' data-column='main_field' data-field-id='{$mainFieldMapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ">";
|
||
echo "<option value=''>Seleziona...</option>";
|
||
echo "</select>";
|
||
echo "<button type='button' class='propagate-btn' data-column='main_field'><i class='fas fa-arrow-down'></i></button>";
|
||
} elseif ($mainFieldMapping['data_type'] === 'Data') {
|
||
echo "<input type='text' class='custom-field date-picker $inputClass' data-column='main_field' value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ">";
|
||
echo "<button type='button' class='propagate-btn' data-column='main_field'><i class='fas fa-arrow-down'></i></button>";
|
||
} elseif ($mainFieldMapping['data_type'] === 'INT') {
|
||
echo "<input type='number' class='custom-field $inputClass' data-column='main_field' value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ">";
|
||
echo "<button type='button' class='propagate-btn' data-column='main_field'><i class='fas fa-arrow-down'></i></button>";
|
||
} else {
|
||
echo "<input type='text' class='custom-field $inputClass' data-column='main_field' value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ">";
|
||
echo "<button type='button' class='propagate-btn' data-column='main_field'><i class='fas fa-arrow-down'></i></button>";
|
||
}
|
||
?>
|
||
</div>
|
||
<?php endif;
|
||
|
||
$topColIndex = $mainFieldMapping ? 2 : 1; ?>
|
||
|
||
<div class="grid-cell grid-top-cell" style="flex: 0 0 150px;" data-index="<?= $mainFieldMapping ? 2 : 1 ?>"></div>
|
||
|
||
<div class="grid-cell grid-top-cell" style="flex: 0 0 300px;" data-index="<?= $mainFieldMapping ? 3 : 2 ?>">
|
||
<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>
|
||
<?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 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 {
|
||
// Show auto import date/time in header too (read-only)
|
||
$autoVal = getImportAutoValue($mapping);
|
||
|
||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'>";
|
||
|
||
if (($mapping['auto_value'] ?? 'none') === 'import_date') {
|
||
echo "<input type='text' class='custom-field date-picker auto-input' value='" . htmlspecialchars($autoVal) . "' readonly>";
|
||
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>";
|
||
} elseif (($mapping['auto_value'] ?? 'none') === 'import_time') {
|
||
echo "<input type='time' class='custom-field auto-input' value='" . htmlspecialchars($autoVal) . "' readonly>";
|
||
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>";
|
||
} else {
|
||
// keep empty cell for other auto fields
|
||
echo "";
|
||
}
|
||
|
||
echo "</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'] ?? '';
|
||
if ($mapping['data_type'] === 'Data' && $mapping['manual_default'] === 'today') {
|
||
$fieldValue = date('Y-m-d');
|
||
}
|
||
$inputClass = 'manual-input';
|
||
if ($mapping['is_required']) $inputClass .= ' required-input';
|
||
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>";
|
||
echo "</select>";
|
||
} elseif ($mapping['data_type'] === 'Data') {
|
||
echo "<input type='text' class='custom-field date-picker $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||
} elseif ($mapping['data_type'] === 'INT') {
|
||
echo "<input type='number' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||
} elseif (in_array($mapping['field_label'], $timeLabels)) {
|
||
echo "<input type='time' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||
} else {
|
||
echo "<input type='text' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||
}
|
||
echo "<button type='button' class='propagate-btn' data-column='manual_$manualIndex'><i class='fas fa-arrow-down'></i></button>";
|
||
echo "</div>";
|
||
|
||
$manualIndex++;
|
||
$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 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
|
||
|
||
$val = fixedDefaultValue($f);
|
||
|
||
$isRequired = ((int)$f['is_required'] === 1);
|
||
$inputClass = 'manual-input' . ($isRequired ? ' required-input' : '');
|
||
$topRequiredClass = ($isRequired && ($val === '' || $val === null)) ? 'missing-required' : '';
|
||
|
||
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');
|
||
|
||
if ($isDate) {
|
||
|
||
echo "<input type='text'
|
||
class='custom-field date-picker {$inputClass} fixed-top'
|
||
data-column='fixed_{$fx}'
|
||
data-fixed-key='" . htmlspecialchars($key, ENT_QUOTES) . "'
|
||
value='" . htmlspecialchars((string)$val, ENT_QUOTES) . "'
|
||
" . ($isRequired ? "required" : "") . ">";
|
||
} else {
|
||
|
||
$isApiField = in_array($key, [
|
||
'MoltiplicatorePrezzo',
|
||
'ClienteResponsabile',
|
||
'ClienteFornitore',
|
||
'ClienteAnalisi',
|
||
'AnagraficaCertestObject',
|
||
'AnagraficaCertestService'
|
||
], true);
|
||
|
||
if ($isApiField) {
|
||
$placeholder = ($key === 'ClienteResponsabile') ? 'Seleziona cliente prima...' : 'Seleziona...';
|
||
|
||
echo "<select
|
||
class='custom-field dropdown-select api-fixed-select {$inputClass} fixed-top'
|
||
data-column='fixed_{$fx}'
|
||
data-fixed-key='" . htmlspecialchars($key, ENT_QUOTES) . "'
|
||
" . ($isRequired ? "required" : "") . ">
|
||
<option value=''>{$placeholder}</option>
|
||
</select>";
|
||
} else {
|
||
|
||
echo "<input type='number'
|
||
class='custom-field {$inputClass} fixed-top'
|
||
data-column='fixed_{$fx}'
|
||
data-fixed-key='" . htmlspecialchars($key, ENT_QUOTES) . "'
|
||
value='" . htmlspecialchars((string)$val, ENT_QUOTES) . "'
|
||
" . ($isRequired ? "required" : "") . ">";
|
||
}
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
?>
|
||
</div>
|
||
|
||
<div class="grid-row">
|
||
<div class="grid-header button-header" style="flex: 0 0 120px;">View</div>
|
||
<?php if ($mainFieldMapping): ?>
|
||
<div class="grid-header" data-index="1" style="flex: 0 0 150px; position: relative;">
|
||
<?= htmlspecialchars($mainFieldMapping['field_label']) ?>
|
||
<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>
|
||
<div class="grid-header" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 300px; position: relative;">Client<div class="resizer"></div>
|
||
</div>
|
||
<?php
|
||
$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>";
|
||
$headerIndex++;
|
||
}
|
||
}
|
||
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>";
|
||
$headerIndex++;
|
||
}
|
||
}
|
||
// Aggiunta header per Tested Component
|
||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>Tested Component<div class='resizer'></div></div>";
|
||
$headerIndex++;
|
||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 200px; position: relative;'>AWB Number<div class='resizer'></div></div>";
|
||
$headerIndex++;
|
||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 250px; position: relative;'>Tracking Info<div class='resizer'></div></div>";
|
||
$headerIndex++;
|
||
|
||
// ---------------- FIXED FIELDS HEADERS ----------------
|
||
if (!empty($fixedFields)) {
|
||
$insertedAfterConsegna = false;
|
||
foreach ($fixedFields as $f) {
|
||
$key = $f['fixed_field_key'];
|
||
$label = $slugMapping[$key] ?? $key;
|
||
|
||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 180px; position: relative;'>"
|
||
. htmlspecialchars($label) .
|
||
"<div class='resizer'></div></div>";
|
||
$headerIndex++;
|
||
|
||
// Inseriamo le 3 colonne SOLO dopo ConsegnaRichiesta
|
||
if ($key === 'ConsegnaRichiesta' && !$insertedAfterConsegna) {
|
||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>Import Reference Code<div class='resizer'></div></div>";
|
||
$headerIndex++;
|
||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . ($slugMapping['filename_import'] ?? 'filename_import') . "<div class='resizer'></div></div>";
|
||
$headerIndex++;
|
||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . ($slugMapping['importdate'] ?? 'importdate') . "<div class='resizer'></div></div>";
|
||
$headerIndex++;
|
||
$insertedAfterConsegna = true;
|
||
}
|
||
}
|
||
}
|
||
?>
|
||
|
||
</div>
|
||
|
||
<!-- Filter row (server-side) -->
|
||
<?php
|
||
// Helper: wrap filter element with clear button
|
||
function wrapFilter(string $inner, bool $hasValue): string {
|
||
$btn = $hasValue
|
||
? "<button type='button' class='filter-clear-btn' onclick='clearOneFilter(this)' title='Clear'>×</button>"
|
||
: '';
|
||
return "<div class='filter-wrap'>{$inner}{$btn}</div>";
|
||
}
|
||
// Helper: render a select filter from a lookup array
|
||
function renderFilterSelect(string $name, array $options, string $currentVal, string $style = ''): string {
|
||
$html = "<select class='grid-filter-input' name='" . htmlspecialchars($name) . "' style='" . htmlspecialchars($style) . "' onchange='submitFilters()'>";
|
||
$html .= "<option value=''>All</option>";
|
||
foreach ($options as $id => $label) {
|
||
$sel = ((string)$id === $currentVal) ? ' selected' : '';
|
||
$html .= "<option value='" . htmlspecialchars((string)$id) . "'{$sel}>" . htmlspecialchars($label) . "</option>";
|
||
}
|
||
$html .= "</select>";
|
||
return wrapFilter($html, $currentVal !== '');
|
||
}
|
||
// Helper: render a text filter
|
||
function renderFilterInput(string $name, string $currentVal): string {
|
||
$inner = "<input type='text' class='grid-filter-input' name='" . htmlspecialchars($name) . "' value='" . htmlspecialchars($currentVal) . "' placeholder='⌕'>";
|
||
return wrapFilter($inner, $currentVal !== '');
|
||
}
|
||
// Helper: render a select filter populated by JS
|
||
function renderFilterSelectJS(string $name, string $currentVal, string $jsClass, string $dataAttr = ''): string {
|
||
$html = "<select class='grid-filter-input {$jsClass}' name='" . htmlspecialchars($name) . "' {$dataAttr} onchange='submitFilters()'>";
|
||
$html .= "<option value=''>All</option>";
|
||
if ($currentVal !== '') {
|
||
$html .= "<option value='" . htmlspecialchars($currentVal) . "' selected>" . htmlspecialchars($currentVal) . "</option>";
|
||
}
|
||
$html .= "</select>";
|
||
return wrapFilter($html, $currentVal !== '');
|
||
}
|
||
// Fields that are client-id lookups
|
||
$clientFilterKeys = ['ClienteFornitore', 'ClienteAnalisi'];
|
||
?>
|
||
<div class="grid-row grid-filter-row" id="gridFilterRow">
|
||
<div class="grid-cell button-cell"></div>
|
||
<?php if ($mainFieldMapping):
|
||
$fKey = 'detail_' . $mainFieldMapping['id'];
|
||
$fVal = $filters[$fKey] ?? '';
|
||
?>
|
||
<div class="grid-cell" style="flex: 0 0 150px; padding: 4px 6px;">
|
||
<?php if ($mainFieldMapping['data_type'] === 'SceltaMultipla'): ?>
|
||
<?= renderFilterSelectJS("f[$fKey]", $fVal, 'scelta-filter', "data-field-id='{$mainFieldMapping['field_id']}'") ?>
|
||
<?php else: ?>
|
||
<?= renderFilterInput("f[$fKey]", $fVal) ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<!-- Status/CommessaWeb -->
|
||
<div class="grid-cell" style="flex: 0 0 150px; padding: 4px 6px;">
|
||
<?= renderFilterInput('f[commessaweb]', $filters['commessaweb'] ?? '') ?>
|
||
</div>
|
||
<!-- Client -->
|
||
<div class="grid-cell" style="flex: 0 0 300px; padding: 4px 6px;">
|
||
<?= renderFilterSelectJS('f[idclient]', $filters['idclient'] ?? '', 'client-filter') ?>
|
||
</div>
|
||
<?php
|
||
// Auto fields
|
||
foreach ($allMappings as $mapping) {
|
||
if (!$mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
|
||
$fKey = 'detail_' . $mapping['id'];
|
||
$fVal = $filters[$fKey] ?? '';
|
||
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>";
|
||
if ($mapping['data_type'] === 'SceltaMultipla') {
|
||
echo renderFilterSelectJS("f[$fKey]", $fVal, 'scelta-filter', "data-field-id='{$mapping['field_id']}'");
|
||
} else {
|
||
echo renderFilterInput("f[$fKey]", $fVal);
|
||
}
|
||
echo "</div>";
|
||
}
|
||
}
|
||
// Manual fields
|
||
foreach ($allMappings as $mapping) {
|
||
if ($mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
|
||
$fKey = 'detail_' . $mapping['id'];
|
||
$fVal = $filters[$fKey] ?? '';
|
||
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>";
|
||
if ($mapping['data_type'] === 'SceltaMultipla') {
|
||
echo renderFilterSelectJS("f[$fKey]", $fVal, 'scelta-filter', "data-field-id='{$mapping['field_id']}'");
|
||
} else {
|
||
echo renderFilterInput("f[$fKey]", $fVal);
|
||
}
|
||
echo "</div>";
|
||
}
|
||
}
|
||
// Tested Component, AWB, Tracking
|
||
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'></div>";
|
||
echo "<div class='grid-cell' style='flex: 0 0 200px; padding: 4px 6px;'></div>";
|
||
echo "<div class='grid-cell' style='flex: 0 0 250px; padding: 4px 6px;'></div>";
|
||
|
||
// Fixed fields
|
||
if (!empty($fixedFields)) {
|
||
$insertedFilterAfterConsegna = false;
|
||
foreach ($fixedFields as $f) {
|
||
$key = $f['fixed_field_key'];
|
||
$fVal = $filters[$key] ?? '';
|
||
echo "<div class='grid-cell' style='flex: 0 0 180px; padding: 4px 6px;'>";
|
||
if (in_array($key, $clientFilterKeys, true)) {
|
||
// Client-based selects (populated by JS)
|
||
echo renderFilterSelectJS("f[$key]", $fVal, 'client-filter');
|
||
} elseif (isset($fixedLookup[$key]) && !empty($fixedLookup[$key])) {
|
||
// PHP-cached lookups
|
||
echo renderFilterSelect("f[$key]", $fixedLookup[$key], $fVal);
|
||
} elseif ($key === 'ConsegnaRichiesta') {
|
||
echo renderFilterInput("f[$key]", $fVal);
|
||
} else {
|
||
echo renderFilterInput("f[$key]", $fVal);
|
||
}
|
||
echo "</div>";
|
||
|
||
if ($key === 'ConsegnaRichiesta' && !$insertedFilterAfterConsegna) {
|
||
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>" . renderFilterInput('f[importreferencecode]', $filters['importreferencecode'] ?? '') . "</div>";
|
||
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>" . renderFilterInput('f[filename_import]', $filters['filename_import'] ?? '') . "</div>";
|
||
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>" . renderFilterInput('f[importdate]', $filters['importdate'] ?? '') . "</div>";
|
||
$insertedFilterAfterConsegna = true;
|
||
}
|
||
}
|
||
}
|
||
?>
|
||
</div>
|
||
|
||
<?php foreach ($importedData as $index => $row): ?>
|
||
<div class="grid-row" data-id="<?= $row['iddatadb'] ?>">
|
||
<div class="grid-cell button-cell" style="flex: 0 0 120px; position: relative;">
|
||
<button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="Photos" style="background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-camera"></i></button>
|
||
<button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="Parts" style="background: #ffc107; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-puzzle-piece"></i></button>
|
||
</div>
|
||
<?php if ($mainFieldMapping):
|
||
$detail = array_filter($manualDetails, fn($d) => $d['mapping_id'] == $mainFieldMapping['id'] && $d['datadb_id'] == $row['iddatadb']);
|
||
$detail = reset($detail) ?: ['field_value' => $mainFieldMapping['manual_default']];
|
||
$fieldValue = $detail['field_value'] ?? $mainFieldMapping['manual_default'] ?? '';
|
||
$isScelta = ($mainFieldMapping['data_type'] === 'SceltaMultipla');
|
||
?>
|
||
<div class="grid-cell" data-col="main_field" data-row="<?= $index ?>" data-index="1" style="flex: 0 0 150px;">
|
||
<span <?= $isScelta ? "class='scelta-value' data-field-id='{$mainFieldMapping['field_id']}'" : '' ?>><?= htmlspecialchars($fieldValue) ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
<div class="grid-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px;">
|
||
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'l') ?>">To LIMS</span>
|
||
<?php if (!empty($row['commessaweb'])): ?>
|
||
<span class="commessaweb-code" style="display:block; font-size:0.75em; color:#555; margin-top:2px;"><?= htmlspecialchars($row['commessaweb']) ?></span>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="grid-cell" data-col="idclient" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 300px;">
|
||
<span class="client-text" data-client-id="<?= htmlspecialchars($row['idclient'] ?? '') ?>"><?= htmlspecialchars($row['idclient'] ?? '') ?></span>
|
||
</div>
|
||
|
||
<?php
|
||
$cellIndex = $mainFieldMapping ? 4 : 3;
|
||
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
|
||
$autoIndex = 0;
|
||
foreach ($allMappings as $mapping) {
|
||
if (!$mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
|
||
$detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']);
|
||
$detail = reset($detail) ?: ['field_value' => $mapping['manual_default']];
|
||
$fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? '';
|
||
$isScelta = ($mapping['data_type'] === 'SceltaMultipla');
|
||
echo "<div class='grid-cell' data-col='auto_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
|
||
echo $isScelta
|
||
? "<span class='scelta-value' data-field-id='{$mapping['field_id']}'>" . htmlspecialchars($fieldValue) . "</span>"
|
||
: "<span>" . htmlspecialchars($fieldValue) . "</span>";
|
||
echo "</div>";
|
||
$cellIndex++;
|
||
$autoIndex++;
|
||
}
|
||
}
|
||
foreach ($allMappings as $mapping) {
|
||
if ($mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
|
||
$detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']);
|
||
$detail = reset($detail) ?: ['field_value' => $mapping['manual_default']];
|
||
$fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? '';
|
||
$isScelta = ($mapping['data_type'] === 'SceltaMultipla');
|
||
echo "<div class='grid-cell' data-col='manual_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
|
||
echo $isScelta
|
||
? "<span class='scelta-value' data-field-id='{$mapping['field_id']}'>" . htmlspecialchars($fieldValue) . "</span>"
|
||
: "<span>" . htmlspecialchars($fieldValue) . "</span>";
|
||
echo "</div>";
|
||
$cellIndex++;
|
||
}
|
||
}
|
||
// Tested Component (empty for view)
|
||
echo "<div class='grid-cell' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'><span></span></div>";
|
||
$cellIndex++;
|
||
?>
|
||
<div class="grid-cell" data-index="<?= $cellIndex ?>" style="flex: 0 0 200px;">
|
||
<span></span>
|
||
</div>
|
||
<?php $cellIndex++; ?>
|
||
<div class="grid-cell" data-row="<?= $index ?>" data-index="<?= $cellIndex ?>" style="flex: 0 0 250px;">
|
||
<span></span>
|
||
</div>
|
||
<?php $cellIndex++; ?>
|
||
|
||
<?php
|
||
// ---------------- FIXED FIELDS CELLS (text only) ----------------
|
||
if (!empty($fixedFields)) {
|
||
foreach ($fixedFields as $f) {
|
||
$key = $f['fixed_field_key'];
|
||
$dbCol = $fixedAliasMap[$key] ?? $key;
|
||
$val = $row[$dbCol] ?? '';
|
||
$isClientField = in_array($key, ['ClienteFornitore', 'ClienteAnalisi'], true);
|
||
$displayVal = $isClientField ? (string)$val : resolveFixedValue($key, $val, $fixedLookup);
|
||
|
||
echo "<div class='grid-cell' data-col='" . htmlspecialchars($key) . "' data-row='$index' data-index='$cellIndex' style='flex: 0 0 180px;'>";
|
||
if ($isClientField && $val !== '' && $val !== null) {
|
||
echo "<span class='client-text' data-client-id='" . htmlspecialchars((string)$val) . "'>" . htmlspecialchars((string)$val) . "</span>";
|
||
} else {
|
||
echo "<span>" . htmlspecialchars($displayVal) . "</span>";
|
||
}
|
||
echo "</div>";
|
||
$cellIndex++;
|
||
|
||
if ($key === 'ConsegnaRichiesta') {
|
||
echo "<div class='grid-cell' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'><span>" . htmlspecialchars($row['importreferencecode'] ?? '') . "</span></div>";
|
||
$cellIndex++;
|
||
echo "<div class='grid-cell' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'><a href='imported_trf/" . htmlspecialchars($row['filename_import'] ?? '') . "' target='_blank'>File</a></div>";
|
||
$cellIndex++;
|
||
echo "<div class='grid-cell' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'><span>" . htmlspecialchars($row['importdate'] ?? '') . "</span></div>";
|
||
$cellIndex++;
|
||
}
|
||
}
|
||
}
|
||
?>
|
||
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</form>
|
||
<!-- Parts Modal -->
|
||
<div class="modal fade" id="partsViewModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">Parts</h5>
|
||
<button type="button" class="btn-close close-parts-modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="partsViewContent"><i class="fas fa-spinner fa-spin"></i> Loading...</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary btn-sm close-parts-modal">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Photos Modal -->
|
||
<div class="modal fade" id="photosViewModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">Photos</h5>
|
||
<button type="button" class="btn-close close-photos-modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="photosViewContent"><i class="fas fa-spinner fa-spin"></i> Loading...</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary btn-sm close-photos-modal">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="overlay toggle-icon"></div>
|
||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||
<?php include('include/footer.php'); ?>
|
||
</div>
|
||
<?php include('jsinclude.php'); ?>
|
||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||
<script>
|
||
$(document).ready(function() {
|
||
// Resolve SceltaMultipla IDs → Valore (cells + filter selects)
|
||
(function() {
|
||
const sceltaEls = document.querySelectorAll('.scelta-value');
|
||
const sceltaFilters = document.querySelectorAll('select.scelta-filter');
|
||
const fieldIds = new Set();
|
||
sceltaEls.forEach(el => { if (el.dataset.fieldId) fieldIds.add(el.dataset.fieldId); });
|
||
sceltaFilters.forEach(el => { if (el.dataset.fieldId) fieldIds.add(el.dataset.fieldId); });
|
||
if (!fieldIds.size) return;
|
||
|
||
$.getJSON('get_customfield_values.php', { field_ids: [...fieldIds].join(',') }, function(data) {
|
||
const lookup = {};
|
||
for (const [fid, values] of Object.entries(data)) {
|
||
lookup[fid] = {};
|
||
(values || []).forEach(v => {
|
||
lookup[fid][v.IdCustomFieldsValue] = v.Valore;
|
||
});
|
||
}
|
||
// Resolve cell values
|
||
sceltaEls.forEach(el => {
|
||
const fid = el.dataset.fieldId;
|
||
const raw = (el.textContent || '').trim();
|
||
if (fid && lookup[fid] && lookup[fid][raw]) {
|
||
el.textContent = lookup[fid][raw];
|
||
}
|
||
});
|
||
// Populate filter selects
|
||
sceltaFilters.forEach(sel => {
|
||
const fid = sel.dataset.fieldId;
|
||
if (!fid || !lookup[fid]) return;
|
||
const currentVal = sel.value;
|
||
// Sort by Valore
|
||
const sorted = Object.entries(lookup[fid]).sort((a, b) => a[1].localeCompare(b[1]));
|
||
sel.innerHTML = '<option value="">All</option>';
|
||
sorted.forEach(([id, label]) => {
|
||
const opt = document.createElement('option');
|
||
opt.value = id;
|
||
opt.textContent = label;
|
||
if (id === currentVal) opt.selected = true;
|
||
sel.appendChild(opt);
|
||
});
|
||
});
|
||
});
|
||
})();
|
||
|
||
// Load client names → resolve cells + populate filter selects
|
||
$.getJSON('get_clienti.php', function(data) {
|
||
const clients = data.value || [];
|
||
const map = {};
|
||
const sorted = [];
|
||
clients.forEach(c => {
|
||
const name = (c.Nominativo || '').trim();
|
||
map[c.IdCliente] = name;
|
||
sorted.push({ id: c.IdCliente, name: name });
|
||
});
|
||
sorted.sort((a, b) => a.name.localeCompare(b.name));
|
||
|
||
// Resolve cell values
|
||
document.querySelectorAll('.client-text').forEach(el => {
|
||
const id = el.getAttribute('data-client-id');
|
||
if (id && map[id]) el.textContent = map[id];
|
||
});
|
||
|
||
// Populate client filter selects
|
||
document.querySelectorAll('select.client-filter').forEach(sel => {
|
||
const currentVal = sel.value;
|
||
sel.innerHTML = '<option value="">All</option>';
|
||
sorted.forEach(c => {
|
||
const opt = document.createElement('option');
|
||
opt.value = c.id;
|
||
opt.textContent = c.name;
|
||
if (String(c.id) === currentVal) opt.selected = true;
|
||
sel.appendChild(opt);
|
||
});
|
||
});
|
||
});
|
||
|
||
// Modal close handlers
|
||
$(document).on('click', '.close-parts-modal', function() {
|
||
const modal = bootstrap.Modal.getInstance(document.getElementById('partsViewModal'));
|
||
if (modal) modal.hide();
|
||
});
|
||
$(document).on('click', '.close-photos-modal', function() {
|
||
const modal = bootstrap.Modal.getInstance(document.getElementById('photosViewModal'));
|
||
if (modal) modal.hide();
|
||
});
|
||
$(document).on('hidden.bs.modal', '#partsViewModal, #photosViewModal', function() {
|
||
$('.modal-backdrop').remove();
|
||
$('body').removeClass('modal-open').css('padding-right', '');
|
||
});
|
||
|
||
// Parts viewer
|
||
$(document).on('click', '.parts-btn', function() {
|
||
const iddatadb = $(this).data('iddatadb');
|
||
$('#partsViewContent').html('<i class="fas fa-spinner fa-spin"></i> Loading...');
|
||
new bootstrap.Modal(document.getElementById('partsViewModal')).show();
|
||
|
||
$.getJSON('load_parts.php', { iddatadb: iddatadb }, function(data) {
|
||
if (!data.parts || data.parts.length === 0) {
|
||
$('#partsViewContent').html('<p class="text-muted">No parts found.</p>');
|
||
return;
|
||
}
|
||
let html = '<table class="table table-sm table-bordered"><thead><tr><th>#</th><th>Description</th><th>Material</th><th>Color</th><th>Matrice</th><th>Note</th></tr></thead><tbody>';
|
||
data.parts.forEach(p => {
|
||
html += '<tr><td>' + (p.part_number||'') + '</td><td>' + (p.part_description||'') + '</td><td>' + (p.material||'') + '</td><td>' + (p.color||'') + '</td><td>' + (p.idmatrice||'') + '</td><td>' + (p.note||'') + '</td></tr>';
|
||
});
|
||
html += '</tbody></table>';
|
||
$('#partsViewContent').html(html);
|
||
}).fail(function() {
|
||
$('#partsViewContent').html('<p class="text-danger">Error loading parts.</p>');
|
||
});
|
||
});
|
||
|
||
// Photos viewer
|
||
$(document).on('click', '.photos-btn', function() {
|
||
const iddatadb = $(this).data('iddatadb');
|
||
$('#photosViewContent').html('<i class="fas fa-spinner fa-spin"></i> Loading...');
|
||
new bootstrap.Modal(document.getElementById('photosViewModal')).show();
|
||
|
||
$.getJSON('load_photo.php', { iddatadb: iddatadb }, function(data) {
|
||
if (!data.success || !data.photos || data.photos.length === 0) {
|
||
$('#photosViewContent').html('<p class="text-muted">No photos found.</p>');
|
||
return;
|
||
}
|
||
let html = '<div class="d-flex flex-wrap gap-2">';
|
||
data.photos.forEach(src => {
|
||
html += '<div style="text-align:center;"><a href="' + src + '" target="_blank"><img src="' + src + '" style="max-width:200px; max-height:200px; border:1px solid #ddd; border-radius:4px; padding:2px;"></a></div>';
|
||
});
|
||
html += '</div>';
|
||
$('#photosViewContent').html(html);
|
||
}).fail(function() {
|
||
$('#photosViewContent').html('<p class="text-danger">Error loading photos.</p>');
|
||
});
|
||
});
|
||
});
|
||
|
||
// ── Column filters ──
|
||
function clearOneFilter(btn) {
|
||
const wrap = btn.closest('.filter-wrap');
|
||
const el = wrap.querySelector('.grid-filter-input');
|
||
if (el) { el.value = ''; submitFilters(); }
|
||
}
|
||
function submitFilters() {
|
||
const filterRow = document.getElementById('gridFilterRow');
|
||
if (!filterRow) return;
|
||
const params = new URLSearchParams(window.location.search);
|
||
for (const key of [...params.keys()]) {
|
||
if (key.startsWith('f[') || key === 'page') params.delete(key);
|
||
}
|
||
filterRow.querySelectorAll('.grid-filter-input').forEach(el => {
|
||
const val = el.value.trim();
|
||
if (val) params.set(el.name, val);
|
||
});
|
||
window.location.href = 'tolims.php?' + params.toString();
|
||
}
|
||
(function() {
|
||
const filterRow = document.getElementById('gridFilterRow');
|
||
if (!filterRow) return;
|
||
filterRow.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Enter') { e.preventDefault(); submitFilters(); }
|
||
});
|
||
filterRow.querySelectorAll('.grid-filter-input').forEach(el => {
|
||
el.classList.toggle('has-value', el.value.trim() !== '');
|
||
el.addEventListener('input', () => el.classList.toggle('has-value', el.value.trim() !== ''));
|
||
el.addEventListener('change', () => el.classList.toggle('has-value', el.value.trim() !== ''));
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|