trf_certest/public/userarea/imported.php

1239 lines
42 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;
$show_all_users = isset($_GET['all_users']) && $_GET['all_users'] == '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;
// Fetch records with status='i' for this template
$userFilter = $show_all_users ? '' : 'AND d.user_id = ?';
$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
WHERE d.templateid = ? AND d.status = 'i' {$userFilter}
ORDER BY d.iddatadb DESC
");
$params = [$template_id];
if (!$show_all_users) $params[] = $user_id;
$stmt->execute($params);
$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
];
// ClienteFornitore rendered as standalone column, exclude from fixed fields
$excludeFromFixed = ['ClienteFornitore'];
$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;
}
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
$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',
];
// 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 gridData (JSON) — all row data for client-side rendering
// ────────────────────────────────────────────────────────────
$gridDataArray = [];
foreach ($importedData as $index => $row) {
$rowObj = [
'iddatadb' => (int)$row['iddatadb'],
'status' => $row['status'] ?? 'i',
'idclient' => $row['idclient'] ?? $default_idclient,
'cliente_fornitore_id' => $row['cliente_fornitore_id'] ?? null,
'commessaweb' => $row['commessaweb'] ?? null,
'user_name' => $row['user_name'] ?? '',
'importreferencecode' => $row['importreferencecode'] ?? '',
'filename_import' => $row['filename_import'] ?? '',
'importdate' => $row['importdate'] ?? '',
];
// Fixed fields
$rowObj['fixedFields'] = [];
foreach ($fixedFields as $f) {
$key = $f['fixed_field_key'];
$dbCol = $fixedAliasMap[$key] ?? $key;
$val = $row[$dbCol] ?? '';
if ($val === '' || $val === null) {
$val = fixedDefaultValue($f);
}
$rowObj['fixedFields'][$key] = (string)$val;
}
// Detail fields (from import_data_details)
$rowObj['details'] = [];
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
foreach ($rowDetails as $d) {
$rowObj['details'][(string)$d['mapping_id']] = $d['field_value'] ?? '';
}
// Main field value
if ($mainFieldMapping) {
$mainDetail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mainFieldMapping['id']);
$mainDetail = reset($mainDetail) ?: ['field_value' => $mainFieldMapping['manual_default'] ?? ''];
$rowObj['mainFieldValue'] = $mainDetail['field_value'] ?? $mainFieldMapping['manual_default'] ?? '';
}
$rowObj['_dirty'] = false;
$gridDataArray[] = $rowObj;
}
// Build columns in display order
$gridColumns = [];
// 1. Main field
if ($mainFieldMapping) {
$gridColumns[] = [
'type' => 'main_field',
'key' => (string)$mainFieldMapping['id'],
'label' => $mainFieldMapping['field_label'],
'dataType' => $mainFieldMapping['data_type'],
'isManual' => (bool)$mainFieldMapping['is_manual'],
'isRequired' => (bool)$mainFieldMapping['is_required'],
'fieldId' => $mainFieldMapping['field_id'] ?? null,
'width' => 150,
];
}
// 2. Status
$gridColumns[] = ['type' => 'status', 'key' => 'status', 'label' => 'Status', 'width' => 150, 'editable' => false];
// 3. Client
$gridColumns[] = ['type' => 'idclient', 'key' => 'idclient', 'label' => 'Client', 'width' => 300];
// 4. Cliente Fornitore
$gridColumns[] = ['type' => 'cliente_fornitore_id', 'key' => 'cliente_fornitore_id', 'label' => $slugMapping['ClienteFornitore'] ?? 'ClienteFornitore', 'width' => 300];
// 5. Auto fields
foreach ($allMappings as $mapping) {
if (!$mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
$gridColumns[] = [
'type' => 'detail',
'key' => (string)$mapping['id'],
'label' => $mapping['field_label'],
'dataType' => $mapping['data_type'],
'isManual' => false,
'isRequired' => (bool)$mapping['is_required'],
'fieldId' => $mapping['field_id'] ?? null,
'autoValue' => $mapping['auto_value'] ?? 'none',
'width' => 150,
];
}
}
// 6. Manual fields
foreach ($allMappings as $mapping) {
if ($mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
$gridColumns[] = [
'type' => 'detail',
'key' => (string)$mapping['id'],
'label' => $mapping['field_label'],
'dataType' => $mapping['data_type'],
'isManual' => true,
'isRequired' => (bool)$mapping['is_required'],
'fieldId' => $mapping['field_id'] ?? null,
'manualDefault' => $mapping['manual_default'] ?? '',
'width' => 150,
];
}
}
// 7. Tested Component
$gridColumns[] = ['type' => 'tested_component', 'key' => 'tested_component', 'label' => 'Tested Component', 'width' => 150];
// 8. AWB
$gridColumns[] = ['type' => 'awb', 'key' => 'awb', 'label' => 'AWB Number', 'width' => 200];
// 9. Tracking
$gridColumns[] = ['type' => 'tracking', 'key' => 'tracking', 'label' => 'Tracking Info', 'width' => 250];
// 10. Fixed fields (with importreferencecode/filename/importdate after ConsegnaRichiesta)
foreach ($fixedFields as $f) {
$key = $f['fixed_field_key'];
$label = $slugMapping[$key] ?? $key;
$gridColumns[] = [
'type' => 'fixed',
'key' => $key,
'label' => $label,
'dataType' => $f['data_type'],
'isRequired' => (bool)$f['is_required'],
'width' => 180,
];
if ($key === 'ConsegnaRichiesta') {
$gridColumns[] = ['type' => 'static', 'key' => 'importreferencecode', 'label' => $slugMapping['importreferencecode'] ?? 'Import Reference Code', 'width' => 150, 'editable' => false];
$gridColumns[] = ['type' => 'static', 'key' => 'filename_import', 'label' => $slugMapping['filename_import'] ?? 'File', 'width' => 150, 'editable' => false];
$gridColumns[] = ['type' => 'static', 'key' => 'importdate', 'label' => $slugMapping['importdate'] ?? 'Import Date', 'width' => 150, 'editable' => false];
}
}
$gridMeta = [
'templateId' => $template_id,
'defaultIdclient' => $default_idclient,
'isAdmin' => Auth::user()->hasRole('Admin'),
'userId' => $user_id,
'fixedAliasMap' => $fixedAliasMap,
'slugMapping' => $slugMapping,
'timeLabels' => $timeLabels,
'columns' => $gridColumns,
'mainFieldMapping' => $mainFieldMapping,
'totalRows' => count($gridDataArray),
];
?>
<script>
window.gridData = <?= json_encode($gridDataArray, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
window.gridMeta = <?= json_encode($gridMeta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
</script>
<!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'); ?>
<!-- FontAwesome - ensure loaded from kit -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/all.min.css">
<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;
}
.grid-container input,
.grid-container select,
.grid-container 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: 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 220px;
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;
}
/* Custom photo modal (not Bootstrap) */
#photosModal {
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);
}
#photosModal > .modal-content {
background-color: #fff;
margin: 5% auto;
padding: 20px;
width: 80%;
max-width: 900px;
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 {
flex: 0 0 220px !important;
min-width: 220px !important;
}
.action-btn {
padding: 6px 10px;
margin-right: 5px;
border: none;
border-radius: 5px;
cursor: pointer;
box-sizing: border-box;
}
.action-btn i {
font-size: 14px;
margin: 0;
}
.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;
}
</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;">
<input type="checkbox" class="form-check-input" id="showAllUsers" <?= $show_all_users ? 'checked' : '' ?>
onchange="window.location.href='imported.php?id=<?= $template_id ?>' + (this.checked ? '&all_users=1' : '')">
Show all users
</label>
</span>
<span class="text-muted" style="font-size: 12px;">(<?= count($importedData) ?> records<?= !$show_all_users ? ' — my records only' : '' ?>)</span>
</div>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center" style="min-height: 42px; gap: 12px;">
<div class="dropdown actions-dropdown" style="flex-shrink: 0;">
<button class="dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-cogs"></i> Actions
</button>
<ul class="dropdown-menu dropdown-menu-end">
<?php if ((Auth::user()->hasRole('Admin'))) : ?>
<li><a class="dropdown-item export-all-lims-btn" href="#"><i class="fas fa-upload" style="color: #eb0b0b;"></i>Export All</a></li>
<?php endif; ?>
<li><a class="dropdown-item save-all-btn" href="#"><i class="fas fa-save" style="color: #28a745;"></i>Save All</a></li>
</ul>
</div>
<button type="button" class="btn btn-outline-success btn-sm" id="addRowBtn" style="flex-shrink:0;"><i class="fas fa-plus"></i> Add Row</button>
<div id="unsavedChanges" style="display:none; color: red; font-weight: bold; font-size: 13px;">
⚠️ Unsaved changes detected!
<span id="changedRows" style="font-weight:normal; color:darkred;"></span>
</div>
<div id="batchExportBar" style="display:none; flex: 1; min-width: 0;">
<div class="d-flex align-items-center gap-2">
<i class="fas fa-spinner fa-spin" style="color: #eb0b0b;"></i>
<span id="batchExportStatus" style="font-size: 13px; font-weight: 500;">Esportazione...</span>
<button type="button" class="btn btn-outline-danger btn-sm" id="exportBatchCancelBtn" style="padding: 2px 10px; font-size: 12px;">Annulla</button>
</div>
</div>
</div>
</div>
<div class="card-body">
<form id="editForm">
<div class="grid-container">
<div class="grid-top" id="gridTopContainer"></div>
<div class="grid-row" id="gridHeaderContainer" style="font-weight:600;"></div>
<div id="gridRowContainer"></div>
</div>
</form>
<div id="partsModalContainer"></div>
<div id="annotationsModalContainer"></div>
<?php include 'photos_functions.php'; ?>
</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>
<style>.btn i { margin-top: 0 !important; margin-bottom: 0 !important; }</style>
<?php include('jsinclude.php'); ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="tracking.js"></script>
<script src="gridRenderer.js"></script>
<script src="saveAll.js"></script>
<script src="exportLims_gridData.js"></script>
<script src="modals_gridData.js"></script>
<script src="photos.js"></script>
<script src="annotationsModal.js"></script>
<script src="partsTable.js"></script>
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exportConfirmModalLabel">Conferma Esportazione</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p id="exportConfirmMessage">Confermi l'esportazione al LIMS per iddatadb <span id="exportIddatadb"></span>?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-primary btn-sm" id="exportConfirmBtn">Conferma</button>
</div>
</div>
</div>
</div>
<!-- Modale di risposta per l'esportazione -->
<div class="modal fade" id="exportResponseModal" tabindex="-1" aria-labelledby="exportResponseModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exportResponseModalLabel">Risultato Esportazione</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p id="exportResponseMessage"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="modal">Chiudi</button>
</div>
</div>
</div>
</div>
<!-- Modal: unsaved changes before export -->
<div class="modal fade" id="exportUnsavedModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modifiche non salvate</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
La riga contiene modifiche non salvate. Salvare prima di procedere con l'esportazione?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-primary btn-sm" id="saveAndExportBtn">Salva ed Esporta</button>
</div>
</div>
</div>
</div>
<!-- Modal: batch export confirm -->
<div class="modal fade" id="exportBatchConfirmModal" tabindex="-1" aria-labelledby="exportBatchConfirmModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exportBatchConfirmModalLabel">Conferma Export All</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Verranno esportate <strong><span id="exportBatchCount"></span></strong> righe al LIMS, una alla volta dall'alto verso il basso.<br>
Potrai annullare il batch in qualsiasi momento.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-danger btn-sm" id="exportBatchConfirmBtn"><i class="fas fa-upload"></i> Avvia Export</button>
</div>
</div>
</div>
</div>
<!-- Modal: batch export unsaved changes -->
<div class="modal fade" id="exportBatchUnsavedModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modifiche non salvate</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
Ci sono modifiche non salvate. Salvare tutte le righe prima di procedere con l'esportazione?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-primary btn-sm" id="batchSaveAndExportBtn"><i class="fas fa-save"></i> Salva ed Esporta</button>
</div>
</div>
</div>
</div>
<!-- Modal: Save All confirm -->
<!-- Delete Confirm Modal -->
<div class="modal fade" id="deleteConfirmModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm Delete</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
Delete record <strong id="deleteIddatadbText"></strong>? This cannot be undone.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-sm" id="deleteConfirmBtn"><i class="fas fa-trash"></i> Delete</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="saveAllConfirmModal" tabindex="-1" aria-labelledby="saveAllConfirmModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="saveAllConfirmModalLabel">Conferma Salvataggio</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Confermi il salvataggio di tutte le righe?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-success btn-sm" id="saveAllConfirmBtn"><i class="fas fa-save"></i> Conferma</button>
</div>
</div>
</div>
</div>
<!-- Modal: Save All result -->
<div class="modal fade" id="saveAllResultModal" tabindex="-1" aria-labelledby="saveAllResultModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="saveAllResultModalLabel">Risultato Salvataggio</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p id="saveAllResultMessage"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="modal">Chiudi</button>
</div>
</div>
</div>
</div>
<!-- Toast: row saved successfully -->
<div class="position-fixed bottom-0 end-0 p-3" style="z-index:9999">
<div id="saveSuccessToast" class="toast align-items-center text-bg-success border-0" role="alert" data-bs-delay="2500">
<div class="d-flex">
<div class="toast-body"><i class="fas fa-check-circle me-2"></i>Riga salvata con successo.</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
</div>
</div>
</body>
</html>