Compare commits
No commits in common. "main" and "feature/milestone3" have entirely different histories.
main
...
feature/mi
4
.gitignore
vendored
4
.gitignore
vendored
@ -54,6 +54,4 @@ yarn-error.log
|
||||
/public/photostrf/qrcodes/
|
||||
|
||||
# Ignora tutti i log ovunque
|
||||
*.log
|
||||
|
||||
public/userarea/cache/
|
||||
*.log
|
||||
@ -1,131 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$sourceIddatadb = isset($data['source_iddatadb']) ? (int)$data['source_iddatadb'] : 0;
|
||||
$targetList = $data['target_iddatadb_list'] ?? [];
|
||||
|
||||
$targetIds = array_values(array_unique(array_filter(array_map('intval', (array)$targetList), function ($v) use ($sourceIddatadb) {
|
||||
return $v > 0 && $v !== $sourceIddatadb;
|
||||
})));
|
||||
|
||||
if ($sourceIddatadb <= 0 || empty($targetIds)) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Missing source or target records'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// 1. Load source parts
|
||||
$stmtParts = $pdo->prepare("
|
||||
SELECT id, part_number, part_description, mix, idmatrice, note, dateexpiry
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = ?
|
||||
ORDER BY part_number ASC, id ASC
|
||||
");
|
||||
$stmtParts->execute([$sourceIddatadb]);
|
||||
$sourceParts = $stmtParts->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($sourceParts)) {
|
||||
$pdo->rollBack();
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'No parts found for source record'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. Prepare statements
|
||||
$stmtInsertPart = $pdo->prepare("
|
||||
INSERT INTO identification_parts
|
||||
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
|
||||
VALUES
|
||||
(:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())
|
||||
");
|
||||
|
||||
$stmtLoadCF = $pdo->prepare("
|
||||
SELECT field_id, value_id, value_text
|
||||
FROM identification_parts_customfields
|
||||
WHERE part_id = ?
|
||||
ORDER BY id ASC
|
||||
");
|
||||
|
||||
$stmtInsertCF = $pdo->prepare("
|
||||
INSERT INTO identification_parts_customfields
|
||||
(part_id, field_id, value_id, value_text, created_at, updated_at)
|
||||
VALUES
|
||||
(:part_id, :field_id, :value_id, :value_text, NOW(), NOW())
|
||||
");
|
||||
|
||||
$details = [];
|
||||
$totalClonedParts = 0;
|
||||
|
||||
// 3. Clone source parts to each target record
|
||||
foreach ($targetIds as $targetIddatadb) {
|
||||
$clonedCountForTarget = 0;
|
||||
|
||||
foreach ($sourceParts as $part) {
|
||||
$stmtInsertPart->execute([
|
||||
':iddatadb' => $targetIddatadb,
|
||||
':part_number' => $part['part_number'],
|
||||
':part_description' => $part['part_description'],
|
||||
':mix' => $part['mix'] ?? 'N',
|
||||
':idmatrice' => $part['idmatrice'] !== '' ? $part['idmatrice'] : null,
|
||||
':note' => $part['note'] ?? null,
|
||||
':dateexpiry' => $part['dateexpiry'] ?? null,
|
||||
]);
|
||||
|
||||
$newPartId = (int)$pdo->lastInsertId();
|
||||
|
||||
// Load source custom fields for this part
|
||||
$stmtLoadCF->execute([(int)$part['id']]);
|
||||
$customFields = $stmtLoadCF->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($customFields as $cf) {
|
||||
$stmtInsertCF->execute([
|
||||
':part_id' => $newPartId,
|
||||
':field_id' => (int)$cf['field_id'],
|
||||
':value_id' => ($cf['value_id'] !== null && $cf['value_id'] !== '') ? (int)$cf['value_id'] : null,
|
||||
':value_text' => $cf['value_text'] !== '' ? $cf['value_text'] : null,
|
||||
]);
|
||||
}
|
||||
|
||||
$clonedCountForTarget++;
|
||||
$totalClonedParts++;
|
||||
}
|
||||
|
||||
$details[] = [
|
||||
'target_iddatadb' => $targetIddatadb,
|
||||
'cloned_parts' => $clonedCountForTarget
|
||||
];
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'source_iddatadb' => $sourceIddatadb,
|
||||
'cloned_targets' => count($targetIds),
|
||||
'total_cloned_parts' => $totalClonedParts,
|
||||
'details' => $details
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Clone failed: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@ -542,23 +542,7 @@ try {
|
||||
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
||||
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
|
||||
$writeLog($logFileStep10, $logContentStep10, "STEP 10 - GET verify (commessa={$commessaId})");
|
||||
// 🔹 STEP 10.1: Save final CodiceCommessa into datadb.commessaweb
|
||||
// After ImportaCommessa, the API returns the final LIMS job code in CodiceCommessa.
|
||||
// Example: CodiceCommessa = 2614795, CodiceCommessaWeb = 26C0103.
|
||||
$finalCodiceCommessa = trim((string)($commessaAfterPatch['CodiceCommessa'] ?? ''));
|
||||
|
||||
if ($finalCodiceCommessa !== '') {
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE datadb
|
||||
SET commessaweb = :commessaweb,
|
||||
status = 'l'
|
||||
WHERE iddatadb = :iddatadb
|
||||
");
|
||||
$stmt->execute([
|
||||
'commessaweb' => substr($finalCodiceCommessa, 0, 30),
|
||||
'iddatadb' => $iddatadb
|
||||
]);
|
||||
}
|
||||
// 🔹 STEP 11: Prepare final response
|
||||
$finalCommessa = [
|
||||
"Cliente" => $clienteId,
|
||||
@ -573,7 +557,7 @@ try {
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"idcommessaweb" => $commessaId,
|
||||
"commessaweb" => $finalCodiceCommessa ?: $commessaWebCode,
|
||||
"commessaweb" => $commessaWebCode,
|
||||
"commessaWeb" => $finalCommessa,
|
||||
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||
"totalCampioni" => count($campioni),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,125 +10,65 @@
|
||||
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
|
||||
<style>
|
||||
/* Main buttons container */
|
||||
.template-buttons {
|
||||
/* Layout flessibile per gestire dimensioni diverse */
|
||||
#templateButtons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
justify-content: flex-start;
|
||||
padding: 10px 0;
|
||||
/* Allinea a sinistra */
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Button sizes */
|
||||
/* Definizione delle dimensioni */
|
||||
/* Definizione delle dimensioni */
|
||||
.btn-small {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
min-width: 100px;
|
||||
min-height: 36px;
|
||||
display: inline-flex;
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
/* Aggiunto */
|
||||
}
|
||||
|
||||
.btn-medium {
|
||||
font-size: 16px;
|
||||
padding: 10px 20px;
|
||||
min-width: 140px;
|
||||
min-height: 48px;
|
||||
display: inline-flex;
|
||||
min-width: 130px;
|
||||
min-height: 45px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
/* Aggiunto */
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
font-size: 20px;
|
||||
padding: 14px 28px;
|
||||
min-width: 190px;
|
||||
min-height: 64px;
|
||||
display: inline-flex;
|
||||
min-width: 180px;
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
/* Aggiunto */
|
||||
}
|
||||
|
||||
.template-icon {
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.btn-large .template-icon {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.btn-small .template-icon {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* Search box */
|
||||
/* Stile della barra di ricerca */
|
||||
#searchInput {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
margin-bottom: 18px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#searchInput:focus {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 0 0 0.15rem rgba(13, 110, 253, 0.15);
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.custom-tabs {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
margin-bottom: 20px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.custom-tabs .nav-link {
|
||||
border: none;
|
||||
border-radius: 10px 10px 0 0;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 18px;
|
||||
}
|
||||
|
||||
.custom-tabs .nav-link:hover {
|
||||
background: #f8f9fa;
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
.custom-tabs .nav-link.active {
|
||||
background: #0d6efd;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
min-height: 140px;
|
||||
}
|
||||
|
||||
.empty-message {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.loading-message {
|
||||
color: #6c757d;
|
||||
padding: 10px 0;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@ -147,54 +87,14 @@
|
||||
<h6 class="mb-0">Active Templates</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Barra di ricerca -->
|
||||
<input type="text" id="searchInput" placeholder="Search template...">
|
||||
|
||||
<ul class="nav nav-tabs custom-tabs" id="templateTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="xls-tab" data-bs-toggle="tab" data-bs-target="#xls-pane" type="button" role="tab" aria-controls="xls-pane" aria-selected="true">
|
||||
<i class="bx bx-spreadsheet template-icon"></i>
|
||||
XLS
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="api-tab" data-bs-toggle="tab" data-bs-target="#api-pane" type="button" role="tab" aria-controls="api-pane" aria-selected="false">
|
||||
<i class="bx bx-transfer-alt template-icon"></i>
|
||||
JSON/API
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="pdf-tab" data-bs-toggle="tab" data-bs-target="#pdf-pane" type="button" role="tab" aria-controls="pdf-pane" aria-selected="false">
|
||||
<i class="bx bx-file-pdf template-icon"></i>
|
||||
PDF
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="templateTabsContent">
|
||||
<div class="tab-pane fade show active" id="xls-pane" role="tabpanel" aria-labelledby="xls-tab">
|
||||
<div id="templateButtonsXLS" class="template-buttons">
|
||||
<div class="loading-message">Loading XLS templates...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="api-pane" role="tabpanel" aria-labelledby="api-tab">
|
||||
<div id="templateButtonsAPI" class="template-buttons">
|
||||
<div class="loading-message">Loading API templates...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pdf-pane" role="tabpanel" aria-labelledby="pdf-tab">
|
||||
<div id="templateButtonsPDF" class="template-buttons">
|
||||
<div class="loading-message">Loading PDF templates...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="templateButtons"></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'); ?>
|
||||
@ -204,137 +104,46 @@
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const allTemplates = [];
|
||||
|
||||
const containers = {
|
||||
XLS: document.getElementById("templateButtonsXLS"),
|
||||
API: document.getElementById("templateButtonsAPI"),
|
||||
PDF: document.getElementById("templateButtonsPDF")
|
||||
};
|
||||
|
||||
function getSizeClass(buttonSize) {
|
||||
if (buttonSize === "small") return "btn-small";
|
||||
if (buttonSize === "large") return "btn-large";
|
||||
return "btn-medium";
|
||||
}
|
||||
|
||||
function getTemplateIcon(sourceType) {
|
||||
switch ((sourceType || '').toUpperCase()) {
|
||||
case 'XLS':
|
||||
return 'bx bx-spreadsheet';
|
||||
case 'API':
|
||||
return 'bx bx-transfer-alt';
|
||||
case 'PDF':
|
||||
return 'bx bx-file-pdf';
|
||||
default:
|
||||
return 'bx bx-file';
|
||||
}
|
||||
}
|
||||
|
||||
function createButton(template) {
|
||||
const sizeClass = getSizeClass(template.button_size);
|
||||
const sourceType = (template.source_type || '').toUpperCase();
|
||||
const iconClass = getTemplateIcon(sourceType);
|
||||
|
||||
const btn = document.createElement("a");
|
||||
btn.href = `import_xls2.php?id=${template.id}`;
|
||||
btn.className = `btn ${sizeClass}`;
|
||||
btn.style.backgroundColor = template.button_bg_color || '#0d6efd';
|
||||
btn.style.color = template.button_text_color || '#ffffff';
|
||||
btn.setAttribute("data-label", (template.button_label || '').toLowerCase());
|
||||
btn.setAttribute("data-source-type", sourceType);
|
||||
|
||||
btn.innerHTML = `
|
||||
<i class="${iconClass} template-icon"></i>
|
||||
<span>${escapeHtml(template.button_label || 'Unnamed')}</span>
|
||||
`;
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function clearContainers() {
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = '';
|
||||
});
|
||||
}
|
||||
|
||||
function renderTemplates(searchValue = '') {
|
||||
clearContainers();
|
||||
|
||||
const grouped = {
|
||||
XLS: [],
|
||||
API: [],
|
||||
PDF: []
|
||||
};
|
||||
|
||||
allTemplates.forEach(template => {
|
||||
const sourceType = (template.source_type || '').toUpperCase();
|
||||
const label = (template.button_label || '').toLowerCase();
|
||||
|
||||
if (searchValue && !label.includes(searchValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (grouped[sourceType]) {
|
||||
grouped[sourceType].push(template);
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(grouped).forEach(type => {
|
||||
const container = containers[type];
|
||||
if (!container) return;
|
||||
|
||||
if (grouped[type].length === 0) {
|
||||
container.innerHTML = `<div class="empty-message">No templates found in this section.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
grouped[type].forEach(template => {
|
||||
container.appendChild(createButton(template));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fetch("load_active_templates.php")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("Error loading templates:", data.message);
|
||||
clearContainers();
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = `<div class="empty-message">Error loading templates.</div>`;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.data)) {
|
||||
clearContainers();
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = `<div class="empty-message">Invalid response format.</div>`;
|
||||
});
|
||||
return;
|
||||
}
|
||||
let templateButtons = document.getElementById("templateButtons");
|
||||
templateButtons.innerHTML = '';
|
||||
|
||||
allTemplates.push(...data.data);
|
||||
renderTemplates();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Fetch error:", error);
|
||||
clearContainers();
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = `<div class="empty-message">Fetch error while loading templates.</div>`;
|
||||
data.data.forEach(template => {
|
||||
let sizeClass = "btn-medium"; // Default
|
||||
if (template.button_size === "small") sizeClass = "btn-small";
|
||||
if (template.button_size === "large") sizeClass = "btn-large";
|
||||
|
||||
let btn = document.createElement("a");
|
||||
btn.href = `import_xls2.php?id=${template.id}`;
|
||||
btn.className = `btn ${sizeClass}`;
|
||||
btn.style.backgroundColor = template.button_bg_color;
|
||||
btn.style.color = template.button_text_color;
|
||||
btn.textContent = template.button_label;
|
||||
btn.setAttribute("data-label", template.button_label.toLowerCase()); // Attributo per ricerca
|
||||
|
||||
templateButtons.appendChild(btn);
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(error => console.error("Fetch error:", error));
|
||||
|
||||
// Funzione per la ricerca live
|
||||
document.getElementById("searchInput").addEventListener("input", function() {
|
||||
const searchValue = this.value.toLowerCase().trim();
|
||||
renderTemplates(searchValue);
|
||||
let searchValue = this.value.toLowerCase();
|
||||
document.querySelectorAll("#templateButtons a").forEach(btn => {
|
||||
let label = btn.getAttribute("data-label");
|
||||
if (label.includes(searchValue)) {
|
||||
btn.style.display = "inline-block";
|
||||
} else {
|
||||
btn.style.display = "none";
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -40,22 +40,6 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.top-scrollbar {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
background: #f8f9fa;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.top-scrollbar-inner {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
@ -340,12 +324,8 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
||||
</div>
|
||||
|
||||
<div class="top-scrollbar" id="topTableScrollbar">
|
||||
<div class="top-scrollbar-inner" id="topTableScrollbarInner"></div>
|
||||
</div>
|
||||
|
||||
<div class="table-container" id="mainTableContainer">
|
||||
<table class="table table-striped table-bordered" id="importPreviewTable">
|
||||
<div class="table-container">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||
@ -383,47 +363,6 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
`;
|
||||
tableContainer.innerHTML = html;
|
||||
|
||||
const topTableScrollbar = document.getElementById('topTableScrollbar');
|
||||
const topTableScrollbarInner = document.getElementById('topTableScrollbarInner');
|
||||
const mainTableContainer = document.getElementById('mainTableContainer');
|
||||
const importPreviewTable = document.getElementById('importPreviewTable');
|
||||
|
||||
function updateTopTableScrollbar() {
|
||||
if (!topTableScrollbar || !topTableScrollbarInner || !mainTableContainer || !importPreviewTable) return;
|
||||
|
||||
topTableScrollbarInner.style.width = importPreviewTable.scrollWidth + 'px';
|
||||
|
||||
if (mainTableContainer.scrollWidth > mainTableContainer.clientWidth) {
|
||||
topTableScrollbar.style.display = 'block';
|
||||
} else {
|
||||
topTableScrollbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
let syncingTop = false;
|
||||
let syncingBottom = false;
|
||||
|
||||
if (topTableScrollbar && mainTableContainer) {
|
||||
topTableScrollbar.addEventListener('scroll', function() {
|
||||
if (syncingBottom) return;
|
||||
syncingTop = true;
|
||||
mainTableContainer.scrollLeft = topTableScrollbar.scrollLeft;
|
||||
syncingTop = false;
|
||||
});
|
||||
|
||||
mainTableContainer.addEventListener('scroll', function() {
|
||||
if (syncingTop) return;
|
||||
syncingBottom = true;
|
||||
topTableScrollbar.scrollLeft = mainTableContainer.scrollLeft;
|
||||
syncingBottom = false;
|
||||
});
|
||||
}
|
||||
|
||||
updateTopTableScrollbar();
|
||||
setTimeout(updateTopTableScrollbar, 100);
|
||||
setTimeout(updateTopTableScrollbar, 300);
|
||||
window.addEventListener('resize', updateTopTableScrollbar);
|
||||
|
||||
const proceedButtonTop = document.getElementById('proceedButtonTop');
|
||||
const proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
||||
const selectAllCheckbox = document.getElementById('selectAll');
|
||||
@ -554,4 +493,4 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@ -202,13 +202,13 @@ foreach ($importedData as $index => $row) {
|
||||
'status' => $row['status'] ?? 'i',
|
||||
'idclient' => $row['idclient'] ?? $default_idclient,
|
||||
'cliente_fornitore_id' => $row['cliente_fornitore_id'] ?? null,
|
||||
'tested_component' => $row['tested_component'] ?? '',
|
||||
'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) {
|
||||
@ -267,12 +267,7 @@ $gridColumns[] = ['type' => 'cliente_fornitore_id', 'key' => 'cliente_fornitore_
|
||||
|
||||
// 5. Auto fields
|
||||
foreach ($allMappings as $mapping) {
|
||||
if (
|
||||
!$mapping['is_manual']
|
||||
&& $mapping['main_field'] != 1
|
||||
&& $mapping['is_visible_import'] == 1
|
||||
&& trim((string)$mapping['field_label']) !== 'Tested Component:'
|
||||
) {
|
||||
if (!$mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
|
||||
$gridColumns[] = [
|
||||
'type' => 'detail',
|
||||
'key' => (string)$mapping['id'],
|
||||
@ -289,12 +284,7 @@ foreach ($allMappings as $mapping) {
|
||||
|
||||
// 6. Manual fields
|
||||
foreach ($allMappings as $mapping) {
|
||||
if (
|
||||
$mapping['is_manual']
|
||||
&& $mapping['main_field'] != 1
|
||||
&& $mapping['is_visible_import'] == 1
|
||||
&& trim((string)$mapping['field_label']) !== 'Tested Component:'
|
||||
) {
|
||||
if ($mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
|
||||
$gridColumns[] = [
|
||||
'type' => 'detail',
|
||||
'key' => (string)$mapping['id'],
|
||||
@ -352,8 +342,8 @@ $gridMeta = [
|
||||
|
||||
?>
|
||||
<script>
|
||||
window.gridData = <?= json_encode($gridDataArray, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
window.gridMeta = <?= json_encode($gridMeta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
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>
|
||||
@ -374,10 +364,20 @@ $gridMeta = [
|
||||
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 {
|
||||
@ -416,9 +416,17 @@ $gridMeta = [
|
||||
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;
|
||||
@ -455,21 +463,6 @@ $gridMeta = [
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-scrollbar {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.top-scrollbar-inner {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.grid-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -804,7 +797,7 @@ $gridMeta = [
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
#photosModal>.modal-content {
|
||||
#photosModal > .modal-content {
|
||||
background-color: #fff;
|
||||
margin: 5% auto;
|
||||
padding: 20px;
|
||||
@ -899,17 +892,9 @@ $gridMeta = [
|
||||
}
|
||||
|
||||
@keyframes new-row-pulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: #cce5ff;
|
||||
}
|
||||
0%, 100% { background-color: transparent; }
|
||||
50% { background-color: #cce5ff; }
|
||||
}
|
||||
|
||||
.row-just-created {
|
||||
animation: new-row-pulse 1s ease-in-out 3;
|
||||
}
|
||||
@ -1026,7 +1011,11 @@ $gridMeta = [
|
||||
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;
|
||||
}
|
||||
|
||||
/* ── Pagination bar ── */
|
||||
.pager-bar {
|
||||
@ -1040,25 +1029,21 @@ $gridMeta = [
|
||||
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;
|
||||
@ -1067,41 +1052,24 @@ $gridMeta = [
|
||||
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: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-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 {
|
||||
.pager-btn, .pager-num {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -1115,152 +1083,21 @@ $gridMeta = [
|
||||
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-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;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
FINAL GRID COLORS
|
||||
Schema/customfield fields = green
|
||||
Fixed/standard fields = white
|
||||
Required fields = red border only
|
||||
========================================================= */
|
||||
|
||||
/* Default: all fields white */
|
||||
.grid-container input,
|
||||
.grid-container select,
|
||||
.grid-container textarea,
|
||||
.grid-top input,
|
||||
.grid-top select,
|
||||
.grid-top textarea {
|
||||
background-color: #ffffff !important;
|
||||
color: #333 !important;
|
||||
}
|
||||
|
||||
/* Schema/customfield fields: green background */
|
||||
.grid-container .schema-field input,
|
||||
.grid-container .schema-field select,
|
||||
.grid-container .schema-field textarea,
|
||||
.grid-top .schema-field input,
|
||||
.grid-top .schema-field select,
|
||||
.grid-top .schema-field textarea {
|
||||
background-color: #dff3e3 !important;
|
||||
}
|
||||
|
||||
/* Fixed and standard fields: white background */
|
||||
.grid-container .fixed-field input,
|
||||
.grid-container .fixed-field select,
|
||||
.grid-container .fixed-field textarea,
|
||||
.grid-container .standard-field input,
|
||||
.grid-container .standard-field select,
|
||||
.grid-container .standard-field textarea,
|
||||
.grid-top .fixed-field input,
|
||||
.grid-top .fixed-field select,
|
||||
.grid-top .fixed-field textarea,
|
||||
.grid-top .standard-field input,
|
||||
.grid-top .standard-field select,
|
||||
.grid-top .standard-field textarea {
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* Required fields: red border only */
|
||||
.grid-container .required-field input,
|
||||
.grid-container .required-field select,
|
||||
.grid-container .required-field textarea,
|
||||
.grid-top .required-field input,
|
||||
.grid-top .required-field select,
|
||||
.grid-top .required-field textarea {
|
||||
border: 2px solid #dc3545 !important;
|
||||
box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.15) !important;
|
||||
}
|
||||
|
||||
/* Required schema/customfield fields: green + red border */
|
||||
.grid-container .schema-field.required-field input,
|
||||
.grid-container .schema-field.required-field select,
|
||||
.grid-container .schema-field.required-field textarea,
|
||||
.grid-top .schema-field.required-field input,
|
||||
.grid-top .schema-field.required-field select,
|
||||
.grid-top .schema-field.required-field textarea {
|
||||
background-color: #dff3e3 !important;
|
||||
border: 2px solid #dc3545 !important;
|
||||
}
|
||||
|
||||
/* Required fixed/standard fields: white + red border */
|
||||
.grid-container .fixed-field.required-field input,
|
||||
.grid-container .fixed-field.required-field select,
|
||||
.grid-container .fixed-field.required-field textarea,
|
||||
.grid-container .standard-field.required-field input,
|
||||
.grid-container .standard-field.required-field select,
|
||||
.grid-container .standard-field.required-field textarea,
|
||||
.grid-top .fixed-field.required-field input,
|
||||
.grid-top .fixed-field.required-field select,
|
||||
.grid-top .fixed-field.required-field textarea,
|
||||
.grid-top .standard-field.required-field input,
|
||||
.grid-top .standard-field.required-field select,
|
||||
.grid-top .standard-field.required-field textarea {
|
||||
background-color: #ffffff !important;
|
||||
border: 2px solid #dc3545 !important;
|
||||
}
|
||||
|
||||
/* Select2 - schema/customfield fields: green */
|
||||
.grid-container .schema-field .select2-container--default .select2-selection--single,
|
||||
.grid-top .schema-field .select2-container--default .select2-selection--single {
|
||||
background-color: #dff3e3 !important;
|
||||
}
|
||||
|
||||
/* Select2 - fixed/standard fields: white */
|
||||
.grid-container .fixed-field .select2-container--default .select2-selection--single,
|
||||
.grid-container .standard-field .select2-container--default .select2-selection--single,
|
||||
.grid-top .fixed-field .select2-container--default .select2-selection--single,
|
||||
.grid-top .standard-field .select2-container--default .select2-selection--single {
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* Select2 - required fields: red border only */
|
||||
.grid-container .required-field .select2-container--default .select2-selection--single,
|
||||
.grid-top .required-field .select2-container--default .select2-selection--single {
|
||||
border: 2px solid #dc3545 !important;
|
||||
box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.15) !important;
|
||||
}
|
||||
|
||||
/* Remove old red background from required classes */
|
||||
.grid-container input.required-input,
|
||||
.grid-container select.required-input,
|
||||
.grid-container textarea.required-input,
|
||||
.grid-top input.required-input,
|
||||
.grid-top select.required-input,
|
||||
.grid-top textarea.required-input {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
|
||||
/* Missing required cell: red outline only */
|
||||
.grid-cell.missing-required {
|
||||
background-color: inherit !important;
|
||||
border-right: 1px solid #dee2e6 !important;
|
||||
outline: 2px solid #dc3545 !important;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
</style>
|
||||
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
</head>
|
||||
@ -1275,20 +1112,20 @@ $gridMeta = [
|
||||
<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>
|
||||
<?php if ($importref === ''): ?>
|
||||
<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;">
|
||||
(<?= $usePagination ? count($importedData) . " of {$totalRows}" : count($importedData) ?> records<?= !$show_all_users ? ' — my records only' : '' ?>)
|
||||
</span>
|
||||
<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;">
|
||||
(<?= $usePagination ? count($importedData) . " of {$totalRows}" : count($importedData) ?> records<?= !$show_all_users ? ' — my records only' : '' ?>)
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($usePagination): ?>
|
||||
<?php
|
||||
<?php
|
||||
$baseQuery = $_GET;
|
||||
unset($baseQuery['limit'], $baseQuery['page']);
|
||||
$pageQuery = $_GET;
|
||||
@ -1296,43 +1133,43 @@ $gridMeta = [
|
||||
$pageBase = 'imported.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 = 'imported.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 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 = 'imported.php?' . http_build_query(array_merge($baseQuery, ['limit' => $lim]));
|
||||
?>
|
||||
<a href="<?= htmlspecialchars($url) ?>" class="pager-limit-btn <?= $isActive ? 'active' : '' ?>"><?= $lim ?></a>
|
||||
<?php endforeach; ?>
|
||||
</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>
|
||||
<?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>
|
||||
<?php endif; ?>
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
@ -1342,9 +1179,9 @@ $gridMeta = [
|
||||
<i class="fas fa-cogs"></i> Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
|
||||
<li><a class="dropdown-item export-all-lims-btn" href="#"><i class="fas fa-upload" style="color: #eb0b0b;"></i>Export All</a></li>
|
||||
|
||||
<?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>
|
||||
@ -1364,18 +1201,13 @@ $gridMeta = [
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="editForm">
|
||||
<div class="top-scrollbar" id="topScrollbar">
|
||||
<div class="top-scrollbar-inner" id="topScrollbarInner"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid-container" id="gridContainer">
|
||||
<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="analysisModalContainer"></div>
|
||||
<div id="annotationsModalContainer"></div>
|
||||
<?php include 'photos_functions.php'; ?>
|
||||
</div>
|
||||
@ -1386,12 +1218,7 @@ $gridMeta = [
|
||||
<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>
|
||||
<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>
|
||||
@ -1404,54 +1231,6 @@ $gridMeta = [
|
||||
<script src="photos.js"></script>
|
||||
<script src="annotationsModal.js"></script>
|
||||
<script src="partsTable.js"></script>
|
||||
<script src="analysisModal.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const topScrollbar = document.getElementById('topScrollbar');
|
||||
const topScrollbarInner = document.getElementById('topScrollbarInner');
|
||||
const gridContainer = document.getElementById('gridContainer');
|
||||
|
||||
if (!topScrollbar || !topScrollbarInner || !gridContainer) return;
|
||||
|
||||
let syncingFromTop = false;
|
||||
let syncingFromGrid = false;
|
||||
|
||||
function updateTopScrollbarWidth() {
|
||||
topScrollbarInner.style.width = gridContainer.scrollWidth + 'px';
|
||||
|
||||
// Mostra la barra solo se serve davvero
|
||||
if (gridContainer.scrollWidth > gridContainer.clientWidth) {
|
||||
topScrollbar.style.display = 'block';
|
||||
} else {
|
||||
topScrollbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
topScrollbar.addEventListener('scroll', function() {
|
||||
if (syncingFromGrid) return;
|
||||
syncingFromTop = true;
|
||||
gridContainer.scrollLeft = topScrollbar.scrollLeft;
|
||||
syncingFromTop = false;
|
||||
});
|
||||
|
||||
gridContainer.addEventListener('scroll', function() {
|
||||
if (syncingFromTop) return;
|
||||
syncingFromGrid = true;
|
||||
topScrollbar.scrollLeft = gridContainer.scrollLeft;
|
||||
syncingFromGrid = false;
|
||||
});
|
||||
|
||||
updateTopScrollbarWidth();
|
||||
|
||||
window.addEventListener('resize', updateTopScrollbarWidth);
|
||||
|
||||
// Ritarda un attimo per sicurezza, visto che la griglia viene renderizzata via JS
|
||||
setTimeout(updateTopScrollbarWidth, 200);
|
||||
setTimeout(updateTopScrollbarWidth, 600);
|
||||
setTimeout(updateTopScrollbarWidth, 1200);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
@ -1615,4 +1394,4 @@ $gridMeta = [
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@ -13,10 +13,7 @@ try {
|
||||
}
|
||||
|
||||
// Recupera solo i template attivi
|
||||
$stmt = $pdo->query("SELECT id, button_label, button_size, button_bg_color, button_text_color, source_type
|
||||
FROM excel_templates
|
||||
WHERE status = 'active'
|
||||
ORDER BY button_label ASC");
|
||||
$stmt = $pdo->query("SELECT id, button_label, button_bg_color, button_text_color, button_size FROM excel_templates WHERE status = 'active'");
|
||||
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$response["success"] = true;
|
||||
|
||||
@ -26,9 +26,7 @@
|
||||
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||
Rinumera Parti
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" id="clonePartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||
<i class="fas fa-clone"></i> Clona Parti
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||
<i class="fas fa-microphone"></i> Voce
|
||||
</button>
|
||||
|
||||
@ -2,111 +2,93 @@
|
||||
* modals_gridData.js — Photos, Parts, Tested Component handlers for gridData pages
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
// ── Photos — use photos.js loadPopupContent (exported to window) ──
|
||||
$(document).on("click", ".photos-btn", function () {
|
||||
const iddatadb = $(this).data("iddatadb") || null;
|
||||
const idquotations = $(this).data("idquotations") || null;
|
||||
const modal = document.getElementById("photosModal");
|
||||
$(document).on('click', '.photos-btn', function () {
|
||||
const iddatadb = $(this).data('iddatadb') || null;
|
||||
const idquotations = $(this).data('idquotations') || null;
|
||||
const modal = document.getElementById('photosModal');
|
||||
if (!modal) return;
|
||||
|
||||
modal.style.display = "block";
|
||||
modal.style.display = 'block';
|
||||
|
||||
if (typeof window.loadPopupContent === "function") {
|
||||
if (typeof window.loadPopupContent === 'function') {
|
||||
window.loadPopupContent(iddatadb, idquotations);
|
||||
}
|
||||
});
|
||||
|
||||
// Close photos modal
|
||||
$(document).on("click", ".close-btn", function () {
|
||||
const modal = document.getElementById("photosModal");
|
||||
if (modal) modal.style.display = "none";
|
||||
$(document).on('click', '.close-btn', function () {
|
||||
const modal = document.getElementById('photosModal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
});
|
||||
|
||||
// Close on backdrop click
|
||||
$(document).on("click", "#photosModal", function (e) {
|
||||
$(document).on('click', '#photosModal', function (e) {
|
||||
if (e.target === this) {
|
||||
this.style.display = "none";
|
||||
this.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// ── Parts (matching import_edit2.php behavior) ─────────────────────
|
||||
$(document).on("click", ".parts-btn", function () {
|
||||
const iddatadb = $(this).data("iddatadb") || null;
|
||||
const idquotations = $(this).data("idquotations") || null;
|
||||
$(document).on('click', '.parts-btn', function () {
|
||||
const iddatadb = $(this).data('iddatadb') || null;
|
||||
const idquotations = $(this).data('idquotations') || null;
|
||||
|
||||
$.ajax({
|
||||
url: "modal_partsTable.php",
|
||||
method: "GET",
|
||||
url: 'modal_partsTable.php',
|
||||
method: 'GET',
|
||||
data: { iddatadb: iddatadb },
|
||||
success: function (response) {
|
||||
$("#partsModalContainer").html(response);
|
||||
const modalElement = document.getElementById("partsModal");
|
||||
$('#partsModalContainer').html(response);
|
||||
const modalElement = document.getElementById('partsModal');
|
||||
if (!modalElement) return;
|
||||
|
||||
$("#trfHeader").text(iddatadb || idquotations || "");
|
||||
|
||||
const visibleIddatadbList = Array.isArray(window.gridData)
|
||||
? window.gridData
|
||||
.map((r) => parseInt(r.iddatadb, 10))
|
||||
.filter((v) => !isNaN(v) && v > 0)
|
||||
: [];
|
||||
|
||||
$("#partsModal")
|
||||
.data("iddatadb", iddatadb)
|
||||
.data("idquotations", idquotations)
|
||||
.data("visible-iddatadb-list", visibleIddatadbList);
|
||||
$("#trfHeader").text(iddatadb || idquotations || '');
|
||||
$("#partsModal").data("iddatadb", iddatadb).data("idquotations", idquotations);
|
||||
|
||||
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||
if (!modal)
|
||||
modal = new bootstrap.Modal(modalElement, {
|
||||
backdrop: true,
|
||||
keyboard: true,
|
||||
focus: true,
|
||||
});
|
||||
if (!modal) modal = new bootstrap.Modal(modalElement, { backdrop: true, keyboard: true, focus: true });
|
||||
modal.show();
|
||||
|
||||
if (typeof window.loadParts === "function") {
|
||||
if (typeof window.loadParts === 'function') {
|
||||
window.loadParts(iddatadb, idquotations);
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error("Error loading parts:", error);
|
||||
},
|
||||
console.error('Error loading parts:', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("hidden.bs.modal", "#partsModal", function () {
|
||||
const modalElement = document.getElementById("partsModal");
|
||||
$(document).on('hidden.bs.modal', '#partsModal', function () {
|
||||
const modalElement = document.getElementById('partsModal');
|
||||
if (modalElement) {
|
||||
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||
if (modal) modal.dispose();
|
||||
}
|
||||
$("#partsModalContainer").empty();
|
||||
$(".modal-backdrop").remove();
|
||||
$("body").removeClass("modal-open").css("padding-right", "");
|
||||
$('#partsModalContainer').empty();
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open').css('padding-right', '');
|
||||
});
|
||||
|
||||
// ── Tested Component quick add ───────────────────────────────────────
|
||||
$(document).on("click", ".add-part-btn", async function () {
|
||||
const iddatadb = $(this).data("iddatadb") || null;
|
||||
const rowIndex = parseInt($(this).data("row"));
|
||||
$(document).on('click', '.add-part-btn', async function () {
|
||||
const iddatadb = $(this).data('iddatadb') || null;
|
||||
const rowIndex = parseInt($(this).data('row'));
|
||||
const row = window.gridData?.[rowIndex];
|
||||
const id = iddatadb || (row ? row.iddatadb : null);
|
||||
if (!id) return;
|
||||
|
||||
const $cell = $(this).closest(".grid-cell, div");
|
||||
const $input = $cell.find("input");
|
||||
const raw = ($input.val() || "").trim();
|
||||
const parts = raw
|
||||
.split("|")
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => s.length > 0);
|
||||
const $cell = $(this).closest('.grid-cell, div');
|
||||
const $input = $cell.find('input');
|
||||
const raw = ($input.val() || '').trim();
|
||||
const parts = raw.split('|').map(s => s.trim()).filter(s => s.length > 0);
|
||||
const uniqueParts = [...new Set(parts)];
|
||||
|
||||
if (!uniqueParts.length) {
|
||||
alert("Insert a description first.");
|
||||
alert('Insert a description first.');
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
@ -114,23 +96,14 @@
|
||||
try {
|
||||
for (const p of uniqueParts) {
|
||||
const formData = new FormData();
|
||||
formData.append("iddatadb", id);
|
||||
formData.append("part_description", p);
|
||||
await fetch("add_part_quick.php", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
formData.append('iddatadb', id);
|
||||
formData.append('part_description', p);
|
||||
await fetch('add_part_quick.php', { method: 'POST', body: formData });
|
||||
}
|
||||
|
||||
if (row) {
|
||||
row.tested_component = raw;
|
||||
row._dirty = true;
|
||||
}
|
||||
|
||||
alert(`Added ${uniqueParts.length} part(s).`);
|
||||
$input.val(raw);
|
||||
$input.val('');
|
||||
} catch (e) {
|
||||
alert("Error: " + e.message);
|
||||
alert('Error: ' + e.message);
|
||||
}
|
||||
});
|
||||
|
||||
@ -138,47 +111,44 @@
|
||||
let deleteIddatadb = null;
|
||||
let deleteRowIndex = null;
|
||||
|
||||
$(document).on("click", ".delete-btn", function () {
|
||||
deleteIddatadb = $(this).data("iddatadb");
|
||||
deleteRowIndex = parseInt($(this).data("row"));
|
||||
const modalEl = document.getElementById("deleteConfirmModal");
|
||||
$(document).on('click', '.delete-btn', function () {
|
||||
deleteIddatadb = $(this).data('iddatadb');
|
||||
deleteRowIndex = parseInt($(this).data('row'));
|
||||
const modalEl = document.getElementById('deleteConfirmModal');
|
||||
if (modalEl) {
|
||||
document.getElementById("deleteIddatadbText").textContent =
|
||||
deleteIddatadb;
|
||||
document.getElementById('deleteIddatadbText').textContent = deleteIddatadb;
|
||||
new bootstrap.Modal(modalEl).show();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", "#deleteConfirmBtn", async function () {
|
||||
const modalEl = document.getElementById("deleteConfirmModal");
|
||||
$(document).on('click', '#deleteConfirmBtn', async function () {
|
||||
const modalEl = document.getElementById('deleteConfirmModal');
|
||||
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||
if (modal) modal.hide();
|
||||
|
||||
if (!deleteIddatadb) return;
|
||||
|
||||
try {
|
||||
const resp = await fetch("delete_record.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ id: deleteIddatadb }),
|
||||
const resp = await fetch('delete_record.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: deleteIddatadb })
|
||||
});
|
||||
const result = await resp.json();
|
||||
|
||||
if (result.success) {
|
||||
// Remove from gridData
|
||||
const idx = window.gridData.findIndex(
|
||||
(r) => r.iddatadb === deleteIddatadb,
|
||||
);
|
||||
const idx = window.gridData.findIndex(r => r.iddatadb === deleteIddatadb);
|
||||
if (idx >= 0) window.gridData.splice(idx, 1);
|
||||
|
||||
// Re-render
|
||||
const gr = window.gridRenderer;
|
||||
if (gr) gr.renderVisibleRows();
|
||||
} else {
|
||||
alert("Error: " + result.message);
|
||||
alert('Error: ' + result.message);
|
||||
}
|
||||
} catch (e) {
|
||||
alert("Error: " + e.message);
|
||||
alert('Error: ' + e.message);
|
||||
}
|
||||
|
||||
deleteIddatadb = null;
|
||||
@ -186,26 +156,20 @@
|
||||
});
|
||||
|
||||
// ── Add new row ──────────────────────────────────────────────────────
|
||||
$(document).on("click", "#addRowBtn", async function () {
|
||||
$(document).on('click', '#addRowBtn', async function () {
|
||||
const btn = this;
|
||||
btn.disabled = true;
|
||||
|
||||
try {
|
||||
const templateId = window.gridMeta?.templateId;
|
||||
if (!templateId) {
|
||||
alert("Template ID missing");
|
||||
return;
|
||||
}
|
||||
if (!templateId) { alert('Template ID missing'); return; }
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const importref = urlParams.get("importref") || "";
|
||||
const resp = await fetch("add_record.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
template_id: templateId,
|
||||
importreferencecode: importref,
|
||||
}),
|
||||
const importref = urlParams.get('importref') || '';
|
||||
const resp = await fetch('add_record.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ template_id: templateId, importreferencecode: importref })
|
||||
});
|
||||
const result = await resp.json();
|
||||
|
||||
@ -213,20 +177,17 @@
|
||||
// Build new row object
|
||||
const newRow = {
|
||||
iddatadb: result.iddatadb,
|
||||
status: "i",
|
||||
idclient: window.gridMeta?.defaultIdclient || "",
|
||||
status: 'i',
|
||||
idclient: window.gridMeta?.defaultIdclient || '',
|
||||
cliente_fornitore_id: null,
|
||||
commessaweb: null,
|
||||
user_name: result.user_name || "",
|
||||
importreferencecode: result.importreferencecode || "",
|
||||
filename_import: "",
|
||||
importdate: new Date()
|
||||
.toISOString()
|
||||
.slice(0, 19)
|
||||
.replace("T", " "),
|
||||
user_name: result.user_name || '',
|
||||
importreferencecode: result.importreferencecode || '',
|
||||
filename_import: '',
|
||||
importdate: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
||||
fixedFields: {},
|
||||
details: {},
|
||||
mainFieldValue: "",
|
||||
mainFieldValue: '',
|
||||
_dirty: false,
|
||||
};
|
||||
|
||||
@ -238,27 +199,20 @@
|
||||
if (gr) gr.renderVisibleRows();
|
||||
|
||||
// Highlight new row briefly
|
||||
const newGridRow = document.querySelector(
|
||||
`.grid-row[data-id="${result.iddatadb}"]`,
|
||||
);
|
||||
const newGridRow = document.querySelector(`.grid-row[data-id="${result.iddatadb}"]`);
|
||||
if (newGridRow) {
|
||||
newGridRow.classList.add("row-just-created");
|
||||
newGridRow.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
});
|
||||
setTimeout(
|
||||
() => newGridRow.classList.remove("row-just-created"),
|
||||
4000,
|
||||
);
|
||||
newGridRow.classList.add('row-just-created');
|
||||
newGridRow.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
setTimeout(() => newGridRow.classList.remove('row-just-created'), 4000);
|
||||
}
|
||||
} else {
|
||||
alert("Error: " + (result.message || "Unknown error"));
|
||||
alert('Error: ' + (result.message || 'Unknown error'));
|
||||
}
|
||||
} catch (e) {
|
||||
alert("Error: " + e.message);
|
||||
alert('Error: ' + e.message);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@ -1595,11 +1595,7 @@ $(document).ready(function () {
|
||||
dataType: "json",
|
||||
delay: 150,
|
||||
data: function (params) {
|
||||
return {
|
||||
q: params.term || "",
|
||||
limit: 20,
|
||||
macro: selectedMacro || "",
|
||||
};
|
||||
return { q: params.term || "", limit: 20, macro: selectedMacro || "" };
|
||||
},
|
||||
processResults: function (data) {
|
||||
return { results: data.results || [] };
|
||||
@ -1658,11 +1654,7 @@ $(document).ready(function () {
|
||||
dataType: "json",
|
||||
delay: 150,
|
||||
data: function (params) {
|
||||
return {
|
||||
q: params.term || "",
|
||||
limit: 20,
|
||||
macro: selectedMacro || "",
|
||||
};
|
||||
return { q: params.term || "", limit: 20, macro: selectedMacro || "" };
|
||||
},
|
||||
processResults: function (data) {
|
||||
return { results: data.results || [] };
|
||||
@ -1808,9 +1800,7 @@ $(document).ready(function () {
|
||||
$("#partsTableBody .part-matrice").each(function () {
|
||||
const $target = $(this);
|
||||
if (!$target.find(`option[value="${globalVal}"]`).length) {
|
||||
$target.append(
|
||||
new Option(globalText, globalVal, true, true),
|
||||
);
|
||||
$target.append(new Option(globalText, globalVal, true, true));
|
||||
}
|
||||
$target.val(globalVal).trigger("change");
|
||||
});
|
||||
@ -1979,109 +1969,7 @@ $(document).ready(function () {
|
||||
".add-row-global, .add-mix-global, .add-mix-row, .remove-row, .propagate-matrice-btn, .propagate-all-btn, .note-btn",
|
||||
markUnsaved,
|
||||
);
|
||||
$(document).on("click", "#clonePartsBtn", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const sourceIddatadb =
|
||||
parseInt($("#partsModal").data("iddatadb"), 10) || null;
|
||||
const visibleListRaw =
|
||||
$("#partsModal").data("visible-iddatadb-list") || [];
|
||||
|
||||
if (!sourceIddatadb) {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Source record not found.</div>',
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(() => {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
const targetIds = (Array.isArray(visibleListRaw) ? visibleListRaw : [])
|
||||
.map((v) => parseInt(v, 10))
|
||||
.filter((v) => !isNaN(v) && v > 0 && v !== sourceIddatadb);
|
||||
|
||||
if (!targetIds.length) {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-warning temp-alert" role="alert">No other visible records available for clone.</div>',
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(() => {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!confirm(
|
||||
`Confermi il clone delle parti del record ${sourceIddatadb} negli altri ${targetIds.length} record visibili?`,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $btn = $(this);
|
||||
const originalHtml = $btn.html();
|
||||
$btn.prop("disabled", true).html(
|
||||
'<i class="fas fa-spinner fa-spin"></i> Clonazione...',
|
||||
);
|
||||
|
||||
$.ajax({
|
||||
url: "clone_parts_to_visible.php",
|
||||
method: "POST",
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({
|
||||
source_iddatadb: sourceIddatadb,
|
||||
target_iddatadb_list: targetIds,
|
||||
}),
|
||||
success: function (response) {
|
||||
$btn.prop("disabled", false).html(originalHtml);
|
||||
|
||||
if (response.success) {
|
||||
const successMsg = $(
|
||||
`<div class="alert alert-success temp-alert" role="alert">
|
||||
Clone completed. Source parts copied to ${response.cloned_targets || 0} record(s), total cloned parts: ${response.total_cloned_parts || 0}.
|
||||
</div>`,
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(successMsg);
|
||||
setTimeout(() => {
|
||||
successMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
} else {
|
||||
const errorMsg = $(
|
||||
`<div class="alert alert-danger temp-alert" role="alert">Clone error: ${response.message || "Unknown error"}</div>`,
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(() => {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
$btn.prop("disabled", false).html(originalHtml);
|
||||
|
||||
const errorMsg = $(
|
||||
`<div class="alert alert-danger temp-alert" role="alert">Clone error: ${error} (${xhr.status})</div>`,
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(() => {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
},
|
||||
});
|
||||
});
|
||||
// Esporta la funzione loadParts per essere usata da import_Edit2.php
|
||||
window.loadParts = loadParts;
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ try {
|
||||
$iddatadb = intval($_POST['iddatadb']);
|
||||
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
|
||||
$clienteFornitoreId = isset($_POST['cliente_fornitore_id']) ? (is_numeric($_POST['cliente_fornitore_id']) ? intval($_POST['cliente_fornitore_id']) : null) : null;
|
||||
$testedComponent = isset($_POST['tested_component']) ? trim((string)$_POST['tested_component']) : null;
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
@ -144,11 +144,7 @@ try {
|
||||
$setParts[] = "cliente_fornitore_id = ?";
|
||||
$params[] = $clienteFornitoreId;
|
||||
}
|
||||
// 5a3) tested_component (se presente)
|
||||
if (isset($testedComponent)) {
|
||||
$setParts[] = "tested_component = ?";
|
||||
$params[] = ($testedComponent === '') ? null : $testedComponent;
|
||||
}
|
||||
|
||||
// 5b) fixed fields dal POST
|
||||
// QUI è il punto chiave: accettiamo SOLO colonne reali (realWhitelist),
|
||||
// ma se dal JS arrivassero ancora key logiche, funzionerebbe uguale
|
||||
|
||||
@ -653,7 +653,7 @@
|
||||
"IdSchemaCustomFields": 163,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Devred",
|
||||
"Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024 aggiornato il 23\/04\/2026\r\n\r\n"
|
||||
"Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 164,
|
||||
@ -760,7 +760,7 @@
|
||||
{
|
||||
"IdSchemaCustomFields": 182,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Ralph Lauren - All testing V.10",
|
||||
"Nome": "Ralph Lauren - All testing V.6",
|
||||
"Descrizione": "AGGIORNAMENTO AL 16\/03\/2026 per estrazione fatturazione"
|
||||
},
|
||||
{
|
||||
@ -876,12 +876,6 @@
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ROSSIMODA",
|
||||
"Descrizione": "Per tutti i campioni di ROSSIMODA\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 202,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LIMS-CIM - MAX MARA",
|
||||
"Descrizione": "Schema per MAX MARA scambio dati Database"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -2016,51 +2016,6 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
|
||||
el.addEventListener('change', () => el.classList.toggle('has-value', el.value.trim() !== ''));
|
||||
});
|
||||
})();
|
||||
|
||||
// ── Column resize ──
|
||||
(function() {
|
||||
const resizers = document.querySelectorAll('.resizer');
|
||||
let currentResizer = null, startX = 0, startWidth = 0, columnIndex = null;
|
||||
|
||||
function resize(e) {
|
||||
if (!currentResizer || columnIndex === null) return;
|
||||
const newWidth = Math.max(80, startWidth + (e.pageX - startX));
|
||||
const sel = '[data-index="' + columnIndex + '"]';
|
||||
const header = document.querySelector('.grid-header' + sel);
|
||||
if (header) header.style.flex = '0 0 ' + newWidth + 'px';
|
||||
const topCell = document.querySelector('.grid-top .grid-cell' + sel);
|
||||
if (topCell) topCell.style.flex = '0 0 ' + newWidth + 'px';
|
||||
document.querySelectorAll('.grid-row .grid-cell' + sel).forEach(function(cell) {
|
||||
cell.style.flex = '0 0 ' + newWidth + 'px';
|
||||
});
|
||||
var filterCell = document.querySelector('#gridFilterRow > .grid-cell:nth-child(' + (parseInt(columnIndex, 10) + 1) + ')');
|
||||
if (filterCell) filterCell.style.flex = '0 0 ' + newWidth + 'px';
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
if (currentResizer) {
|
||||
document.removeEventListener('mousemove', resize);
|
||||
document.removeEventListener('mouseup', stopResize);
|
||||
currentResizer = null;
|
||||
columnIndex = null;
|
||||
}
|
||||
}
|
||||
|
||||
resizers.forEach(function(resizer) {
|
||||
resizer.addEventListener('mousedown', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
currentResizer = this;
|
||||
var header = this.closest('.grid-header');
|
||||
if (!header) return;
|
||||
columnIndex = header.getAttribute('data-index');
|
||||
startX = e.pageX;
|
||||
startWidth = header.offsetWidth;
|
||||
document.addEventListener('mousemove', resize);
|
||||
document.addEventListener('mouseup', stopResize);
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user