first commit theloftstore

This commit is contained in:
2025-11-12 18:43:50 +01:00
parent e75be99e43
commit 6b65b31932
480 changed files with 9352 additions and 57951 deletions
+2 -1
View File
@@ -42,8 +42,9 @@
"laravel/tinker": "^2.7",
"laravel/ui": "^4.0",
"phpmailer/phpmailer": "^6.9",
"phpoffice/phpspreadsheet": "^4.1",
"phpoffice/phpspreadsheet": "^5.2",
"proengsoft/laravel-jsvalidation": "^4.0.0",
"smalot/pdfparser": "^2.12",
"socialiteproviders/microsoft": "^4.7",
"spatie/laravel-query-builder": "^5.0",
"vanguardapp/activity-log": "^6.0",
Generated
+64 -12
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9c4f1e3bc3ee2180211c055e70635aef",
"content-hash": "97382155de516648df8ab1f1c50ab1d5",
"packages": [
{
"name": "akaunting/laravel-setting",
@@ -3839,16 +3839,16 @@
},
{
"name": "phpoffice/phpspreadsheet",
"version": "4.1.0",
"version": "5.2.0",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
"reference": "6ff18c3a8df3a945492f75ce455d77f7ad55dd5c"
"reference": "3b8994b3aac4b61018bc04fc8c441f4fd68c18eb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/6ff18c3a8df3a945492f75ce455d77f7ad55dd5c",
"reference": "6ff18c3a8df3a945492f75ce455d77f7ad55dd5c",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/3b8994b3aac4b61018bc04fc8c441f4fd68c18eb",
"reference": "3b8994b3aac4b61018bc04fc8c441f4fd68c18eb",
"shasum": ""
},
"require": {
@@ -3878,18 +3878,19 @@
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
"dompdf/dompdf": "^2.0 || ^3.0",
"friendsofphp/php-cs-fixer": "^3.2",
"mitoteam/jpgraph": "^10.3",
"mitoteam/jpgraph": "^10.5",
"mpdf/mpdf": "^8.1.1",
"phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan": "^1.1 || ^2.0",
"phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0",
"phpstan/phpstan-phpunit": "^1.0 || ^2.0",
"phpunit/phpunit": "^10.5",
"squizlabs/php_codesniffer": "^3.7",
"tecnickcom/tcpdf": "^6.5"
},
"suggest": {
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
"ext-intl": "PHP Internationalization Functions",
"ext-intl": "PHP Internationalization Functions, regquired for NumberFormat Wizard",
"mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
@@ -3938,9 +3939,9 @@
],
"support": {
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/4.1.0"
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.2.0"
},
"time": "2025-03-02T06:52:24+00:00"
"time": "2025-10-26T15:54:22+00:00"
},
{
"name": "phpoption/phpoption",
@@ -4980,6 +4981,57 @@
],
"time": "2024-04-27T21:32:50+00:00"
},
{
"name": "smalot/pdfparser",
"version": "v2.12.1",
"source": {
"type": "git",
"url": "https://github.com/smalot/pdfparser.git",
"reference": "98d31ba34ef5b5a98897ef4b6c3925d502ea53b1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/smalot/pdfparser/zipball/98d31ba34ef5b5a98897ef4b6c3925d502ea53b1",
"reference": "98d31ba34ef5b5a98897ef4b6c3925d502ea53b1",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"ext-zlib": "*",
"php": ">=7.1",
"symfony/polyfill-mbstring": "^1.18"
},
"type": "library",
"autoload": {
"psr-0": {
"Smalot\\PdfParser\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
],
"authors": [
{
"name": "Sebastien MALOT",
"email": "sebastien@malot.fr"
}
],
"description": "Pdf parser library. Can read and extract information from pdf file.",
"homepage": "https://www.pdfparser.org",
"keywords": [
"extract",
"parse",
"parser",
"pdf",
"text"
],
"support": {
"issues": "https://github.com/smalot/pdfparser/issues",
"source": "https://github.com/smalot/pdfparser/tree/v2.12.1"
},
"time": "2025-07-31T06:19:56+00:00"
},
{
"name": "socialiteproviders/manager",
"version": "v4.8.1",
@@ -11355,6 +11407,6 @@
"php": "^8.2.0",
"ext-json": "*"
},
"platform-dev": [],
"platform-dev": {},
"plugin-api-version": "2.6.0"
}
File diff suppressed because it is too large Load Diff
-50
View File
@@ -1,50 +0,0 @@
<?php
ob_start();
session_start();
require_once '../../vendor/autoload.php';
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'excel_data' => []];
try {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
$template_id = isset($input['template_id']) ? intval($input['template_id']) : 0;
$filename = $input['routine_data']['filename'] ?? '';
$headerrow = $input['routine_data']['headerrow'] ?? 1;
$excelData = $input['excel_data'] ?? [];
$routineData = $input['routine_data'] ?? [];
if (!$filename || empty($excelData)) {
throw new Exception("Dati della routine mancanti.");
}
$routineFile = __DIR__ . '/routines/' . $filename;
if (file_exists($routineFile)) {
include_once $routineFile;
$routineData['xls_headers'] = $_SESSION['headers'] ?? [];
applyRoutine($excelData, $routineData); // Modifica $excelData in place
error_log("Routine {$routineData['name']} applicata (file: {$filename}) per template {$template_id}, header row: {$headerrow}");
} else {
throw new Exception("File della routine non trovato: $routineFile");
}
// Aggiorna la sessione con i dati modificati
$_SESSION['excel_data'] = $excelData;
$response['excel_data'] = $excelData;
$response['rows'] = array_column($excelData, 'data');
$response['columns'] = $_SESSION['headers'];
$response['template_id'] = $template_id;
$response['filename'] = $input['filename'] ?? '';
} else {
$response['error'] = "Richiesta non valida.";
}
} catch (Exception $e) {
$response['error'] = "Errore durante l'applicazione della routine: " . $e->getMessage();
error_log("Exception in apply_routine.php: " . $e->getMessage());
}
ob_end_clean();
header('Content-Type: application/json');
echo json_encode($response);
exit;
+88
View File
@@ -0,0 +1,88 @@
<?php
// class/chatpdf_helper.php
class ChatPDFHelper
{
private $apiKey = 'sec_Wyk8yl97UBzDW85zVWsuk4YFpSPGlr2Q';
private $apiBase = 'https://api.chatpdf.com/v1';
// Carica un PDF e restituisce l'ID del file
public function uploadPdf($pdfPath)
{
$ch = curl_init("{$this->apiBase}/sources/add-file");
$cfile = new CURLFile($pdfPath, 'application/pdf', basename($pdfPath));
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['file' => $cfile],
CURLOPT_HTTPHEADER => [
"x-api-key: {$this->apiKey}"
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
return $result['sourceId'] ?? null;
}
// Esegue una domanda al PDF caricato
public function askPdf($sourceId, $question)
{
$apiKey = $_ENV['CHATPDF_API_KEY'] ?? null;
if (!$apiKey) {
throw new Exception("ChatPDF API key not found in .env");
}
$url = "https://api.chatpdf.com/v1/chats/message";
$payload = json_encode([
"sourceId" => $sourceId,
"messages" => [
[
"role" => "user",
"content" => $question
]
]
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"x-api-key: $apiKey",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => $payload,
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception('cURL error: ' . curl_error($ch));
}
curl_close($ch);
$data = json_decode($response, true);
// Debug temporaneo
file_put_contents(
__DIR__ . '/../debug_chatpdf.log',
"Question: {$question}\nResponse: {$response}\n\n",
FILE_APPEND
);
if (isset($data['content'])) {
return trim($data['content']);
}
// ChatPDF a volte restituisce una lista di risposte
if (isset($data[0]['content'])) {
return trim($data[0]['content']);
}
return null;
}
}
+249
View File
@@ -0,0 +1,249 @@
<?php
include('include/headscript.php');
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// === Carica elenco clienti ===
$clients = $pdo->query("SELECT idclient, client_name FROM clients ORDER BY client_name")->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Client Files Management</title>
<?php include('cssinclude.php'); ?>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">
<style>
.upload-box {
background: #f9f9f9;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.select2-container {
width: 100% !important;
}
</style>
</head>
<body>
<?php include('include/navbar.php'); ?>
<div class="dashboard-main-wrapper wrapper">
<?php include('include/topbar.php'); ?>
<div class="dashboard-body">
<div class="card shadow-sm">
<div class="card-body">
<h4 class="mb-4">📁 Client File Management</h4>
<!-- Client selection -->
<div class="row mb-4">
<div class="col-md-4">
<label for="idclient" class="form-label">Select Client</label>
<select id="idclient" class="form-select select2" required>
<option value="">-- Choose Client --</option>
<?php foreach ($clients as $cl): ?>
<option value="<?= htmlspecialchars($cl['idclient']) ?>">
<?= htmlspecialchars($cl['client_name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<!-- Upload sections -->
<div class="row">
<div class="col-md-6">
<div class="upload-box">
<h6>📄 Upload Mapping File (PDF)</h6>
<form id="uploadMappingForm" enctype="multipart/form-data">
<input type="hidden" name="file_type" value="mapping">
<div class="mb-3">
<input type="file" name="file" accept=".pdf" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary w-100">Upload Mapping</button>
</form>
</div>
</div>
<div class="col-md-6">
<div class="upload-box">
<h6>💰 Upload Price List (PDF)</h6>
<form id="uploadPriceForm" enctype="multipart/form-data">
<input type="hidden" name="file_type" value="pricelist">
<div class="mb-3">
<input type="file" name="file" accept=".pdf" class="form-control" required>
</div>
<button type="submit" class="btn btn-success w-100">Upload Pricelist</button>
</form>
</div>
</div>
</div>
<hr>
<h5 class="mt-4">📋 Uploaded Files</h5>
<div class="table-responsive">
<table id="clientFilesTable" class="table table-striped table-bordered w-100">
<thead>
<tr>
<th>ID</th>
<th>Client</th>
<th>File Type</th>
<th>Filename</th>
<th>Source ID</th>
<th>Uploaded By</th>
<th>Upload Date</th>
<th>Actions</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
$(document).ready(function() {
$('.select2').select2();
const table = $('#clientFilesTable').DataTable({
ajax: {
url: 'client_files_list.php',
dataSrc: ''
},
columns: [{
data: 'id'
},
{
data: 'client_name'
},
{
data: 'file_type'
},
{
data: 'filename'
},
{
data: 'source_id'
},
{
data: 'uploaded_by'
},
{
data: 'upload_date'
},
{
data: null,
orderable: false,
render: function(data, type, row) {
return `
<button class="btn btn-danger btn-sm delete-file" data-id="${row.id}">
<i class="fas fa-trash"></i>
</button>`;
}
}
],
pageLength: 10,
order: [
[0, 'desc']
]
});
// Upload handlers
function handleUpload(formSelector) {
$(formSelector).on('submit', function(e) {
e.preventDefault();
const idclient = $('#idclient').val();
if (!idclient) {
Swal.fire('Warning', 'Select a client first.', 'warning');
return;
}
const formData = new FormData(this);
formData.append('idclient', idclient);
$.ajax({
url: 'client_files_upload.php',
type: 'POST',
data: formData,
processData: false,
contentType: false,
beforeSend: () => Swal.showLoading(),
success: function(res) {
Swal.close();
let json;
try {
json = JSON.parse(res);
} catch (e) {
Swal.fire('Error', res, 'error');
return;
}
if (json.status === 'ok') {
Swal.fire('Success', 'File uploaded successfully.', 'success');
table.ajax.reload(null, false);
} else {
Swal.fire('Error', json.message, 'error');
}
},
error: function() {
Swal.fire('Error', 'Upload failed.', 'error');
}
});
});
}
handleUpload('#uploadMappingForm');
handleUpload('#uploadPriceForm');
// Delete
$('#clientFilesTable').on('click', '.delete-file', function() {
const id = $(this).data('id');
Swal.fire({
title: 'Delete file?',
text: 'This will remove the file record.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, delete it'
}).then(result => {
if (result.isConfirmed) {
$.post('client_files_delete.php', {
id
}, res => {
let json;
try {
json = JSON.parse(res);
} catch (e) {
Swal.fire('Error', res, 'error');
return;
}
if (json.status === 'ok') {
Swal.fire('Deleted', 'File removed.', 'success');
table.ajax.reload(null, false);
} else {
Swal.fire('Error', json.message, 'error');
}
});
}
});
});
});
</script>
</body>
</html>
+33
View File
@@ -0,0 +1,33 @@
<?php
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
header('Content-Type: application/json');
$id = intval($_POST['id'] ?? 0);
if ($id <= 0) {
echo json_encode(['status' => 'error', 'message' => 'Invalid file ID']);
exit;
}
try {
// Recupera il nome del file
$stmt = $pdo->prepare("SELECT filename FROM client_files WHERE id = ?");
$stmt->execute([$id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row && !empty($row['filename'])) {
$filePath = __DIR__ . '/../uploads/client_files/' . $row['filename'];
if (file_exists($filePath)) {
unlink($filePath); // elimina file fisico
}
}
// Cancella riga dal DB
$pdo->prepare("DELETE FROM client_files WHERE id = ?")->execute([$id]);
echo json_encode(['status' => 'ok']);
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
+36
View File
@@ -0,0 +1,36 @@
<?php
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
header('Content-Type: application/json');
try {
$idclient = intval($_GET['idclient'] ?? 0);
$query = "
SELECT
cf.id,
c.client_name,
cf.file_type,
cf.filename,
cf.source_id,
cf.upload_date,
cf.notes
FROM client_files cf
LEFT JOIN clients c ON c.idclient = cf.idclient
" . ($idclient > 0 ? "WHERE cf.idclient = :idclient" : "") . "
ORDER BY cf.upload_date DESC
";
$stmt = $pdo->prepare($query);
if ($idclient > 0) {
$stmt->bindValue(':idclient', $idclient, PDO::PARAM_INT);
}
$stmt->execute();
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($data);
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
+98
View File
@@ -0,0 +1,98 @@
<?php
require_once(__DIR__ . '/class/db-functions.php');
use Dotenv\Dotenv;
header('Content-Type: application/json');
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
throw new Exception("Invalid request method.");
}
$idclient = intval($_POST['idclient'] ?? 0);
$file_type = $_POST['file_type'] ?? '';
$uploaded_by = $_POST['uploaded_by'] ?? null; // opzionale
$notes = $_POST['notes'] ?? null;
if ($idclient <= 0 || empty($file_type) || !isset($_FILES['file'])) {
throw new Exception("Missing required parameters.");
}
// Cartella di upload
$uploadDir = __DIR__ . '/../uploads/client_files/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$file = $_FILES['file'];
if ($file['error'] !== UPLOAD_ERR_OK) {
throw new Exception("File upload error.");
}
$filename = time() . '_' . preg_replace('/[^A-Za-z0-9_.-]/', '_', basename($file['name']));
$targetPath = $uploadDir . $filename;
if (!move_uploaded_file($file['tmp_name'], $targetPath)) {
throw new Exception("Failed to move uploaded file.");
}
// === 1️⃣ Inserisci nel DB senza source_id inizialmente ===
$stmt = $pdo->prepare("
INSERT INTO client_files (idclient, file_type, filename, uploaded_by, notes)
VALUES (:idclient, :file_type, :filename, :uploaded_by, :notes)
");
$stmt->execute([
':idclient' => $idclient,
':file_type' => $file_type,
':filename' => $filename,
':uploaded_by' => $uploaded_by,
':notes' => $notes
]);
$insertId = $pdo->lastInsertId();
// === 2️⃣ Upload verso ChatPDF ===
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2));
$dotenv->load();
$apiKey = $_ENV['CHATPDF_API_KEY'] ?? null;
if (!$apiKey) {
throw new Exception("ChatPDF API key not found in .env");
}
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.chatpdf.com/v1/sources/add-file",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["x-api-key: $apiKey"],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
"file" => new CURLFile($targetPath)
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) throw new Exception("ChatPDF API error: $err");
$json = json_decode($response, true);
$sourceId = $json['sourceId'] ?? null;
// === 3️⃣ Se ChatPDF restituisce un source_id, aggiorniamo il DB ===
if ($sourceId) {
$upd = $pdo->prepare("UPDATE client_files SET source_id = :source_id WHERE id = :id");
$upd->execute([':source_id' => $sourceId, ':id' => $insertId]);
$msg = "File uploaded and ChatPDF registered (sourceId: $sourceId)";
} else {
$msg = "File uploaded, but ChatPDF did not return a source ID. Response: " . json_encode($json);
}
echo json_encode(['status' => 'ok', 'message' => $msg, 'source_id' => $sourceId]);
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
+84 -1
View File
@@ -19,4 +19,87 @@
<!-- Font awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<!-- SweetAlert2 -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
/* --- Allineamento contenuto rispetto alla sidebar --- */
.dashboard-main-wrapper {
display: flex;
flex-direction: row;
}
/* Sidebar fissa a sinistra */
.sidebar-wrapper {
width: 260px;
/* o la larghezza effettiva della tua sidebar */
position: fixed;
left: 0;
top: 0;
height: 100%;
z-index: 1000;
}
/* Contenuto principale spostato a destra della sidebar */
.dashboard-body {
margin-left: 260px;
/* deve corrispondere alla larghezza sidebar */
padding: 20px 30px;
width: calc(100% - 260px);
background-color: #f9fafb;
}
/* Se vuoi includere anche la topbar fissa */
.dashboard-body {
margin-top: 70px;
/* altezza della topbar */
}
/* --- Comportamento responsive --- */
@media (max-width: 992px) {
.sidebar-wrapper {
position: absolute;
left: -260px;
transition: all 0.3s ease;
}
.sidebar-wrapper.active {
left: 0;
}
.dashboard-body {
margin-left: 0;
width: 100%;
}
}
</style>
<style>
/* === Sidebar collassata === */
.wrapper.toggled .sidebar-wrapper {
width: 70px !important;
transition: all 0.3s ease;
overflow: hidden;
}
/* Nasconde il testo */
.wrapper.toggled .logo-text,
.wrapper.toggled .menu-title,
.wrapper.toggled .menu-label {
display: none !important;
}
/* Riduce padding e centra le icone */
.wrapper.toggled .metismenu a {
justify-content: center !important;
padding: 10px 0 !important;
}
.wrapper.toggled .metismenu i {
margin-right: 0 !important;
}
/* Sposta il contenuto principale */
.wrapper.toggled .dashboard-body {
margin-left: 70px !important;
width: calc(100% - 70px) !important;
transition: all 0.3s ease;
}
</style>
File diff suppressed because one or more lines are too long
-29
View File
@@ -1,29 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$partId = $data['part_id'] ?? null;
if (!$partId) {
echo json_encode(['success' => false, 'message' => 'ID parte mancante']);
exit;
}
try {
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE id = :part_id");
$stmt->execute([':part_id' => $partId]);
$rowCount = $stmt->rowCount();
if ($rowCount > 0) {
echo json_encode(['success' => true, 'message' => 'Parte eliminata con successo']);
} else {
echo json_encode(['success' => false, 'message' => 'Nessuna parte trovata con ID ' . $partId]);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nell\'eliminazione: ' . $e->getMessage()]);
}
-28
View File
@@ -1,28 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$partId = $data['part_id'] ?? null;
if (!$partId) {
echo json_encode(['success' => false, 'message' => 'ID parte mancante']);
exit;
}
try {
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE id = :part_id");
$stmt->execute([':part_id' => $partId]);
$rowCount = $stmt->rowCount();
if ($rowCount > 0) {
echo json_encode(['success' => true, 'message' => 'Parte eliminata con successo']);
} else {
echo json_encode(['success' => false, 'message' => 'Nessuna parte trovata con ID ' . $partId]);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nell\'eliminazione: ' . $e->getMessage()]);
}
-37
View File
@@ -1,37 +0,0 @@
<?php
// delete_photo.php
include('include/headscript.php');
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['photo_id'])) {
echo json_encode(['success' => false, 'message' => 'Richiesta non valida']);
exit;
}
$photoId = intval($_POST['photo_id']);
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Recupera il percorso del file
$stmt = $pdo->prepare("SELECT file_path FROM datadb_photos WHERE id = ?");
$stmt->execute([$photoId]);
$photo = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$photo) {
echo json_encode(['success' => false, 'message' => 'Foto non trovata']);
exit;
}
// Elimina il file dal server
$filePath = '../photostrf/' . $photo['file_path'];
if (file_exists($filePath)) {
unlink($filePath);
}
// Elimina il record dal database
$stmt = $pdo->prepare("DELETE FROM datadb_photos WHERE id = ?");
$stmt->execute([$photoId]);
echo json_encode(['success' => true, 'message' => 'Foto eliminata con successo']);
-64
View File
@@ -1,64 +0,0 @@
<?php
// Enable errors for debugging
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/delete_record_debug.log');
// Log iniziale
error_log("Inizio cancellazione record alle " . date('Y-m-d H:i:s'));
// Includi il file di configurazione del database
include('include/headscript.php');
// Ricevi l'ID dalla richiesta POST
$input = json_decode(file_get_contents('php://input'), true);
$iddatadb = isset($input['id']) ? intval($input['id']) : 0;
if ($iddatadb <= 0) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'ID non valido']);
exit;
}
// Connessione al database
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Errore di connessione al database: ' . $e->getMessage()]);
error_log("Errore di connessione al database: " . $e->getMessage());
exit;
}
// Inizia una transazione
$pdo->beginTransaction();
try {
// Elimina i dettagli associati dal tavolo import_data_details
$stmt = $pdo->prepare("DELETE FROM import_data_details WHERE id = ?");
$stmt->execute([$iddatadb]);
// Elimina il record principale dal tavolo datadb
$stmt = $pdo->prepare("DELETE FROM datadb WHERE iddatadb = ?");
$stmt->execute([$iddatadb]);
// Verifica se il record è stato eliminato
if ($stmt->rowCount() > 0) {
$pdo->commit();
echo json_encode(['success' => true, 'message' => 'Record eliminato con successo']);
error_log("Record con iddatadb=$iddatadb eliminato con successo");
} else {
$pdo->rollBack();
http_response_code(404);
echo json_encode(['success' => false, 'message' => 'Record non trovato']);
error_log("Record con iddatadb=$iddatadb non trovato");
}
} catch (Exception $e) {
$pdo->rollBack();
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Errore durante la cancellazione: ' . $e->getMessage()]);
error_log("Errore durante la cancellazione del record con iddatadb=$iddatadb: " . $e->getMessage());
}
-49
View File
@@ -1,49 +0,0 @@
<?php
// Abilita la gestione degli errori
ini_set('display_errors', 1);
error_reporting(E_ALL);
// Includi la connessione al database
require_once 'class/db-functions.php';
try {
// Controlla se è stato passato un ID valido
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
throw new Exception('Invalid ID');
}
$id = intval($_GET['id']); // Sanifica l'ID
// Connessione al database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
if (!$pdo) {
throw new Exception('Database connection failed');
}
// Verifica se l'ID esiste
$stmtCheck = $pdo->prepare("SELECT id FROM excel_templates WHERE id = ?");
$stmtCheck->execute([$id]);
if ($stmtCheck->rowCount() === 0) {
throw new Exception('Template not found');
}
// Esegui l'eliminazione
$stmt = $pdo->prepare("DELETE FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
if ($stmt->rowCount() > 0) {
$message = "Template deleted successfully!";
$status = "success";
} else {
throw new Exception('Failed to delete template');
}
} catch (Exception $e) {
$message = $e->getMessage();
$status = "error";
}
// Reindirizza con un messaggio
header("Location: templates_dashboard.php?status=$status&message=" . urlencode($message));
exit;
-434
View File
@@ -1,434 +0,0 @@
<?php include('include/headscript.php');
// Controlla se è stato passato un ID valido
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
exit;
}
$id = intval($_GET['id']); // Sanifica l'ID
// Recupera il template dal database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
exit;
}
// Recupera tutte le routine dal database
$stmt = $pdo->prepare("SELECT * FROM routine");
$stmt->execute();
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!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" />
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<?php include('cssinclude.php'); ?>
<!-- Include jQuery prima di Select2 -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<title>Edit Template <?= 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="card mb-4">
<div class="card-header">
<h5 class="mb-0">Update XLS Template</h5>
</div>
<div class="card-body">
<p class="mb-2">Edit the following form in order to update the selected import XLS template</p>
<p class="mb-2">Mandatory Fields</p>
<ul class="mb-0">
<li>Template Name</li>
<li>Row Header and Column Header: where the title of the excel starts</li>
<li>Schema</li>
</ul>
</div>
</div>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Edit Template: <?php echo htmlspecialchars($template['name']); ?></h6>
</div>
</div>
</div>
<div class="card-body">
<div class="col-12">
<form id="editTemplateForm" method="POST">
<input type="hidden" name="id" value="<?php echo $template['id']; ?>">
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="name" class="form-control" value="<?php echo htmlspecialchars($template['name']); ?>" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="number" name="header_row" class="form-control" value="<?php echo $template['header_row']; ?>" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?>*</label>
<input type="text" name="start_column" class="form-control" value="<?php echo htmlspecialchars($template['start_column']); ?>" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
<textarea name="description" class="form-control"><?php echo htmlspecialchars($template['description']); ?></textarea>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?>*</label>
<input type="text" name="target_table" class="form-control" value="<?php echo htmlspecialchars($template['target_table']); ?>" readonly required>
</div>
<div class="mb-3">
<label class="form-label">Button Size</label>
<select name="button_size" class="form-control">
<option value="small" <?php echo ($template['button_size'] ?? 'medium') === 'small' ? 'selected' : ''; ?>>Small</option>
<option value="medium" <?php echo ($template['button_size'] ?? 'medium') === 'medium' ? 'selected' : ''; ?>>Medium</option>
<option value="large" <?php echo ($template['button_size'] ?? 'medium') === 'large' ? 'selected' : ''; ?>>Large</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Button Background Color</label>
<input type="color" name="button_bg_color" class="form-control" value="<?php echo htmlspecialchars($template['button_bg_color'] ?? '#007bff'); ?>">
</div>
<div class="mb-3">
<label class="form-label">Button Text Color</label>
<input type="color" name="button_text_color" class="form-control" value="<?php echo htmlspecialchars($template['button_text_color'] ?? '#ffffff'); ?>">
</div>
<div class="mb-3">
<label class="form-label">Button Label</label>
<input type="text" name="button_label" class="form-control" value="<?php echo htmlspecialchars($template['button_label'] ?? 'Click Me'); ?>">
</div>
<div class="mb-3">
<label class="form-label">Select Client *</label>
<select name="client_id" id="clientSelect" class="form-control" required>
<option value="">Select a client...</option>
</select>
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
</div>
<div class="mb-3">
<label class="form-label">Select Schema *</label>
<select name="schema_id" id="schemaSelect" class="form-control" required>
<option value="">Select a schema...</option>
</select>
<span id="schemaLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Caricamento schemi in corso...</span>
</div>
<div class="mb-3">
<label class="form-label">Select Routine</label>
<select name="idroutine" id="routineSelect" class="form-control">
<option value="">Select a routine...</option>
<?php foreach ($routines as $routine): ?>
<option value="<?php echo $routine['idroutine']; ?>" <?php echo ($template['idroutine'] ?? '') == $routine['idroutine'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($routine['name']); ?>
</option>
<?php endforeach; ?>
</select>
<div id="routineDetails" class="mt-2" style="display: none;">
<h6>Routine Details</h6>
<p><strong>Name:</strong> <span id="routineName"></span></p>
<p><strong>Description:</strong> <span id="routineDescription"></span></p>
<p><strong>Action 1:</strong> <span id="routineAction1"></span></p>
<p><strong>Action 2:</strong> <span id="routineAction2"></span></p>
<p><strong>Action 3:</strong> <span id="routineAction3"></span></p>
</div>
</div>
<br>
<button type="submit" class="btn btn-primary"><?= htmlspecialchars($savechanges, ENT_QUOTES, 'UTF-8'); ?></button>
<a href="templates_dashboard.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="overlay toggle-icon"></div>
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Verifica che jQuery sia caricato
if (typeof jQuery === 'undefined') {
alert("Errore: jQuery non è caricato. Contatta l'amministratore.");
return;
}
const form = document.getElementById("editTemplateForm");
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
const schemaLoadingStatus = document.getElementById("schemaLoadingStatus");
const routineSelect = document.getElementById("routineSelect");
const routineDetails = document.getElementById("routineDetails");
const routineName = document.getElementById("routineName");
const routineDescription = document.getElementById("routineDescription");
const routineAction1 = document.getElementById("routineAction1");
const routineAction2 = document.getElementById("routineAction2");
const routineAction3 = document.getElementById("routineAction3");
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
return;
}
// Inizializza Select2
$('#clientSelect').select2({
placeholder: "Search for a client...",
allowClear: true
});
$('#schemaSelect').select2({
placeholder: "Search for a schema...",
allowClear: true
});
$('#routineSelect').select2({
placeholder: "Select a routine...",
allowClear: true
});
// Carica i clienti
async function loadClients() {
try {
clientLoadingStatus.style.display = 'inline';
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
const response = await fetch("get_clienti.php", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
const select = document.getElementById("clientSelect");
select.innerHTML = '<option value="">Select a client...</option>';
data.value.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
if (parseInt(id) === parseInt(<?php echo json_encode($template['idclient'] ?? 0); ?>)) {
option.selected = true;
}
select.add(option);
});
$(select).trigger('change');
clientLoadingStatus.textContent = "Clienti caricati.";
} catch (error) {
clientLoadingStatus.textContent = "Errore nel caricamento.";
Swal.fire({
title: "Errore!",
text: "Impossibile caricare i clienti: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
}
}
// Carica gli schemi
async function loadSchemas() {
try {
schemaLoadingStatus.style.display = 'inline';
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
const response = await fetch("get_schemi.php", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
const select = document.getElementById("schemaSelect");
select.innerHTML = '<option value="">Select a schema...</option>';
data.value.forEach(schema => {
const nome = schema.Nome || "Nome non disponibile";
const id = schema.IdSchemaCustomFields || "ID non disponibile";
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
if (parseInt(id) === parseInt(<?php echo json_encode($template['idschema'] ?? 0); ?>)) {
option.selected = true;
}
select.add(option);
});
$(select).trigger('change');
schemaLoadingStatus.textContent = "Schemi caricati.";
} catch (error) {
schemaLoadingStatus.textContent = "Errore nel caricamento.";
Swal.fire({
title: "Errore!",
text: "Impossibile caricare gli schemi: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => schemaLoadingStatus.style.display = 'none', 2000);
}
}
// Carica i dati
async function loadData() {
try {
await loadClients();
await loadSchemas();
} catch (error) {
Swal.fire({
title: "Errore!",
text: "Errore nel caricamento dei dati: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
}
}
loadData();
// Routine dettagli
const routines = <?php echo json_encode($routines); ?>;
function updateRoutineDetails() {
const selectedId = routineSelect.value;
routineDetails.style.display = selectedId ? 'block' : 'none';
if (selectedId) {
const routine = routines.find(r => r.idroutine == selectedId);
if (routine) {
routineName.textContent = routine.name || 'N/A';
routineDescription.textContent = routine.description || 'N/A';
routineAction1.textContent = routine.action1 || 'N/A';
routineAction2.textContent = routine.action2 || 'N/A';
routineAction3.textContent = routine.action3 || 'N/A';
} else {
routineName.textContent = 'N/A';
routineDescription.textContent = 'N/A';
routineAction1.textContent = 'N/A';
routineAction2.textContent = 'N/A';
routineAction3.textContent = 'N/A';
}
} else {
routineName.textContent = '';
routineDescription.textContent = '';
routineAction1.textContent = '';
routineAction2.textContent = '';
routineAction3.textContent = '';
}
}
routineSelect.addEventListener('change', updateRoutineDetails);
updateRoutineDetails(); // Inizializza dettagli se una routine è preselezionata
// Submit del form
form.addEventListener("submit", function(e) {
e.preventDefault();
let formData = new FormData(this);
const clientSelect = document.getElementById("clientSelect");
const clientId = clientSelect.value;
const selectedClientOption = clientSelect.options[clientSelect.selectedIndex];
if (!clientId) {
Swal.fire({
title: "Errore!",
text: "Per favore seleziona un cliente.",
icon: "error",
confirmButtonText: "OK"
});
return;
}
let clientName = "";
if (selectedClientOption) {
const optionText = selectedClientOption.text.trim();
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
clientName = nameMatch ? nameMatch[1].trim() : optionText;
}
formData.append("client_name", clientName);
const schemaSelect = document.getElementById("schemaSelect");
const schemaId = schemaSelect.value;
const selectedSchemaOption = schemaSelect.options[schemaSelect.selectedIndex];
if (!schemaId) {
Swal.fire({
title: "Errore!",
text: "Per favore seleziona uno schema.",
icon: "error",
confirmButtonText: "OK"
});
return;
}
let schemaName = "";
if (selectedSchemaOption) {
const optionText = selectedSchemaOption.text.trim();
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
}
formData.append("idschema", schemaId);
formData.append("schemaname", schemaName);
// Aggiungi idroutine
const routineId = routineSelect.value;
formData.append("idroutine", routineId);
fetch("process_edit_template_xls.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
title: "Successo!",
text: "Template aggiornato con successo!",
icon: "success",
confirmButtonText: "OK"
}).then(() => {
window.location.href = "templates_dashboard.php";
});
} else {
Swal.fire({
title: "Errore!",
text: data.message,
icon: "error",
confirmButtonText: "OK"
});
}
})
.catch(error => {
Swal.fire({
title: "Errore!",
text: "Si è verificato un errore imprevisto.",
icon: "error",
confirmButtonText: "OK"
});
});
});
});
</script>
</body>
</html>
-239
View File
@@ -1,239 +0,0 @@
document.addEventListener("DOMContentLoaded", () => {
console.log("export_to_lims.js loaded");
// Debug: verifica che i pulsanti siano trovati
const exportButtons = document.querySelectorAll(".export-lims-btn");
console.log(`Found ${exportButtons.length} export-lims-btn buttons`);
if (exportButtons.length === 0) {
console.warn("No .export-lims-btn buttons found in the DOM");
return;
}
exportButtons.forEach((btn) => {
btn.addEventListener("click", (e) => {
e.preventDefault();
const rowIndex = btn.dataset.row;
const iddatadb = btn.dataset.iddatadb;
console.log(
`Export to LIMS clicked for row ${rowIndex}, iddatadb: ${iddatadb}`,
);
// Mostra il modale di conferma
const confirmModalElement =
document.getElementById("exportConfirmModal");
if (!confirmModalElement) {
console.error("exportConfirmModal not found in the DOM");
alert("Errore: Modale di conferma non trovato");
return;
}
const confirmModal = new bootstrap.Modal(confirmModalElement, {
keyboard: false,
});
document.getElementById("exportIddatadb").textContent = iddatadb;
confirmModal.show();
// Gestisci il click su "Conferma"
const confirmBtn = document.getElementById("exportConfirmBtn");
if (!confirmBtn) {
console.error("exportConfirmBtn not found in the DOM");
confirmModal.hide();
alert("Errore: Pulsante di conferma non trovato");
return;
}
const confirmHandler = async () => {
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
confirmModal.hide();
const formData = new FormData();
formData.append("iddatadb", iddatadb);
try {
const response = await fetch("export_to_lims.php", {
method: "POST",
body: formData,
});
if (!response.ok)
throw new Error(
`HTTP error! status: ${response.status}`,
);
const data = await response.json();
console.log("Export response:", data);
// Mostra il modale di risposta
const responseModalElement = document.getElementById(
"exportResponseModal",
);
if (!responseModalElement) {
console.error(
"exportResponseModal not found in the DOM",
);
alert("Errore: Modale di risposta non trovato");
return;
}
const responseModal = new bootstrap.Modal(
responseModalElement,
{
keyboard: false,
},
);
const responseMessage = document.getElementById(
"exportResponseMessage",
);
if (data.success) {
responseMessage.innerHTML = `${data.message.replace(/\n/g, "<br>")}<br>ID CommessaWeb: ${data.idcommessaweb}`;
document.getElementById(
"exportResponseModalLabel",
).textContent = "Esportazione Completata";
responseModal.show();
// Aggiorna la UI per riflettere lo stato 'To LIMS'
const statusCell = btn
.closest(".grid-row")
.querySelector(
'.grid-cell[data-col="status"] .status-badge',
);
if (statusCell) {
statusCell.classList.remove("status-i", "status-P");
statusCell.classList.add("status-l");
statusCell.textContent = "To LIMS";
}
// Gestisci la chiusura del modale di risposta
responseModalElement.addEventListener(
"hidden.bs.modal",
() => {
console.log(
"exportResponseModal closed, cleaning up",
);
// Rimuovi tutti i backdrop residui
document
.querySelectorAll(".modal-backdrop")
.forEach((backdrop) => {
console.log(
"Removing backdrop:",
backdrop,
);
backdrop.remove();
});
// Ripristina il body
document.body.classList.remove("modal-open");
document.body.style.paddingRight = "";
// Nascondi l'overlay
const overlay = document.querySelector(
".overlay.toggle-icon",
);
if (overlay) {
overlay.style.display = "none";
}
},
{ once: true },
);
} else {
responseMessage.textContent = `Errore durante la generazione dei payload: ${data.message}`;
document.getElementById(
"exportResponseModalLabel",
).textContent = "Errore Esportazione";
responseModal.show();
// Gestisci la chiusura del modale di risposta anche in caso di errore
responseModalElement.addEventListener(
"hidden.bs.modal",
() => {
console.log(
"exportResponseModal closed, cleaning up",
);
// Rimuovi tutti i backdrop residui
document
.querySelectorAll(".modal-backdrop")
.forEach((backdrop) => {
console.log(
"Removing backdrop:",
backdrop,
);
backdrop.remove();
});
// Ripristina il body
document.body.classList.remove("modal-open");
document.body.style.paddingRight = "";
// Nascondi l'overlay
const overlay = document.querySelector(
".overlay.toggle-icon",
);
if (overlay) {
overlay.style.display = "none";
}
},
{ once: true },
);
}
} catch (error) {
console.error("Export error:", error);
const responseModalElement = document.getElementById(
"exportResponseModal",
);
if (!responseModalElement) {
console.error(
"exportResponseModal not found in the DOM",
);
alert("Errore: Modale di risposta non trovato");
return;
}
const responseModal = new bootstrap.Modal(
responseModalElement,
{
keyboard: false,
},
);
document.getElementById(
"exportResponseMessage",
).textContent =
`Errore durante la generazione dei payload: ${error.message}`;
document.getElementById(
"exportResponseModalLabel",
).textContent = "Errore Esportazione";
responseModal.show();
// Gestisci la chiusura del modale di risposta in caso di errore
responseModalElement.addEventListener(
"hidden.bs.modal",
() => {
console.log(
"exportResponseModal closed, cleaning up",
);
// Rimuovi tutti i backdrop residui
document
.querySelectorAll(".modal-backdrop")
.forEach((backdrop) => {
console.log("Removing backdrop:", backdrop);
backdrop.remove();
});
// Ripristina il body
document.body.classList.remove("modal-open");
document.body.style.paddingRight = "";
// Nascondi l'overlay
const overlay = document.querySelector(
".overlay.toggle-icon",
);
if (overlay) {
overlay.style.display = "none";
}
},
{ once: true },
);
}
// Rimuovi il listener dopo l'esecuzione
confirmBtn.removeEventListener("click", confirmHandler);
};
// Rimuovi eventuali listener precedenti
confirmBtn.removeEventListener("click", confirmHandler);
confirmBtn.addEventListener("click", confirmHandler);
});
});
});
-291
View File
@@ -1,291 +0,0 @@
<?php
require_once "class/VisualLimsApiClient.class.php";
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
header("Content-Type: application/json");
// 🔹 Configura directory log (creala se non esiste)
$logDir = __DIR__ . '/logs/api/';
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
// 🔹 Base URL API
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
// 🔹 Funzione per validare e convertire date
function validateDate($value)
{
// Prova a validare come data (accetta formati comuni)
$date = DateTime::createFromFormat('Y-m-d', $value) ?: DateTime::createFromFormat('Y-m-d H:i:s', $value);
if ($date) {
return $date->format('Y-m-d\TH:i:sP'); // Formato ISO 8601
}
return null; // Imposta null se non è una data valida
}
try {
$iddatadb = $_POST['iddatadb'] ?? null;
if (!$iddatadb) {
throw new Exception("Missing iddatadb");
}
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
$stmt = $pdo->prepare("
SELECT d.idclient AS clienteId, et.idschema AS schemaId
FROM datadb as d
INNER JOIN excel_templates as et ON d.templateid = et.id
WHERE d.iddatadb = :iddatadb
LIMIT 1
");
$stmt->execute(['iddatadb' => $iddatadb]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result) {
throw new Exception("No Cliente/Schema found for iddatadb {$iddatadb}");
}
$clienteId = (int) $result['clienteId'];
$schemaId = (int) $result['schemaId'];
// 🔹 STEP 3: Fetch Parts (including idmatrice)
$stmt = $pdo->prepare("
SELECT part_number, part_description, material, color, mix, idmatrice
FROM identification_parts
WHERE iddatadb = :iddatadb
");
$stmt->execute(['iddatadb' => $iddatadb]);
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 🔹 STEP 4: Fetch Field Values with Labels
$stmt = $pdo->prepare("
SELECT
idd.field_value,
m.field_label,
m.schema_id,
m.field_id
FROM
import_data_details as idd
JOIN template_mapping m ON idd.mapping_id = m.id
WHERE idd.id = :iddatadb
");
$stmt->execute(['iddatadb' => $iddatadb]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$fieldValues = [];
$valueMap = [];
foreach ($rows as $row) {
$fieldValues[] = [
"IdCommesseCustomFields" => (int) $row['field_id'],
"Valore" => $row['field_value'],
"FieldLabel" => $row['field_label']
];
$valueMap[(int) $row['field_id']] = $row['field_value'];
}
// Logga i fieldValues in error_log
$logFieldValues = "FieldValues dal DB (iddatadb={$iddatadb}):\n" . json_encode($fieldValues, JSON_PRETTY_PRINT);
error_log($logFieldValues);
// 🔹 Initialize API client
$api = VisualLimsApiClient::getInstance();
// 🔹 STEP 5: Create CommessaWeb (NOT WebOrder)
$commessaWebPayload = [
"Cliente" => $clienteId,
"SchemaCustomField" => $schemaId,
"Richiedente" => "Test Web Import",
"Descrizione" => "TEST CommessaWeb",
];
// Costruisci log curl-like per STEP 5
$jsonPayload = json_encode($commessaWebPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$logContentStep5 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb' \\\n" .
"--header 'Content-Type: application/json' \\\n" .
"--header 'Authorization: Bearer ••••••' \\\n" .
"--data '{$jsonPayload}'";
$commessaWeb = $api->post("CommessaWeb", $commessaWebPayload);
$logContentStep5 .= "\n\nRESPONSE:\n" . json_encode($commessaWeb, JSON_PRETTY_PRINT);
// Salva log
$logFileStep5 = $logDir . "commessa_create_step5_" . $iddatadb . "_" . time() . ".txt";
file_put_contents($logFileStep5, $logContentStep5);
$commessaId = $commessaWeb["IdCommessa"];
$commessaWebCode = substr($commessaWeb["CodiceCommessa"] ?? "TEST CommessaWeb", 0, 30); // Limite a 30 caratteri
// 🔹 STEP 6: Create Campioni (Samples) for each part
$campioni = [];
$logContentStep6 = "";
foreach ($parts as $index => $part) {
$matriceId = (int) ($part["idmatrice"] ?? 0);
if ($matriceId <= 0) {
throw new Exception("Invalid or missing idmatrice for part: " . ($part["part_number"] ?? "Unknown"));
}
$campionePayload = [
"Commessa" => $commessaId,
"Matrice" => $matriceId,
"SottoMatrice" => null,
"SchemaCustomField" => $schemaId,
"NoteWeb" => $part["part_description"] ?? ""
];
// Costruisci curl-like per questo campione
$jsonPayload = json_encode($campionePayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$logContentStep6 .= "CAMPIONE #{$index}\n" .
"curl --location --request POST '{$apiBaseUrl}Campione' \\\n" .
"--header 'Content-Type: application/json' \\\n" .
"--header 'Authorization: Bearer ••••••' \\\n" .
"--data '{$jsonPayload}'\n\n";
$campione = $api->post("Campione", $campionePayload);
$logContentStep6 .= "RESPONSE:\n" . json_encode($campione, JSON_PRETTY_PRINT) . "\n\n---\n";
$campione["PartNumber"] = $part["part_number"] ?? "";
$campione["Material"] = $part["material"] ?? "";
$campione["Color"] = $part["color"] ?? "";
$campione["Mix"] = $part["mix"] ?? "";
$campioni[] = $campione;
}
// Salva log per STEP 6
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
file_put_contents($logFileStep6, $logContentStep6);
// 🔹 STEP 7: Update Custom Fields for CommessaWeb
if (!empty($fieldValues)) {
// GET con espansione per CustomField
$expand = "CommesseCustomFields(\$expand=CustomField)";
$commessaWithFields = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
// Logga il GET
$logContentGet = "curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
"--header 'Authorization: Bearer ••••••'\n\n" .
"RESPONSE:\n" . json_encode($commessaWithFields, JSON_PRETTY_PRINT);
$logFileGet = $logDir . "commessa_{$commessaId}_get_step7_" . time() . ".txt";
file_put_contents($logFileGet, $logContentGet);
// Prepara payload PATCH
$commessaCustomFields = [];
foreach ($commessaWithFields["CommesseCustomFields"] as $customField) {
$definitionId = (int) ($customField["CustomField"]["IdCustomField"] ?? 0);
$fieldId = (int) $customField["IdCommesseCustomFields"];
$currentValue = $customField["Valore"] ?? '';
$fieldType = $customField["CustomField"]["Tipo"] ?? '';
$newValue = isset($valueMap[$definitionId]) ? $valueMap[$definitionId] : $currentValue;
// Valida se il campo è di tipo Data
if ($fieldType === 'Data' && $newValue !== $currentValue) {
$newValue = validateDate($newValue);
}
$commessaCustomFields[] = [
"IdCommesseCustomFields" => $fieldId,
"Valore" => $newValue
];
}
if (!empty($commessaCustomFields)) {
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
// Logga payload e response
$jsonPayload = json_encode($updatePayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$logContentStep7 = "curl --location --request PATCH '{$apiBaseUrl}CommessaWeb({$commessaId})' \\\n" .
"--header 'Content-Type: application/json' \\\n" .
"--header 'Authorization: Bearer ••••••' \\\n" .
"--data '{$jsonPayload}'";
$patchResponse = $api->patch("CommessaWeb({$commessaId})", $updatePayload);
$logContentStep7 .= "\n\nRESPONSE:\n" . json_encode($patchResponse, JSON_PRETTY_PRINT);
$logFileStep7 = $logDir . "commessa_{$commessaId}_update_step7_" . time() . ".txt";
file_put_contents($logFileStep7, $logContentStep7);
}
}
// 🔹 STEP 8: Update datadb with idcommessaweb, commessaweb, and status
$stmt = $pdo->prepare("
UPDATE datadb
SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l'
WHERE iddatadb = :iddatadb
");
$stmt->execute([
'idcommessaweb' => $commessaId,
'commessaweb' => $commessaWebCode,
'iddatadb' => $iddatadb
]);
// 🔹 STEP 9: Send CommessaWeb to laboratory (commentato come richiesto)
/*
$sendResult = $api->post("CommessaWeb({$commessaId})/InviaCommessa", []);
// Logga il POST
$logContentStep9 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/InviaCommessa' \\\n" .
"--header 'Content-Type: application/json' \\\n" .
"--header 'Authorization: Bearer ••••••' \\\n" .
"--data '{}'\n\n" .
"RESPONSE:\n" . json_encode($sendResult, JSON_PRETTY_PRINT);
$logFileStep9 = $logDir . "commessa_{$commessaId}_send_step9_" . time() . ".txt";
file_put_contents($logFileStep9, $logContentStep9);
*/
// 🔹 STEP 10: GET di controllo post-PATCH
$expand = "CommesseCustomFields(\$expand=CustomField)";
$commessaAfterPatch = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
// Logga il GET di controllo
$logContentStep10 = "curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
"--header 'Authorization: Bearer ••••••'\n\n" .
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
file_put_contents($logFileStep10, $logContentStep10);
// 🔹 STEP 11: Prepare final response
$finalCommessa = [
"Cliente" => $clienteId,
"SchemaCustomField" => $schemaId,
"Richiedente" => $commessaWeb["Richiedente"] ?? "Web Import",
"Descrizione" => $commessaWeb["Descrizione"] ?? "",
"CommesseCustomFields" => $commessaAfterPatch["CommesseCustomFields"] ?? [],
"Campioni" => $campioni,
"Inviata" => 0 // Non inviato, come richiesto
];
echo json_encode([
"success" => true,
"commessaWeb" => $finalCommessa,
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
"totalCampioni" => count($campioni),
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
"message" => "Export successful",
"logFiles" => [
"step5_create" => $logFileStep5,
"step6_campioni" => $logFileStep6,
"step7_patch" => $logFileStep7,
"step10_get" => $logFileStep10
]
]);
} catch (Exception $e) {
error_log("LIMS Export Error: " . $e->getMessage() . "\nTrace: " . $e->getTraceAsString());
echo json_encode([
"success" => false,
"message" => "Export failed: " . $e->getMessage(),
"logFiles" => [
"step5_create" => $logFileStep5 ?? null,
"step6_campioni" => $logFileStep6 ?? null,
"step7_patch" => $logFileStep7 ?? null,
"step10_get" => $logFileStep10 ?? null
]
]);
}
-150
View File
@@ -1,150 +0,0 @@
<?php
// Abilita il debug degli errori (solo per sviluppo)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Assicurati che non ci sia output prima del JSON
ob_start();
// Imposta l'header per JSON
header('Content-Type: application/json');
// Configura la tua chiave API di TrackingMore
$apiKey = 'u4ssgynn-xuyy-9act-ca29-2glsv2qlzh0w'; // La tua chiave API reale
// Funzione per inviare una risposta JSON e terminare l'esecuzione
function sendResponse($data)
{
ob_end_clean();
echo json_encode($data);
exit;
}
// Verifica che il numero di tracking e il corriere siano stati inviati
if (!isset($_POST['tracking_number']) || empty($_POST['tracking_number']) || !isset($_POST['courier_code']) || empty($_POST['courier_code'])) {
sendResponse(['success' => false, 'message' => 'Numero di tracking o corriere non fornito']);
}
$trackingNumber = $_POST['tracking_number'];
$courierCode = $_POST['courier_code'];
// Lista dei corrieri validi per validazione
$validCarriers = ['tnt-it', 'dhl', 'gls', 'sda', 'ups'];
if (!in_array($courierCode, $validCarriers)) {
sendResponse(['success' => false, 'message' => 'Corriere non valido']);
}
// Imposta il nome del corriere in base al codice
$carrierNames = [
'tnt-it' => 'TNT Italy',
'dhl' => 'DHL',
'gls' => 'GLS',
'sda' => 'SDA',
'ups' => 'UPS'
];
$courierName = $carrierNames[$courierCode];
// Funzione per fare una richiesta cURL a TrackingMore
function makeRequest($url, $data, $apiKey, $method = 'POST')
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Tracking-Api-Key: ' . $apiKey,
'Content-Type: application/json'
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
$jsonData = json_encode($data, JSON_PRETTY_PRINT);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
file_put_contents('debug.log', "Encoded JSON Data: $jsonData\n", FILE_APPEND);
}
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
file_put_contents('debug.log', "Request URL: $url\nRequest Data: " . json_encode($data) . "\nResponse: $response\nHTTP Code: $httpCode\nError: $error\n", FILE_APPEND);
if ($response === false) {
return [
'success' => false,
'code' => $httpCode,
'message' => 'Errore nella richiesta API: ' . $error
];
}
$decodedResponse = json_decode($response, true);
if ($decodedResponse === null) {
return [
'success' => false,
'code' => $httpCode,
'message' => 'Risposta API non valida (non è JSON)'
];
}
$decodedResponse['success'] = isset($decodedResponse['meta']['code']) && ($decodedResponse['meta']['code'] === 200 || $decodedResponse['meta']['code'] === 201);
$decodedResponse['http_code'] = $httpCode;
return $decodedResponse;
}
// Step 1: Prova a creare il tracking
$createUrl = 'https://api.trackingmore.com/v4/trackings/create';
$createData = [
'tracking_number' => $trackingNumber,
'courier_code' => $courierCode
];
$createResponse = makeRequest($createUrl, $createData, $apiKey);
file_put_contents('debug.log', "Create Response: " . json_encode($createResponse) . "\n", FILE_APPEND);
$trackingInfo = null;
if ($createResponse['success']) {
// Creazione riuscita, usa i dati restituiti
$trackingInfo = $createResponse['data'];
} else {
// Controlla se l'errore è "Tracking No. already exists" (4101)
if (isset($createResponse['meta']['code']) && $createResponse['meta']['code'] === 4101) {
// Il tracking esiste già, usa /trackings (GET) con tracking_number e courier_code
$getUrl = 'https://api.trackingmore.com/v4/get?tracking_numbers=' . urlencode($trackingNumber);
$getResponse = makeRequest($getUrl, [], $apiKey, 'GET');
file_put_contents('debug.log', "Get Response: " . json_encode($getResponse) . "\n", FILE_APPEND);
if ($getResponse['success'] && !empty($getResponse['data']['items']) && !empty($getResponse['data']['items'][0])) {
$trackingInfo = $getResponse['data']['items'][0];
} else {
$errorMessage = isset($getResponse['meta']['message']) ? $getResponse['meta']['message'] : 'Errore sconosciuto';
sendResponse(['success' => false, 'message' => 'Errore nel recupero del tracking esistente: ' . $errorMessage]);
}
} else {
// Altro errore nella creazione
$errorMessage = isset($createResponse['meta']['message']) ? $createResponse['meta']['message'] : 'Errore sconosciuto';
sendResponse(['success' => false, 'message' => 'Errore nella creazione del tracking: ' . $errorMessage]);
}
}
if (!$trackingInfo) {
sendResponse(['success' => false, 'message' => 'Nessuna informazione di tracking trovata']);
}
// Estrai la data di consegna e il firmatario
$deliveryDate = 'Non disponibile';
$signedBy = 'Non disponibile';
if (isset($trackingInfo['origin_info']['trackinfo']) && is_array($trackingInfo['origin_info']['trackinfo'])) {
foreach ($trackingInfo['origin_info']['trackinfo'] as $checkpoint) {
if (isset($checkpoint['checkpoint_delivery_status']) && $checkpoint['checkpoint_delivery_status'] === 'delivered') {
$deliveryDate = $checkpoint['checkpoint_date'] ?? 'Non disponibile';
$signedBy = $trackingInfo['signed_by'] ?? 'Non disponibile';
break;
}
}
}
// Restituisci i dati al frontend
sendResponse([
'success' => true,
'deliveryDate' => $deliveryDate,
'signedBy' => $signedBy,
'carrierName' => $courierName
]);
@@ -1,166 +0,0 @@
<?php
// Abilita il debug degli errori (solo per sviluppo)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Assicurati che non ci sia output prima del JSON
ob_start();
// Imposta l'header per JSON
header('Content-Type: application/json');
// Configura la tua chiave API di 17Track
$apiKey = '489F6B6DADDE09A5B6CB1C42B5363A3F'; // Sostituisci con la tua chiave API reale
// Funzione per inviare una risposta JSON e terminare l'esecuzione
function sendResponse($data)
{
ob_end_clean();
echo json_encode($data);
exit;
}
// Verifica che il numero di tracking sia stato inviato
if (!isset($_POST['tracking_number']) || empty($_POST['tracking_number'])) {
sendResponse(['success' => false, 'message' => 'Numero di tracking non fornito']);
}
$trackingNumber = $_POST['tracking_number'];
// Funzione per fare una richiesta cURL a 17Track
function makeRequest($url, $data, $apiKey, $method = 'POST')
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'17token: ' . $apiKey,
'Content-Type: application/json'
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
file_put_contents('debug.log', "Request URL: $url\nRequest Data: " . json_encode($data) . "\nResponse: $response\nHTTP Code: $httpCode\nError: $error\n", FILE_APPEND);
if ($response === false || $httpCode !== 200) {
return [
'success' => false,
'code' => $httpCode,
'message' => 'Errore nella richiesta API: HTTP ' . $httpCode . ' - ' . $error
];
}
$decodedResponse = json_decode($response, true);
if ($decodedResponse === null) {
return [
'success' => false,
'code' => $httpCode,
'message' => 'Risposta API non valida (non è JSON)'
];
}
return $decodedResponse;
}
// Step 1: Prova con auto-detection
$trackUrl = 'https://api.17track.net/track/v2/register';
$trackData = [
[
'number' => $trackingNumber,
'carrier' => null,
'auto_detection' => true
]
];
$registerResponse = makeRequest($trackUrl, $trackData, $apiKey);
file_put_contents('debug.log', "Register Response (Auto-detect): " . json_encode($registerResponse) . "\n", FILE_APPEND);
if (!isset($registerResponse['data']['accepted']) || empty($registerResponse['data']['accepted'])) {
$errorMessage = $registerResponse['data']['rejected'][0]['error']['message'] ?? 'Errore sconosciuto';
sendResponse(['success' => false, 'message' => 'Errore nella registrazione del tracking: ' . $errorMessage]);
}
// Step 2: Recupera i dettagli con riprova
$getUrl = 'https://api.17track.net/track/v2/gettrackinfo';
$getData = [['number' => $trackingNumber]];
$maxAttempts = 3;
$delaySeconds = 5;
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
$trackResponse = makeRequest($getUrl, $getData, $apiKey);
file_put_contents('debug.log', "Track Response (Attempt $attempt): " . json_encode($trackResponse) . "\n", FILE_APPEND);
if (isset($trackResponse['data']['accepted']) && !empty($trackResponse['data']['accepted'])) {
break;
}
if ($attempt < $maxAttempts) {
sleep($delaySeconds);
}
}
// Se auto-detection ha successo, procedi
if (isset($trackResponse['data']['accepted']) && !empty($trackResponse['data']['accepted'])) {
$trackingInfo = $trackResponse['data']['accepted'][0]['track'] ?? null;
if (!$trackingInfo) {
sendResponse(['success' => false, 'message' => 'Nessuna informazione di tracking trovata']);
}
$deliveryDate = 'Non disponibile';
$signedBy = 'Non disponibile';
$carrierName = $trackingInfo['carrier']['name'] ?? 'Sconosciuto';
if (isset($trackingInfo['z0']['e']) && $trackingInfo['z0']['e'] == 40) {
$deliveryDate = $trackingInfo['z0']['z'] ?? 'Non disponibile';
$signedBy = $trackingInfo['z0']['d'] ?? 'Non disponibile';
}
sendResponse([
'success' => true,
'deliveryDate' => $deliveryDate,
'signedBy' => $signedBy,
'carrierName' => $carrierName
]);
}
// Step 3: Se auto-detection fallisce, prova una lista di corrieri comuni
$commonCarriers = [
['code' => 100003, 'name' => 'TNT'], // TNT
['code' => 100001, 'name' => 'DHL'], // DHL Express
['code' => 100065, 'name' => 'DHL eCommerce'], // DHL eCommerce
['code' => 100002, 'name' => 'UPS'] // UPS
];
$possibleCarriers = [];
foreach ($commonCarriers as $carrier) {
$trackData = [
[
'number' => $trackingNumber,
'carrier' => $carrier['code'],
'auto_detection' => false
]
];
$registerResponse = makeRequest($trackUrl, $trackData, $apiKey);
if (isset($registerResponse['data']['accepted']) && !empty($registerResponse['data']['accepted'])) {
$possibleCarriers[] = $carrier['name'];
}
sleep(1); // Piccolo ritardo per evitare limiti di rate
}
if (!empty($possibleCarriers)) {
sendResponse([
'success' => false,
'message' => 'Auto-detection fallita. Seleziona un corriere tra quelli possibili.',
'possibleCarriers' => $possibleCarriers
]);
} else {
sendResponse(['success' => false, 'message' => 'Nessun corriere identificato per questo numero di tracking']);
}
@@ -1,166 +0,0 @@
<?php
// Abilita il debug degli errori (solo per sviluppo)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Assicurati che non ci sia output prima del JSON
ob_start();
// Imposta l'header per JSON
header('Content-Type: application/json');
// Configura la tua chiave API di TrackingMore
$apiKey = 'u4ssgynn-xuyy-9act-ca29-2glsv2qlzh0w'; // La tua chiave API reale
// Funzione per inviare una risposta JSON e terminare l'esecuzione
function sendResponse($data)
{
ob_end_clean();
echo json_encode($data);
exit;
}
// Verifica che il numero di tracking sia stato inviato
if (!isset($_POST['tracking_number']) || empty($_POST['tracking_number'])) {
sendResponse(['success' => false, 'message' => 'Numero di tracking non fornito']);
}
$trackingNumber = $_POST['tracking_number'];
// Funzione per fare una richiesta cURL a TrackingMore
function makeRequest($url, $data, $apiKey, $method = 'POST')
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Tracking-Api-Key: ' . $apiKey,
'Content-Type: application/json'
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
file_put_contents('debug.log', "Request URL: $url\nRequest Data: " . json_encode($data) . "\nResponse: $response\nHTTP Code: $httpCode\nError: $error\n", FILE_APPEND);
if ($response === false || ($httpCode !== 200 && $httpCode !== 201)) {
return [
'success' => false,
'code' => $httpCode,
'message' => 'Errore nella richiesta API: HTTP ' . $httpCode . ' - ' . $error
];
}
$decodedResponse = json_decode($response, true);
if ($decodedResponse === null) {
return [
'success' => false,
'code' => $httpCode,
'message' => 'Risposta API non valida (non è JSON)'
];
}
$decodedResponse['success'] = isset($decodedResponse['meta']['code']) && ($decodedResponse['meta']['code'] === 200 || $decodedResponse['meta']['code'] === 201);
return $decodedResponse;
}
// Step 1: Identifica il corriere
$detectUrl = 'https://api.trackingmore.com/v4/couriers/detect';
$detectData = ['tracking_number' => $trackingNumber];
$detectResponse = makeRequest($detectUrl, $detectData, $apiKey);
file_put_contents('debug.log', "Detect Response: " . json_encode($detectResponse) . "\n", FILE_APPEND);
if (!$detectResponse['success']) {
$errorMessage = isset($detectResponse['meta']['message']) ? $detectResponse['meta']['message'] : 'Errore sconosciuto';
sendResponse(['success' => false, 'message' => 'Errore nella rilevazione del corriere: ' . $errorMessage]);
}
$couriers = $detectResponse['data'] ?? [];
if (empty($couriers)) {
sendResponse(['success' => false, 'message' => 'Corriere non identificato per il numero di tracking']);
}
// Prendi il primo corriere per ora
$courierCode = $couriers[0]['courier_code'] ?? null;
$courierName = $couriers[0]['courier_name'] ?? 'Sconosciuto';
if (!$courierCode) {
sendResponse(['success' => false, 'message' => 'Corriere non identificato']);
}
// Step 2: Crea un tracking
$createUrl = 'https://api.trackingmore.com/v4/trackings/create';
$createData = [
'tracking_number' => $trackingNumber,
'courier_code' => $courierCode
];
$createResponse = makeRequest($createUrl, $createData, $apiKey);
file_put_contents('debug.log', "Create Response: " . json_encode($createResponse) . "\n", FILE_APPEND);
if (!$createResponse['success']) {
$errorMessage = isset($createResponse['meta']['message']) ? $createResponse['meta']['message'] : 'Errore sconosciuto';
sendResponse(['success' => false, 'message' => 'Errore nella creazione del tracking: ' . $errorMessage]);
}
// Step 3: Recupera i dettagli del tracking con riprova
$getUrl = 'https://api.trackingmore.com/v4/trackings/get?tracking_number=' . urlencode($trackingNumber);
$maxAttempts = 3;
$delaySeconds = 5;
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
$trackResponse = makeRequest($getUrl, [], $apiKey, 'GET');
file_put_contents('debug.log', "Track Response (Attempt $attempt): " . json_encode($trackResponse) . "\n", FILE_APPEND);
if ($trackResponse['success'] && !empty($trackResponse['data'])) {
break;
}
if ($attempt < $maxAttempts) {
sleep($delaySeconds);
}
}
if (!$trackResponse['success']) {
$errorMessage = isset($trackResponse['meta']['message']) ? $trackResponse['meta']['message'] : 'Errore sconosciuto';
sendResponse(['success' => false, 'message' => 'Errore nel recupero delle informazioni: ' . $errorMessage]);
}
$trackingInfo = $trackResponse['data'] ?? null;
if (!$trackingInfo) {
// Se ci sono più corrieri rilevati, restituisci una lista per la tendina
if (count($couriers) > 1) {
$possibleCarriers = array_map(function ($courier) {
return ['code' => $courier['courier_code'], 'name' => $courier['courier_name']];
}, $couriers);
sendResponse([
'success' => false,
'message' => 'Dati non trovati. Seleziona un corriere tra quelli rilevati.',
'possibleCarriers' => $possibleCarriers
]);
}
sendResponse(['success' => false, 'message' => 'Nessuna informazione di tracking trovata']);
}
// Estrai la data di consegna e il firmatario
$deliveryDate = 'Non disponibile';
$signedBy = 'Non disponibile';
foreach ($trackingInfo['trackinfo'] as $checkpoint) {
if ($checkpoint['status'] === 'delivered') {
$deliveryDate = $checkpoint['Date'];
$signedBy = $checkpoint['signed_by'] ?? 'Non disponibile';
break;
}
}
// Restituisci i dati al frontend
sendResponse([
'success' => true,
'deliveryDate' => $deliveryDate,
'signedBy' => $signedBy,
'carrierName' => $courierName
]);
-74
View File
@@ -1,74 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
// Disable PHP error display
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// Parametri OData
$params = [
'$select' => 'IdCliente,Nominativo,CodiceNazioneFatturazione',
'$orderby' => 'Nominativo asc'
];
// Costruisce query string con encoding corretto
$queryString = http_build_query($params);
// Componi endpoint finale
$endpoint = "Cliente?$queryString";
// Funzione per eseguire la chiamata con retry
function makeApiRequest($api, $endpoint, $maxRetries = 3)
{
for ($retry = 0; $retry < $maxRetries; $retry++) {
try {
// Tenta la chiamata API
$data = $api->get($endpoint);
// Salva risposta per debug
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
return $data;
} catch (Exception $e) {
$errorMessage = $e->getMessage();
// Controlla se l'errore è legato all'autenticazione (HTTP 400 con messaggio specifico)
if (strpos($errorMessage, 'HTTP 400') !== false && strpos($errorMessage, 'Cannot persist the object') !== false) {
// Forza il refresh del token
try {
// Assumi che VisualLimsApiClient abbia un metodo per il refresh del token
$api->refreshToken(); // Da implementare in VisualLimsApiClient se non esiste
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
} catch (Exception $refreshEx) {
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
}
// Ritarda leggermente prima del retry
usleep(500000); // 500ms
continue;
}
// Altri errori non gestiti dal retry
throw $e;
}
}
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
}
// Esegui la chiamata con retry
$data = makeApiRequest($api, $endpoint);
echo json_encode($data);
} catch (Exception $e) {
http_response_code(500);
$errorResponse = [
'error' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
];
error_log("Errore in get_clienti.php: " . json_encode($errorResponse));
echo json_encode($errorResponse);
}
-39
View File
@@ -1,39 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// ID della CommessaWeb specifica (cambialo di volta in volta)
$id = 529435; // TODO: Cambia questo valore con l'ID desiderato
// Endpoint per recuperare la CommessaWeb specifica con espansione dello schema custom
$endpoint = "CommessaWeb({$id})";
// Opzioni per l'espansione: includi OrderCustomFields per ottenere i campi custom dello schema assegnato all'ordine
$options = ['$expand' => 'OrderCustomFields'];
// Debug: salva URL usato
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Salva il JSON in locale
file_put_contents(__DIR__ . '/commessaweb_schema_response.json', json_encode($data, JSON_PRETTY_PRINT));
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
@@ -1,41 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// მივიღოთ მრავლობითი field_ids
$fieldIds = [];
if (isset($_GET['field_ids'])) {
$fieldIds = array_filter(array_map('intval', explode(',', $_GET['field_ids'])));
}
// თუ არ გადმოგვცეს -> ერთი default
if (empty($fieldIds)) {
$fieldIds = [156];
}
$results = [];
foreach ($fieldIds as $customFieldId) {
$endpoint = "CustomField($customFieldId)?\$expand=CustomFieldsValues";
$data = $api->get($endpoint);
$results[$customFieldId] = $data['CustomFieldsValues'] ?? [];
}
// Debug ფაილი
file_put_contents(__DIR__ . '/customfield_values_response.json', json_encode($results));
echo json_encode($results);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'error' => $e->getMessage()
]);
}
-59
View File
@@ -1,59 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Risale a root/vendor/
use Dotenv\Dotenv;
// Set JSON header
header('Content-Type: application/json');
// Debug: Log the path where we expect the .env file
$envPath = dirname(__DIR__, 2);
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
// Carica il file .env dalla root del progetto
try {
$dotenv = Dotenv::createImmutable($envPath);
$dotenv->load();
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
echo json_encode(['success' => false, 'message' => 'Errore caricamento configurazione: ' . $e->getMessage()]);
exit(1);
}
// Recupera le variabili d'ambiente
$dbHost = $_ENV['DB_HOST'];
$dbName = $_ENV['DB_DATABASE'];
$dbUser = $_ENV['DB_USERNAME'];
$dbPass = $_ENV['DB_PASSWORD'];
$dbPrefix = $_ENV['DB_PREFIX'];
// Debug: Log database connection details (excluding password)
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
// Connessione al database MySQL
try {
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
echo json_encode(['success' => false, 'message' => 'Errore connessione al database: ' . $e->getMessage()]);
exit(1);
}
try {
// Query per recuperare i valori distinti di MacroMatrice, escludendo quelli che iniziano con '*' e ordinandoli
$query = "SELECT DISTINCT MacroMatrice FROM {$dbPrefix}matrici WHERE MacroMatrice IS NOT NULL AND MacroMatrice NOT LIKE '*%' ORDER BY MacroMatrice ASC";
$stmt = $pdo->prepare($query);
$stmt->execute();
$macroMatrici = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Debug: Log del numero di MacroMatrice recuperate
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Retrieved ' . count($macroMatrici) . ' MacroMatrice from database' . PHP_EOL, FILE_APPEND);
// Restituisci risposta JSON
echo json_encode(['success' => true, 'value' => $macroMatrici]);
} catch (PDOException $e) {
// Log errore e restituisci risposta di errore
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore nel recupero delle MacroMatrice: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
echo json_encode(['success' => false, 'message' => 'Errore nel recupero delle MacroMatrice: ' . $e->getMessage()]);
exit(1);
}
-36
View File
@@ -1,36 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// Endpoint per recuperare le Matrici
$endpoint = 'Matrice';
// (Opzionale) aggiungi parametri, ad esempio $top per limitare i risultati
$options = []; // oppure ad esempio: ['$top' => 100]
// Debug: salva URL usato
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Salva il JSON in locale
file_put_contents(__DIR__ . '/matrici_response.json', json_encode($data, JSON_PRETTY_PRINT));
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
-59
View File
@@ -1,59 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Risale a root/vendor/
use Dotenv\Dotenv;
// Set JSON header
header('Content-Type: application/json');
// Debug: Log the path where we expect the .env file
$envPath = dirname(__DIR__, 2);
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
// Carica il file .env dalla root del progetto
try {
$dotenv = Dotenv::createImmutable($envPath);
$dotenv->load();
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
echo json_encode(['success' => false, 'message' => 'Errore caricamento configurazione: ' . $e->getMessage()]);
exit(1);
}
// Recupera le variabili d'ambiente
$dbHost = $_ENV['DB_HOST'];
$dbName = $_ENV['DB_DATABASE'];
$dbUser = $_ENV['DB_USERNAME'];
$dbPass = $_ENV['DB_PASSWORD'];
$dbPrefix = $_ENV['DB_PREFIX'];
// Debug: Log database connection details (excluding password)
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
// Connessione al database MySQL
try {
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
echo json_encode(['success' => false, 'message' => 'Errore connessione al database: ' . $e->getMessage()]);
exit(1);
}
try {
// Query per recuperare le matrici, includendo MacroMatrice, escludendo quelle che iniziano con '*' e ordinandole
$query = "SELECT IdMatrice, NomeMatrice, MacroMatrice FROM {$dbPrefix}matrici WHERE NomeMatrice NOT LIKE '*%' ORDER BY NomeMatrice ASC";
$stmt = $pdo->prepare($query);
$stmt->execute();
$matrici = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Debug: Log del numero di matrici recuperate
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Retrieved ' . count($matrici) . ' matrices from database' . PHP_EOL, FILE_APPEND);
// Restituisci risposta JSON
echo json_encode(['success' => true, 'value' => $matrici]);
} catch (PDOException $e) {
// Log errore e restituisci risposta di errore
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore nel recupero delle matrici: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
echo json_encode(['success' => false, 'message' => 'Errore nel recupero delle matrici: ' . $e->getMessage()]);
exit(1);
}
-37
View File
@@ -1,37 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClientXml.class.php';
header('Content-Type: application/xml; charset=utf-8');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClientXml::getInstance();
// Endpoint per i metadata
$endpoint = '$metadata';
// Nessun parametro aggiuntivo necessario
$options = [];
// Debug: salva URL usato
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Salva il file XML in locale
file_put_contents(__DIR__ . '/metadata_response.xml', $data);
// Restituisci il contenuto XML
echo $data;
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(500);
echo '<?xml version="1.0" encoding="utf-8"?><error>' . htmlspecialchars($e->getMessage()) . '</error>';
}
-26
View File
@@ -1,26 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
$rapporto_id = 533329;
// Costruzione manuale dell'endpoint con espansione annidata
$endpoint = "Rapporto($rapporto_id)?\$expand=CampioniDatiRapporto(\$expand=AnalisiDatiRapporto,CustomFieldsDatiRapporto)";
// Non passiamo options, già incluso nell'endpoint
$data = $api->get($endpoint);
file_put_contents(__DIR__ . '/rapporto_expanded.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
-30
View File
@@ -1,30 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// ID dello schema passato via GET o default 45
$schemaId = isset($_GET['id']) && is_numeric($_GET['id']) ? intval($_GET['id']) : 42;
// IMPORTANTE: $expand va dentro la stringa endpoint per entità singola
$endpoint = "SchemaCustomField($schemaId)?\$expand=SchemiCustomFieldsDettagli(\$expand=CustomField)";
// Nessun parametro aggiuntivo
$data = $api->get($endpoint);
// Salva la risposta per debug
file_put_contents(__DIR__ . '/schema_dettagli_response.json', json_encode($data));
echo json_encode($data);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'error' => $e->getMessage()
]);
}
-36
View File
@@ -1,36 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// Nessun filtro o espansione: solo la lista degli schemi
$endpoint = 'SchemaCustomField';
// (Opzionale) aggiungi $top se vuoi limitare i risultati
$options = []; // oppure ad esempio: ['$top' => 100]
// Debug: salva URL usato
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Salva il JSON in locale
file_put_contents(__DIR__ . '/schemi_base_response.json', json_encode($data, JSON_PRETTY_PRINT));
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
@@ -1,26 +0,0 @@
```php
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Torna al livello di public per trovare vendor/
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
// Disabilita la visualizzazione degli errori PHP per evitare output HTML
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
$data = $api->get("SchemaCustomField"); // Recupera la lista degli schemi custom fields
// Salva la risposta in un file per debug
file_put_contents(__DIR__ . '/schemi_custom_fields_response.json', json_encode($data));
echo json_encode($data);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'error' => $e->getMessage()
]);
}
?>
File diff suppressed because it is too large Load Diff
-153
View File
@@ -1,153 +0,0 @@
<?php include('include/headscript.php'); ?>
<!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'); ?>
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
<style>
/* Layout flessibile per gestire dimensioni diverse */
#templateButtons {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: flex-start;
/* Allinea a sinistra */
padding: 20px;
}
/* Definizione delle dimensioni */
/* Definizione delle dimensioni */
.btn-small {
font-size: 12px;
padding: 6px 12px;
min-width: 100px;
min-height: 30px;
display: flex;
/* Aggiunto */
justify-content: center;
/* Aggiunto */
align-items: center;
/* Aggiunto */
}
.btn-medium {
font-size: 16px;
padding: 10px 20px;
min-width: 130px;
min-height: 45px;
display: flex;
/* Aggiunto */
justify-content: center;
/* Aggiunto */
align-items: center;
/* Aggiunto */
}
.btn-large {
font-size: 20px;
padding: 14px 28px;
min-width: 180px;
min-height: 60px;
display: flex;
/* Aggiunto */
justify-content: center;
/* Aggiunto */
align-items: center;
/* Aggiunto */
}
/* Stile della barra di ricerca */
#searchInput {
width: 100%;
padding: 10px;
font-size: 16px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<h6 class="mb-0">Active Templates</h6>
</div>
<div class="card-body">
<!-- Barra di ricerca -->
<input type="text" id="searchInput" placeholder="Search template...">
<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'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script>
document.addEventListener("DOMContentLoaded", function() {
fetch("load_active_templates.php")
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("Error loading templates:", data.message);
return;
}
let templateButtons = document.getElementById("templateButtons");
templateButtons.innerHTML = '';
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() {
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>
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-173
View File
@@ -1,173 +0,0 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/import_debug.log');
if (!file_exists(__DIR__ . '/import_debug.log')) {
file_put_contents(__DIR__ . '/import_debug.log', "Inizio importazione alle " . date('Y-m-d H:i:s') . "\n", FILE_APPEND);
}
// Log iniziale
error_log("Inizio importazione alle " . date('Y-m-d H:i:s'));
include('include/headscript.php');
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
exit;
}
$template_id = intval($_POST['template_id']);
$selected_rows = array_map('intval', $_POST['selected_rows']);
$columns = json_decode($_POST['columns'], true);
$rows = json_decode($_POST['rows'], true);
$excelrows = json_decode($_POST['excelrows'], true);
$newFilename = htmlspecialchars($_POST['filename']);
$_SESSION['template_id'] = $template_id;
$_SESSION['selected_rows'] = $selected_rows;
$_SESSION['columns'] = $columns;
$_SESSION['rows'] = $rows;
$_SESSION['excelrows'] = $excelrows;
$_SESSION['filename'] = $newFilename;
error_log("Received Data - Template ID: $template_id, Selected Rows: " . json_encode($selected_rows));
error_log("Columns: " . json_encode($columns));
error_log("Rows: " . json_encode($rows));
error_log("Excelrows: " . json_encode($excelrows));
$user_id = $iduserlogin ?? 1;
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Genera un UUID univoco per importreferencecode
$importReferenceCode = date('YmdHis') . '-' . uniqid();
// Recupera tutti i mapping dal template
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field FROM template_mapping WHERE template_id = ?");
$stmt->execute([$template_id]);
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
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) {
$mainFieldMapping = $mapping;
break;
}
}
// Inserisci le righe selezionate in datadb
$insertedIds = [];
foreach ($selected_rows as $rowIndex) {
$row = $rows[$rowIndex] ?? null;
$excelrow = $excelrows[$rowIndex] ?? null;
if ($row === null || $excelrow === null) {
error_log("Errore: riga o excelrow mancante per rowIndex $rowIndex");
continue;
}
// Recupera l'idclient di default dal template
$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;
$values = [
$template_id,
$importReferenceCode,
$newFilename,
'i',
$user_id,
null,
date('Y-m-d'),
$excelrow,
$default_idclient // Aggiungi idclient
];
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate, excelrow, idclient) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute($values);
$iddatadb = $pdo->lastInsertId();
$insertedIds[] = $iddatadb;
// Inserisci tutti i campi in import_data_details
foreach ($allMappings as $mapping) {
$fieldValue = null;
if (!$mapping['is_manual']) {
$excelColumn = trim($mapping['excel_column']);
$excelColumnIndex = array_search($excelColumn, array_map('trim', $columns));
if ($excelColumnIndex !== false && isset($row[$excelColumnIndex]) && $row[$excelColumnIndex] !== '') {
$fieldValue = $row[$excelColumnIndex];
error_log("Found Excel column '$excelColumn' at index $excelColumnIndex, value: " . var_export($fieldValue, true));
} else {
$fieldValue = $mapping['manual_default'] ?? '';
error_log("Excel column '$excelColumn' not found or empty, using default: " . var_export($fieldValue, true));
}
switch ($mapping['data_type']) {
case 'INT':
$fieldValue = is_numeric($fieldValue) ? (int)$fieldValue : ($mapping['manual_default'] ?? 0);
break;
case 'DATE':
$fieldValue = !empty($fieldValue) ? date('Y-m-d', strtotime($fieldValue)) : ($mapping['manual_default'] === 'today' ? date('Y-m-d') : ($mapping['manual_default'] ?? ''));
break;
case 'CHAR':
$fieldValue = !empty($fieldValue) ? substr((string)$fieldValue, 0, 1) : ($mapping['manual_default'] ?? '');
break;
case 'Testo':
case 'VARCHAR':
default:
$fieldValue = !empty($fieldValue) ? htmlspecialchars((string)$fieldValue) : ($mapping['manual_default'] ?? '');
break;
}
} else {
$fieldValue = $mapping['manual_default'] ?? '';
if ($mapping['data_type'] === 'DATE' && $mapping['manual_default'] === 'today') {
$fieldValue = date('Y-m-d');
}
}
if ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
}
error_log("Inserting into import_data_details - Mapping ID: " . $mapping['id'] . ", Field Value: " . var_export($fieldValue, true) . ", Is Manual: " . $mapping['is_manual'] . ", Excel Column: " . ($mapping['excel_column'] ?? 'N/A') . ", Manual Default: " . ($mapping['manual_default'] ?? 'N/A'));
$stmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
$stmt->execute([$iddatadb, $mapping['id'], $fieldValue]);
error_log("Inserted into import_data_details for ID $iddatadb, Mapping ID: " . $mapping['id'] . ", Field Value: " . var_export($fieldValue, true));
}
}
$_SESSION['inserted_ids'] = $insertedIds;
$params = [
'template_id' => $template_id,
'filename' => $newFilename,
];
?>
<form id="redirectForm" action="import_edit2.php" method="post">
<input type="hidden" name="template_id" value="<?= htmlspecialchars($template_id) ?>">
<input type="hidden" name="filename" value="<?= htmlspecialchars($newFilename) ?>">
<?php foreach ($selected_rows as $row): ?>
<input type="hidden" name="selected_rows[]" value="<?= htmlspecialchars($row) ?>">
<?php endforeach; ?>
<?php foreach ($insertedIds as $id): ?>
<input type="hidden" name="inserted_ids[]" value="<?= htmlspecialchars($id) ?>">
<?php endforeach; ?>
<input type="hidden" name="columns" value='<?= json_encode($columns) ?>'>
<input type="hidden" name="rows" value='<?= json_encode($rows) ?>'>
<input type="hidden" name="excelrows" value='<?= json_encode($excelrows) ?>'>
</form>
<script>
document.getElementById('redirectForm').submit();
</script>
<?php
exit;
?>
+412
View File
@@ -0,0 +1,412 @@
<?php
include('include/headscript.php');
require_once(__DIR__ . '/class/db-functions.php'); // usa la tua classe PDO
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Imported Data Management</title>
<?php include('cssinclude.php'); ?>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">
<style>
.editable {
cursor: pointer;
background-color: #fff8e1;
}
.action-btn {
padding: 4px 8px;
font-size: 0.85rem;
}
td.editable:hover {
background-color: #fff5cc;
cursor: text;
}
td.modified {
background-color: #ff9c9cff !important;
/* evidenzia celle modificate */
}
td.noedit {
background-color: #f8f9fa;
color: #888;
}
</style>
</head>
<body>
<?php include('include/navbar.php'); ?>
<div class="dashboard-main-wrapper wrapper">
<?php include('include/topbar.php'); ?>
<div class="dashboard-body">
<div class="card shadow-sm">
<div class="card-body">
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3">
<h4 class="mb-0">Imported Data</h4>
<div class="d-flex align-items-center gap-2">
<label for="clientFilter" class="form-label mb-0 me-2">Filter by Client:</label>
<select id="clientFilter" class="form-select form-select-sm" style="width: 220px;">
<option value="">All Clients</option>
<?php
// carichiamo i clienti dal DB
$clients = $pdo->query("SELECT idclient, client_name FROM clients ORDER BY client_name")->fetchAll(PDO::FETCH_ASSOC);
foreach ($clients as $cl) {
echo '<option value="' . htmlspecialchars($cl['idclient']) . '">' . htmlspecialchars($cl['client_name']) . '</option>';
}
?>
</select>
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-2">
<div>
<button id="updatePricesBtn" class="btn btn-warning btn-sm" style="display:none;">
<i class="fas fa-sync-alt"></i> Aggiorna Prezzi
</button>
</div>
</div>
<div class="table-responsive">
<table id="importTable" class="table table-striped table-bordered w-100">
<thead>
<tr>
<th></th> <!-- Checkbox column -->
<th>ID</th>
<th>Collection</th>
<th>Category</th>
<th>Model</th>
<th>Model Description</th>
<th>Mod.Code</th>
<th>Description</th>
<th>Finish.Code</th>
<th>Finishing Description</th>
<th>Available Qty</th>
<th>WH</th>
<th>Price</th>
<th>Actions</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script>
$(document).ready(function() {
let table = $('#importTable').DataTable({
ajax: {
url: 'import_list_data.php',
dataSrc: ''
},
columns: [{
data: null,
orderable: false,
searchable: false,
className: 'text-center',
render: function() {
return '<input type="checkbox" class="row-check" />';
}
},
{
data: 'id'
},
{
data: 'collection',
className: 'editable'
},
{
data: 'category',
className: 'noedit text-muted'
},
{
data: 'model',
className: 'editable'
},
{
data: 'model_description',
className: 'editable'
},
{
data: 'mod_code',
className: 'text-muted'
},
{
data: 'description',
className: 'editable'
},
{
data: 'finish_code',
className: 'editable'
},
{
data: 'finishing_description',
className: 'editable'
},
{
data: 'available_qty',
className: 'editable'
},
{
data: 'wh',
className: 'editable'
},
{
data: 'price',
className: 'editable'
},
{
data: null,
orderable: false,
render: function(data, type, row) {
return `
<button class="btn btn-success btn-sm action-btn save-row" data-id="${row.id}">
<i class="fas fa-save"></i>
</button>
<button class="btn btn-danger btn-sm action-btn delete-row" data-id="${row.id}">
<i class="fas fa-trash"></i>
</button>
`;
}
}
],
pageLength: 25,
order: [
[1, 'desc']
],
responsive: true,
autoWidth: false
});
// === Filtro per Client ===
$('#clientFilter').on('change', function() {
const selectedClient = $(this).val();
// Ricarica i dati passando il parametro idclient
table.ajax.url('import_list_data.php' + (selectedClient ? '?idclient=' + selectedClient : '')).load();
});
// === Inline Editing ===
$('#importTable tbody').on('click', 'td.editable', function() {
const cell = table.cell(this);
const rowData = table.row(cell.index().row).data();
const colName = table.column(cell.index().column).dataSrc();
const originalValue = rowData[colName] ?? '';
// Evita doppia apertura
if ($(this).find('input').length > 0) return;
const input = $('<input type="text" class="form-control form-control-sm"/>')
.val(originalValue)
.css({
'min-width': '120px',
'height': '24px',
'padding': '0 4px'
});
$(this).html(input);
input.focus();
// Gestione conferma o uscita
input.on('blur keydown', function(e) {
if (e.type === 'blur' || e.key === 'Enter') {
const newValue = $(this).val();
// Aggiorna in memoria e interfaccia
rowData[colName] = newValue;
table.row(cell.index().row).data(rowData).draw(false);
const td = $(table.cell(cell.index()).node());
if (newValue !== originalValue) {
td.addClass('modified'); // evidenzia modificato
} else {
td.removeClass('modified');
}
}
});
});
// === Salvataggio singola riga ===
$('#importTable').on('click', '.save-row', function() {
const rowData = table.row($(this).closest('tr')).data();
$.ajax({
url: 'import_list_save.php',
type: 'POST',
data: {
row: JSON.stringify(rowData)
},
success: function(res) {
Swal.close();
// Se il server restituisce già JSON, non serve JSON.parse()
let json = (typeof res === 'object') ? res : null;
if (!json) {
try {
json = JSON.parse(res);
} catch (e) {
console.error("Invalid JSON from server:", res);
Swal.fire('Errore', 'Risposta non valida dal server.', 'error');
return;
}
}
if (json.status === 'ok') {
Swal.fire({
title: 'Successo',
text: `Aggiornati ${json.updated} prezzi.`,
icon: 'success',
timer: 2000,
showConfirmButton: false
});
table.ajax.reload(null, false); // 🔁 aggiorna tabella
} else {
console.error("Errore backend:", json);
Swal.fire('Errore', json.message || 'Errore sconosciuto.', 'error');
}
},
error: function(xhr, status, err) {
Swal.fire('Errore', 'Errore di rete o server non raggiungibile.', 'error');
console.error("AJAX error:", status, err, xhr.responseText);
}
});
});
// === Eliminazione con conferma ===
$('#importTable').on('click', '.delete-row', function() {
const id = $(this).data('id');
Swal.fire({
title: 'Are you sure?',
text: 'This record will be permanently deleted.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, delete it',
cancelButtonText: 'Cancel'
}).then(result => {
if (result.isConfirmed) {
$.ajax({
url: 'import_list_delete.php',
type: 'POST',
data: {
id: id
},
success: function(res) {
let json;
try {
json = JSON.parse(res);
} catch (e) {
Swal.fire('Error', res, 'error');
return;
}
if (json.status === 'ok') {
Swal.fire('Deleted', 'Row removed successfully.', 'success');
table.ajax.reload(null, false);
} else {
Swal.fire('Error', json.message, 'error');
}
},
error: function() {
Swal.fire('Error', 'Request failed.', 'error');
}
});
}
});
});
// === Gestione selezione righe e pulsante Aggiorna Prezzi ===
$('#importTable').on('change', '.row-check', function() {
const checkedCount = $('#importTable .row-check:checked').length;
$('#updatePricesBtn').toggle(checkedCount > 0);
});
// === Aggiornamento prezzi con ChatPDF ===
$('#updatePricesBtn').on('click', function() {
const selectedRows = [];
$('#importTable .row-check:checked').each(function() {
const row = table.row($(this).closest('tr')).data();
if (row) selectedRows.push(row);
});
if (selectedRows.length === 0) {
Swal.fire('Notice', 'Seleziona almeno una riga.', 'info');
return;
}
const clientId = $('#clientFilter').val();
if (!clientId) {
Swal.fire('Notice', 'Seleziona prima un cliente.', 'info');
return;
}
Swal.fire({
title: 'Aggiornare i prezzi?',
text: `Verranno interrogati i prezzi per ${selectedRows.length} prodotti.`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Procedi',
cancelButtonText: 'Annulla'
}).then(result => {
if (result.isConfirmed) {
Swal.fire({
title: 'Aggiornamento in corso...',
allowOutsideClick: false,
didOpen: () => Swal.showLoading()
});
$.ajax({
url: 'update_prices_chatpdf.php',
type: 'POST',
data: {
rows: JSON.stringify(selectedRows),
idclient: clientId
},
success: function(res) {
Swal.close();
let json;
try {
json = JSON.parse(res);
} catch (e) {
Swal.fire('Errore', res, 'error');
return;
}
if (json.status === 'ok') {
Swal.fire('Successo', `Aggiornati ${json.updated} prezzi.`, 'success');
table.ajax.reload(null, false);
} else {
Swal.fire('Errore', json.message, 'error');
}
},
error: function() {
Swal.fire('Errore', 'Aggiornamento fallito.', 'error');
}
});
}
});
});
});
</script>
</body>
</html>
+21
View File
@@ -0,0 +1,21 @@
<?php
header('Content-Type: application/json; charset=utf-8');
include('include/headscript.php');
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$idclient = isset($_GET['idclient']) && is_numeric($_GET['idclient']) ? intval($_GET['idclient']) : null;
try {
if ($idclient) {
$stmt = $pdo->prepare("SELECT * FROM importdb WHERE idclient = :idclient ORDER BY id DESC");
$stmt->execute([':idclient' => $idclient]);
} else {
$stmt = $pdo->query("SELECT * FROM importdb ORDER BY id DESC");
}
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
+20
View File
@@ -0,0 +1,20 @@
<?php
include('include/headscript.php');
require_once(__DIR__ . '/../class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$id = intval($_POST['id'] ?? 0);
if ($id <= 0) {
echo json_encode(['status' => 'error', 'message' => 'Invalid ID']);
exit;
}
try {
$stmt = $pdo->prepare("DELETE FROM importdb WHERE id = :id");
$stmt->execute([':id' => $id]);
echo json_encode(['status' => 'ok']);
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
+47
View File
@@ -0,0 +1,47 @@
<?php
include('include/headscript.php');
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$row = json_decode($_POST['row'] ?? '', true);
if (!$row || !isset($row['id'])) {
echo json_encode(['status' => 'error', 'message' => 'Invalid data']);
exit;
}
try {
$sql = "UPDATE importdb SET
collection = :collection,
category = :category,
model = :model,
model_description = :model_description,
description = :description,
finish_code = :finish_code,
finishing_description = :finishing_description,
available_qty = :available_qty,
wh = :wh,
price = :price,
updated_on = CURRENT_TIMESTAMP
WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':collection' => $row['collection'] ?? null,
':category' => $row['category'] ?? null,
':model' => $row['model'] ?? null,
':model_description' => $row['model_description'] ?? null,
':description' => $row['description'] ?? null,
':finish_code' => $row['finish_code'] ?? null,
':finishing_description' => $row['finishing_description'] ?? null,
':available_qty' => $row['available_qty'] ?? null,
':wh' => $row['wh'] ?? null,
':price' => $row['price'] ?? null,
':id' => $row['id']
]);
echo json_encode(['status' => 'ok']);
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
+200
View File
@@ -0,0 +1,200 @@
<?php
include('include/headscript.php');
require_once(__DIR__ . '/class/db-functions.php'); // PDO class
use Smalot\PdfParser\Parser;
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// --- Carica clienti ---
$clients = $pdo->query("SELECT idclient, client_name FROM clients ORDER BY client_name")->fetchAll(PDO::FETCH_ASSOC);
// --- Messaggio di stato ---
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['price_pdf'])) {
$idclient = intval($_POST['idclient'] ?? 0);
if ($idclient > 0 && $_FILES['price_pdf']['error'] === UPLOAD_ERR_OK) {
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) mkdir($uploadDir, 0777, true);
$filename = basename($_FILES['price_pdf']['name']);
$targetPath = $uploadDir . $filename;
if (move_uploaded_file($_FILES['price_pdf']['tmp_name'], $targetPath)) {
try {
require_once(dirname(__DIR__, 2) . '/vendor/autoload.php');
$parser = new Parser();
$pdf = $parser->parseFile($targetPath);
$text = $pdf->getText();
// Salva testo completo per analisi
file_put_contents(__DIR__ . '/debug_pdf_text.txt', $text);
// Pattern iniziale: "499 1C 23 €123,45" o "499 1C 23 123,45"
$pattern = '/(\d{3})\s([A-Z0-9]{1,2})\s(\d{2})\s*[€]?\s?(\d{1,4},\d{2})/';
preg_match_all($pattern, $text, $matches, PREG_SET_ORDER);
// Salva tutti i match trovati
$debugMatches = "Matches found: " . count($matches) . "\n\n";
foreach ($matches as $m) {
$debugMatches .= implode(' | ', $m) . "\n";
}
file_put_contents(__DIR__ . '/debug_matches.txt', $debugMatches);
$inserted = 0;
$stmt = $pdo->prepare("
INSERT INTO price_mapping (idclient, price_ref, price, source_file)
VALUES (:idclient, :price_ref, :price, :source_file)
");
foreach ($matches as $m) {
$ref = trim($m[1] . $m[2] . $m[3]);
$price = str_replace(',', '.', $m[4]);
$stmt->execute([
':idclient' => $idclient,
':price_ref' => $ref,
':price' => $price,
':source_file' => $filename
]);
$inserted++;
}
$message = "<div class='alert alert-success mt-3'>✅ Imported $inserted prices successfully.</div>";
} catch (Exception $e) {
$message = "<div class='alert alert-danger mt-3'>Error parsing PDF: " . htmlspecialchars($e->getMessage()) . "</div>";
}
} else {
$message = "<div class='alert alert-danger mt-3'>Upload failed.</div>";
}
} else {
$message = "<div class='alert alert-warning mt-3'>Select a client and upload a valid PDF file.</div>";
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Import PDF Prices</title>
<?php include('cssinclude.php'); ?>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<style>
.form-section {
background: #f9f9f9;
border-radius: 8px;
padding: 20px;
}
</style>
</head>
<body>
<?php include('include/navbar.php'); ?>
<div class="dashboard-main-wrapper wrapper">
<?php include('include/topbar.php'); ?>
<div class="dashboard-body">
<div class="card shadow-sm">
<div class="card-body">
<h4 class="mb-3">📄 Import Price List from PDF</h4>
<form method="POST" enctype="multipart/form-data" class="form-section row g-3 align-items-end">
<div class="col-md-4">
<label for="idclient" class="form-label">Select Client</label>
<select name="idclient" id="idclient" class="form-select" required>
<option value="">-- Choose Client --</option>
<?php foreach ($clients as $cl): ?>
<option value="<?= htmlspecialchars($cl['idclient']) ?>">
<?= htmlspecialchars($cl['client_name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label for="price_pdf" class="form-label">Upload PDF File</label>
<input type="file" name="price_pdf" id="price_pdf" class="form-control" accept="application/pdf" required>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-upload"></i> Import
</button>
</div>
</form>
<?= $message; ?>
<hr>
<h5 class="mt-4 mb-2">📊 Imported Prices</h5>
<div class="table-responsive">
<table id="priceTable" class="table table-striped table-bordered w-100">
<thead>
<tr>
<th>ID</th>
<th>Client</th>
<th>Code Ref</th>
<th>Price ()</th>
<th>Source File</th>
<th>Created At</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script>
$(document).ready(function() {
$('#priceTable').DataTable({
ajax: {
url: 'price_mapping_list.php',
dataSrc: ''
},
columns: [{
data: 'id'
},
{
data: 'client_name'
},
{
data: 'price_ref'
},
{
data: 'price'
},
{
data: 'source_file'
},
{
data: 'created_at'
}
],
pageLength: 25,
order: [
[0, 'desc']
],
responsive: true,
autoWidth: false
});
});
</script>
</body>
</html>
+76
View File
@@ -0,0 +1,76 @@
<?php
include('include/headscript.php');
require_once(__DIR__ . '/class/db-functions.php'); // usa la tua classe PDO
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Ottieni dati dal POST
$rows = isset($_POST['rows']) ? json_decode($_POST['rows'], true) : [];
$idclient = isset($_POST['idclient']) ? intval($_POST['idclient']) : null;
// Recupera utente loggato
$iduser = $_SESSION['iduserlogin'] ?? null;
if (!$rows || !is_array($rows) || count($rows) === 0) {
echo json_encode(['status' => 'error', 'message' => 'No rows to import.']);
exit;
}
if (empty($idclient)) {
echo json_encode(['status' => 'error', 'message' => 'Client not specified.']);
exit;
}
try {
$pdo->beginTransaction();
$sql = "
INSERT INTO importdb
(collection, category, model, model_description, mod_code, description, finish_code, finishing_description, available_qty, wh, price, source_file, iduser, idclient)
VALUES
(:collection, :category, :model, :model_description, :mod_code, :description, :finish_code, :finishing_description, :available_qty, :wh, :price, :source_file, :iduser, :idclient)
ON DUPLICATE KEY UPDATE
collection = VALUES(collection),
category = VALUES(category),
model = VALUES(model),
model_description = VALUES(model_description),
description = VALUES(description),
finish_code = VALUES(finish_code),
finishing_description = VALUES(finishing_description),
available_qty = VALUES(available_qty),
wh = VALUES(wh),
price = VALUES(price),
updated_on = CURRENT_TIMESTAMP()
";
$stmt = $pdo->prepare($sql);
foreach ($rows as $r) {
if (isset($r['avalable_qty']) && !isset($r['available_qty'])) {
$r['available_qty'] = $r['avalable_qty'];
}
$stmt->execute([
':collection' => $r['collection'] ?? null,
':category' => $r['category'] ?? null,
':model' => $r['model'] ?? null,
':model_description' => $r['model_description'] ?? null,
':mod_code' => $r['mod_code'] ?? null,
':description' => $r['description'] ?? null,
':finish_code' => $r['finish_code'] ?? null,
':finishing_description' => $r['finishing_description'] ?? null,
':available_qty' => is_numeric($r['available_qty']) ? intval($r['available_qty']) : null,
':wh' => $r['wh'] ?? null,
':price' => isset($r['price']) && is_numeric($r['price']) ? floatval($r['price']) : null,
':source_file' => basename($_POST['source_file'] ?? 'upload.xlsx'),
':iduser' => $iduser,
':idclient' => $idclient
]);
}
$pdo->commit();
echo json_encode(['status' => 'ok', 'message' => count($rows) . ' rows imported/updated.']);
} catch (Exception $e) {
$pdo->rollBack();
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
+216 -261
View File
@@ -1,303 +1,258 @@
<?php
include('include/headscript.php');
// Controlla se è stato passato un ID valido
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
exit;
}
$id = intval($_GET['id']); // Sanifica l'ID
// Recupera il template dal database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
exit;
}
// Debug del template
error_log("Loaded template: " . print_r($template, true));
// Recupera lista clienti
$clients = $pdo->query("SELECT idclient, client_name FROM clients ORDER BY client_name")->fetchAll(PDO::FETCH_ASSOC);
?>
<!doctype html>
<!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" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Import XLS - Cassina Products</title>
<?php include('cssinclude.php'); ?>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">
<style>
.table-container {
overflow-x: auto;
max-width: 100%;
margin-bottom: 20px;
}
.table {
width: 100%;
border-collapse: collapse;
}
.table th,
.table td {
padding: 10px;
text-align: left;
border: 1px solid #dee2e6;
min-width: 100px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.table th:first-child,
.table td:first-child {
min-width: 50px;
max-width: 50px;
.select2-container {
width: 100% !important;
}
.table td,
.table th {
background-color: #f8f9fa;
position: relative;
cursor: col-resize;
user-select: none;
}
.table th .resize-handle {
position: absolute;
top: 0;
right: 0;
width: 5px;
height: 100%;
cursor: col-resize;
background: transparent;
}
.table th .resize-handle:hover {
background: #007bff;
}
.search-container {
margin-bottom: 20px;
}
.search-container input {
width: 300px;
padding: 8px;
border: 1px solid #ced4da;
border-radius: 5px;
}
.loader {
display: none;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 10px auto;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
vertical-align: middle;
}
</style>
<title><?= htmlspecialchars($template['name']) ?> - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/navbar.php'); ?>
<div class="dashboard-main-wrapper">
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?></h6>
<small>Template ID: <?= $id ?>, Start Row: <?= $template['header_row'] ?>, Start Column: <?= $template['start_column'] ?></small>
<div class="dashboard-body">
<div class="card shadow-sm">
<div class="card-body">
<h4 class="mb-4">Import Excel File</h4>
<!-- Upload Form -->
<form id="uploadForm" enctype="multipart/form-data" method="POST">
<div class="row g-3 align-items-end">
<div class="col-md-3">
<label for="xlsFile" class="form-label">Excel file (.xls / .xlsx)</label>
<input type="file" name="xlsFile" id="xlsFile" accept=".xls,.xlsx" class="form-control" required>
</div>
<div class="col-md-2">
<label for="headerRow" class="form-label">Header row</label>
<input type="number" name="headerRow" id="headerRow" value="4" min="1" class="form-control" required>
</div>
<div class="col-md-3">
<label for="idclient" class="form-label">Client</label>
<select name="idclient" id="idclient" class="form-select select2" required>
<option value="">Select client...</option>
<?php foreach ($clients as $cl): ?>
<option value="<?= htmlspecialchars($cl['idclient']) ?>">
<?= htmlspecialchars($cl['client_name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-upload"></i> Upload
</button>
</div>
</div>
</div>
<div class="card-body">
<!-- Form per caricare il file -->
<form id="uploadForm" enctype="multipart/form-data" class="mb-4">
<div class="mb-3">
<label for="excel_file" class="form-label">Upload XLS File</label>
<input type="file" class="form-control" id="excel_file" name="excel_file" accept=".xls,.xlsx" required>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
<div class="loader" id="loader"></div>
</form>
</form>
<!-- Contenitore per messaggi di errore -->
<div id="errorContainer" class="alert alert-danger mt-3" style="display: none;"></div>
<hr class="my-4">
<!-- Contenitore per la tabella -->
<div id="tableContainer"></div>
<!-- Filter + Table Section -->
<div id="tableSection" class="d-none">
<div class="mb-3">
<label class="form-label">Filter by Categories</label>
<select id="categorySelect" class="form-select select2" multiple></select>
</div>
<table id="dataTable" class="table table-striped table-bordered w-100">
<thead></thead>
<tbody></tbody>
</table>
<button id="saveData" class="btn btn-success mt-3">
<i class="fas fa-save"></i> Save to Database
</button>
</div>
</div>
</div>
</div>
<!--end page wrapper -->
<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>
<!--end wrapper-->
<!-- search modal -->
<?php //include('include/searchmodal.php');
?>
<!-- end search modal -->
<!--start switcher-->
<?php //include('include/themeswitcher.php');
?>
<!--end switcher-->
<?php include('jsinclude.php'); ?>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const form = document.getElementById('uploadForm');
const loader = document.getElementById('loader');
const errorContainer = document.getElementById('errorContainer');
const tableContainer = document.getElementById('tableContainer');
$(document).ready(function() {
form.addEventListener('submit', function(e) {
$('.select2').select2();
let fullData = [];
let table;
// === Upload Excel ===
$('#uploadForm').on('submit', function(e) {
e.preventDefault();
loader.style.display = 'block';
errorContainer.style.display = 'none';
tableContainer.innerHTML = '';
let formData = new FormData(this);
const formData = new FormData(this);
formData.append('template_id', <?= $id ?>);
formData.append('header_row', <?= $template['header_row'] ?>);
formData.append('start_column', <?= $template['start_column'] ?>);
fetch('process_import_xls.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
loader.style.display = 'none';
if (data.error) {
errorContainer.textContent = data.error;
errorContainer.style.display = 'block';
} else {
let html = `
<form id="selectRowsForm" action="import_edit.php" method="POST">
<input type="hidden" name="template_id" value="${data.template_id}">
<input type="hidden" name="columns" value='${JSON.stringify(data.columns)}'>
<input type="hidden" name="rows" value='${JSON.stringify(data.rows)}'>
<input type="hidden" name="filename" value="${data.filename}">
<div class="search-container">
<input type="text" id="searchInput" class="form-control" placeholder="Cerca nelle righe...">
</div>
<div class="table-container">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Seleziona</th>
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
</tr>
</thead>
<tbody>
${data.rows.map((row, index) => `
<tr>
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}"></td>
${row.map(cell => `<td>${cell}</td>`).join('')}
</tr>
`).join('')}
</tbody>
</table>
</div>
<button type="submit" class="btn btn-primary mt-3" id="proceedButton" disabled>Prosegui</button>
</form>
`;
tableContainer.innerHTML = html;
// Aggiungi logica per il ridimensionamento delle colonne
const thElements = document.querySelectorAll('.table th');
thElements.forEach((th, index) => {
if (index === 0) return; // Escludi la colonna "Seleziona" dal ridimensionamento
const resizeHandle = th.querySelector('.resize-handle');
if (resizeHandle) {
resizeHandle.addEventListener('mousedown', (e) => {
e.preventDefault();
const startX = e.clientX;
const startWidth = th.offsetWidth;
const onMouseMove = (e) => {
const newWidth = Math.max(50, startWidth + (e.clientX - startX));
th.style.width = `${newWidth}px`;
th.style.minWidth = `${newWidth}px`;
th.style.maxWidth = `${newWidth}px`;
const cells = document.querySelectorAll(`.table td:nth-child(${index + 1})`);
cells.forEach(cell => {
cell.style.width = `${newWidth}px`;
cell.style.minWidth = `${newWidth}px`;
cell.style.maxWidth = `${newWidth}px`;
});
};
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
}
});
// Aggiungi event listener per la ricerca
const searchInput = document.getElementById('searchInput');
const rows = document.querySelectorAll('.table tbody tr');
const checkboxes = document.querySelectorAll('.row-checkbox');
const proceedButton = document.getElementById('proceedButton');
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
rows.forEach(row => {
const text = Array.from(row.cells).slice(1).map(cell => cell.textContent.toLowerCase()).join(' ');
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
});
// Aggiungi event listener per i checkbox
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
proceedButton.disabled = !Array.from(checkboxes).some(cb => cb.checked);
});
});
$.ajax({
url: 'import_xls_process.php',
type: 'POST',
data: formData,
processData: false,
contentType: false,
beforeSend: () => Swal.fire({
title: 'Processing...',
allowOutsideClick: false,
didOpen: () => Swal.showLoading()
}),
success: function(res) {
Swal.close();
let json;
try {
json = JSON.parse(res);
} catch (e) {
Swal.fire('Error', 'Invalid JSON:' + res, 'error');
return;
}
})
.catch(error => {
loader.style.display = 'none';
errorContainer.textContent = 'Errore durante il caricamento del file: ' + error.message;
errorContainer.style.display = 'block';
});
if (json.status === 'ok') {
fullData = json.data;
$('#tableSection').removeClass('d-none');
// Popola Select categorie
$('#categorySelect').empty();
json.categories.forEach(cat => {
$('#categorySelect').append(`<option value="${cat}">${cat}</option>`);
});
// Mostra tabella vuota inizialmente (nessuna categoria selezionata)
renderTable([], json.columns);
Swal.fire('Success', 'File uploaded successfully! Select categories to view data.', 'success');
} else {
Swal.fire('Error', json.message, 'error');
}
},
error: function() {
Swal.fire('Error', 'Upload failed.', 'error');
}
});
});
// === Filtro dinamico categorie ===
$('#categorySelect').on('change', function() {
const selected = $(this).val();
let filtered = [];
if (selected && selected.length) {
filtered = fullData.filter(r => selected.includes(r['category']));
}
renderTable(filtered, fullData.length ? Object.keys(fullData[0]) : []);
});
// === Funzione per DataTable ===
function renderTable(data, columns) {
if ($.fn.DataTable.isDataTable('#dataTable')) {
$('#dataTable').DataTable().destroy();
}
// Aggiungiamo una colonna "Select"
const cols = [{
title: '<input type="checkbox" id="selectAllRows" />',
data: null,
orderable: false,
searchable: false,
className: 'text-center',
render: function() {
return '<input type="checkbox" class="row-select" />';
}
},
...columns.map(c => ({
title: c.replace(/_/g, ' ').toUpperCase(),
data: c
}))
];
table = $('#dataTable').DataTable({
destroy: true,
data: data,
columns: cols,
pageLength: 25,
order: [],
createdRow: function(row, rowData) {
const selectedCats = $('#categorySelect').val() || [];
const cat = (rowData['category'] || '').trim();
if (selectedCats.includes(cat)) {
$(row).find('input.row-select').prop('checked', true);
}
$(row).find('input.row-select').data('row', rowData);
}
});
// --- Gestione "Select All" ---
$('#selectAllRows').off('click').on('click', function() {
const isChecked = this.checked;
$('#dataTable input.row-select').prop('checked', isChecked);
});
}
// === Salvataggio selezione ===
$('#saveData').on('click', function() {
const selectedRows = [];
$('#dataTable input.row-select:checked').each(function() {
const rowData = $(this).data('row');
if (rowData) selectedRows.push(rowData);
});
if (selectedRows.length === 0) {
Swal.fire('Notice', 'No rows selected for import.', 'info');
return;
}
$.ajax({
url: 'import_save.php',
type: 'POST',
data: {
rows: JSON.stringify(selectedRows),
idclient: $('#idclient').val(),
source_file: $('#xlsFile')[0].files[0]?.name || ''
},
success: function(res) {
let json;
try {
json = JSON.parse(res);
} catch (e) {
Swal.fire('Error', res, 'error');
return;
}
if (json.status === 'ok') Swal.fire('Success', 'Rows imported successfully!', 'success');
else Swal.fire('Error', json.message, 'error');
},
error: function() {
Swal.fire('Error', 'Failed to import data.', 'error');
}
});
});
});
</script>
</body>
-413
View File
@@ -1,413 +0,0 @@
<?php
include('include/headscript.php');
// Controlla se è stato passato un ID valido
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
exit;
}
$id = intval($_GET['id']); // Sanifica l'ID
// Recupera il template dal database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
exit;
}
// Verifica i mapping
$stmt = $pdo->prepare("SELECT id FROM template_mapping WHERE template_id = ?");
$stmt->execute([$id]);
$hasMappings = $stmt->fetch(PDO::FETCH_ASSOC);
// Debug del template
error_log("Loaded template: " . print_r($template, true));
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.table-container {
overflow-x: auto;
max-width: 100%;
margin-bottom: 20px;
}
.table {
width: 100%;
border-collapse: collapse;
}
.table th,
.table td {
padding: 10px;
text-align: left;
border: 1px solid #dee2e6;
min-width: 100px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.table th:first-child,
.table td:first-child {
min-width: 50px;
max-width: 50px;
}
.table th {
background-color: #f8f9fa;
position: relative;
cursor: col-resize;
user-select: none;
}
.table th .resize-handle {
position: absolute;
top: 0;
right: 0;
width: 5px;
height: 100%;
cursor: col-resize;
background: transparent;
}
.table th .resize-handle:hover {
background: #007bff;
}
.search-container {
margin-bottom: 20px;
}
.search-container input {
width: 300px;
padding: 8px;
border: 1px solid #ced4da;
border-radius: 5px;
}
.loader {
display: none;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 10px auto;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<title><?= htmlspecialchars($template['name']) ?> - <?= 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">
<?php include('top_stat_widget.php'); ?>
<div class="mb-3 text">
<a href="historical_trf.php?id=<?= $id ?>&status=i" class="btn btn-warning me-2">Imported (i)</a>
<a href="historical_trf.php?id=<?= $id ?>&status=P" class="btn btn-primary me-2">In Progress (P)</a>
<a href="historical_trf.php?id=<?= $id ?>&status=l" class="btn btn-success">To LIMS (l)</a>
</div>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?></h6>
<small>Template ID: <?= $id ?>, Start Row: <?= $template['header_row'] ?>, Start Column: <?= $template['start_column'] ?></small>
</div>
</div>
</div>
<div class="card-body">
<?php if (!$hasMappings): ?>
<div class="alert alert-warning" role="alert">
Nessun mapping trovato per questo template. Configura i mapping prima di procedere.
</div>
<?php endif; ?>
<form id="uploadForm" enctype="multipart/form-data" class="mb-4">
<div class="mb-3">
<label for="excel_file" class="form-label">Upload XLS File</label>
<input type="file" class="form-control" id="excel_file" name="excel_file" accept=".xls,.xlsx" required>
</div>
<button type="submit" class="btn btn-primary" <?= !$hasMappings ? 'disabled' : '' ?>>Upload</button>
<div class="loader" id="loader"></div>
</form>
<div id="errorContainer" class="alert alert-danger mt-3" style="display: none;"></div>
<div id="tableContainer"></div>
<div class="modal fade" id="routineConfirmModal" tabindex="-1" aria-labelledby="routineConfirmModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="routineConfirmModalLabel">Conferma Applicazione Routine</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p><strong>Routine:</strong> <span id="routineName"></span></p>
<p><strong>Descrizione:</strong> <span id="routineDescription"></span></p>
<p>Vuoi applicare questa routine al file caricato?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancelRoutineBtn">Annulla</button>
<button type="button" class="btn btn-primary" id="confirmRoutineBtn">Applica</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="overlay toggle-icon"></div>
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
<?php include('jsinclude.php'); ?>
<script>
document.addEventListener("DOMContentLoaded", function() {
const form = document.getElementById('uploadForm');
const loader = document.getElementById('loader');
const errorContainer = document.getElementById('errorContainer');
const tableContainer = document.getElementById('tableContainer');
const routineModal = new bootstrap.Modal(document.getElementById('routineConfirmModal'));
const confirmRoutineBtn = document.getElementById('confirmRoutineBtn');
const cancelRoutineBtn = document.getElementById('cancelRoutineBtn');
let routineData = null;
let excelData = null;
let responseData = null;
form.addEventListener('submit', function(e) {
e.preventDefault();
loader.style.display = 'block';
errorContainer.style.display = 'none';
tableContainer.innerHTML = '';
const formData = new FormData(this);
const templateId = <?= $id ?>;
console.log('Template ID passed to formData:', templateId);
formData.append('template_id', templateId);
formData.append('header_row', <?= $template['header_row'] ?>);
formData.append('start_column', <?= $template['start_column'] ?>);
fetch('process_import_xls2.php', {
method: 'POST',
body: formData
})
.then(response => {
console.log('Stato risposta:', response.status);
return response.json();
})
.then(data => {
console.log('Risposta JSON:', data);
loader.style.display = 'none';
if (data.error) {
errorContainer.textContent = data.error;
errorContainer.style.display = 'block';
} else if (data.apply_routine) {
console.log('Routine rilevata:', data.routine_data);
routineData = data.routine_data;
excelData = data.excel_data;
responseData = data;
document.getElementById('routineName').textContent = routineData.name || 'Sconosciuta';
document.getElementById('routineDescription').textContent = routineData.instruction || 'Nessuna descrizione';
routineModal.show();
} else {
console.log('Nessuna routine, procedo con tabella');
showTable(data);
}
})
.catch(error => {
console.log('Errore fetch:', error);
loader.style.display = 'none';
errorContainer.textContent = 'Errore durante il caricamento del file: ' + error.message;
errorContainer.style.display = 'block';
});
});
confirmRoutineBtn.addEventListener('click', function() {
console.log('Conferma routine:', routineData);
routineModal.hide();
fetch('apply_routine.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: <?= $id ?>,
filename: routineData.filename,
headerrow: routineData.headerrow,
excel_data: excelData,
routine_data: routineData
})
})
.then(response => {
console.log('Stato apply_routine:', response.status);
return response.json();
})
.then(data => {
console.log('Risposta apply_routine:', data);
if (data.error) {
errorContainer.textContent = data.error;
errorContainer.style.display = 'block';
} else {
showTable(data);
}
})
.catch(error => {
console.log('Errore apply_routine:', error);
errorContainer.textContent = 'Errore durante l\'applicazione della routine: ' + error.message;
errorContainer.style.display = 'block';
});
});
cancelRoutineBtn.addEventListener('click', function() {
console.log('Routine annullata, procedo con tabella');
routineModal.hide();
showTable(responseData);
});
function showTable(data) {
console.log('Mostro tabella con dati:', data);
let html = `
<form id="selectRowsForm" action="import_insert.php" method="POST">
<input type="hidden" name="template_id" value="${data.template_id}">
<input type="hidden" name="columns" value='${JSON.stringify(data.columns)}'>
<input type="hidden" name="rows" value='${JSON.stringify(data.rows)}'>
<input type="hidden" name="excelrows" value='${JSON.stringify(data.excel_data.map(row => row.excelrow))}'>
<input type="hidden" name="filename" value="${data.filename}">
<div class="search-container">
<input type="text" id="searchInput" class="form-control" placeholder="Cerca nelle righe...">
</div>
<div class="table-container">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th><input type="checkbox" id="selectAll"> Seleziona</th>
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
</tr>
</thead>
<tbody>
${data.excel_data.map((row, index) => `
<tr>
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${row.excelrow}"></td>
${row.data.map(cell => `<td>${cell}</td>`).join('')}
</tr>
`).join('')}
</tbody>
</table>
</div>
<button type="submit" class="btn btn-primary mt-3" id="proceedButton" disabled>Prosegui</button>
</form>
`;
tableContainer.innerHTML = html;
const proceedButton = document.getElementById('proceedButton');
const selectAllCheckbox = document.getElementById('selectAll');
const checkboxes = document.querySelectorAll('.row-checkbox');
function updateProceedButton() {
proceedButton.disabled = !Array.from(checkboxes).some(cb => cb.checked);
}
selectAllCheckbox.addEventListener('change', function() {
checkboxes.forEach(checkbox => {
checkbox.checked = this.checked;
});
updateProceedButton();
});
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
console.log('Checkbox changed, checked:', this.checked, 'excelrow:', this.dataset.excelrow);
selectAllCheckbox.checked = Array.from(checkboxes).every(cb => cb.checked);
updateProceedButton();
});
});
const thElements = document.querySelectorAll('.table th');
thElements.forEach((th, index) => {
if (index === 0) return;
const resizeHandle = th.querySelector('.resize-handle');
if (resizeHandle) {
resizeHandle.addEventListener('mousedown', (e) => {
e.preventDefault();
const startX = e.clientX;
const startWidth = th.offsetWidth;
const onMouseMove = (e) => {
const newWidth = Math.max(50, startWidth + (e.clientX - startX));
th.style.width = `${newWidth}px`;
th.style.minWidth = `${newWidth}px`;
th.style.maxWidth = `${newWidth}px`;
const cells = document.querySelectorAll(`.table td:nth-child(${index + 1})`);
cells.forEach(cell => {
cell.style.width = `${newWidth}px`;
cell.style.minWidth = `${newWidth}px`;
cell.style.maxWidth = `${newWidth}px`;
});
};
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
}
});
const searchInput = document.getElementById('searchInput');
const rows = document.querySelectorAll('.table tbody tr');
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
rows.forEach(row => {
const text = Array.from(row.cells).slice(1).map(cell => cell.textContent.toLowerCase()).join(' ');
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
});
updateProceedButton();
}
});
</script>
</body>
</html>
+63
View File
@@ -0,0 +1,63 @@
<?php
include('include/headscript.php');
require __DIR__ . '/../../vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\IOFactory;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['xlsFile'])) {
$headerRow = intval($_POST['headerRow']) - 1;
$fileTmp = $_FILES['xlsFile']['tmp_name'];
if (!$fileTmp) {
echo json_encode(['status' => 'error', 'message' => 'No file uploaded.']);
exit;
}
try {
$spreadsheet = IOFactory::load($fileTmp);
$sheet = $spreadsheet->getActiveSheet();
$data = $sheet->toArray(null, true, true, true);
// Normalize headers
$headers = $data[$headerRow + 1];
$normalizedHeaders = [];
foreach ($headers as $k => $v) {
$key = strtolower(trim($v));
$key = str_replace([' ', '.', '-', '/'], '_', $key);
$normalizedHeaders[$k] = $key;
}
// Build data array
$rows = array_slice($data, $headerRow + 1);
$array = [];
foreach ($rows as $row) {
$item = [];
foreach ($normalizedHeaders as $k => $key) {
$item[$key] = trim($row[$k]);
}
$array[] = $item;
}
// Extract categories
$categories = [];
foreach ($array as $row) {
if (!empty($row['category'])) {
$cat = trim($row['category']);
if ($cat !== '' && !in_array($cat, $categories)) {
$categories[] = $cat;
}
}
}
sort($categories);
echo json_encode([
'status' => 'ok',
'columns' => array_keys($array[0] ?? []),
'categories' => $categories,
'data' => $array
]);
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
}
-396
View File
@@ -1,396 +0,0 @@
<?php include('include/headscript.php');
// Recupera tutte le routine dal database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT * FROM routine");
$stmt->execute();
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!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" />
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<?php include('cssinclude.php'); ?>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<title>Insert XLS Template <?= 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="card mb-4">
<div class="card-header">
<h5 class="mb-0">Insert new XLS Template</h5>
</div>
<div class="card-body">
<p class="mb-2">Fill the following form in order to create a new import XLS template</p>
<p class="mb-2">Mandatory Fields</p>
<ul class="mb-0">
<li>Template Name</li>
<li>Row Header and Column Header: where the title of the excel starts</li>
<li>Schema and client</li>
</ul>
</div>
</div>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Fill the form and click "Create Template"</h6>
</div>
</div>
</div>
<div class="card-body">
<div class="col-12">
<form id="insertTemplateForm" method="POST">
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="number" name="header_row" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="start_column" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
<textarea name="description" class="form-control"></textarea>
</div>
<input type="hidden" name="target_table" value="datadb">
<div class="mb-3">
<label class="form-label">Button Size</label>
<select name="button_size" class="form-control">
<option value="small">Small</option>
<option value="medium" selected>Medium</option>
<option value="large">Large</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Button Background Color</label>
<input type="color" name="button_bg_color" class="form-control" value="#007bff">
</div>
<div class="mb-3">
<label class="form-label">Button Text Color</label>
<input type="color" name="button_text_color" class="form-control" value="#ffffff">
</div>
<div class="mb-3">
<label class="form-label">Button Label</label>
<input type="text" name="button_label" class="form-control" value="Click Me">
</div>
<div class="mb-3">
<label class="form-label">Select Client *</label>
<select name="client_id" id="clientSelect" class="form-control" required>
<option value="">Select a client...</option>
</select>
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
</div>
<div class="mb-3">
<label class="form-label">Select Schema *</label>
<select name="schema_id" id="schemaSelect" class="form-control" required>
<option value="">Select a schema...</option>
</select>
<span id="schemaLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Caricamento schemi in corso...</span>
</div>
<div class="mb-3">
<label class="form-label">Select Routine</label>
<select name="idroutine" id="routineSelect" class="form-control">
<option value="">Select a routine...</option>
<?php foreach ($routines as $routine): ?>
<option value="<?php echo $routine['idroutine']; ?>">
<?php echo htmlspecialchars($routine['name']); ?>
</option>
<?php endforeach; ?>
</select>
<div id="routineDetails" class="mt-2" style="display: none;">
<h6>Routine Details</h6>
<p><strong>Name:</strong> <span id="routineName"></span></p>
<p><strong>Description:</strong> <span id="routineDescription"></span></p>
<p><strong>Action 1:</strong> <span id="routineAction1"></span></p>
<p><strong>Action 2:</strong> <span id="routineAction2"></span></p>
<p><strong>Action 3:</strong> <span id="routineAction3"></span></p>
</div>
</div>
<br>
<button type="submit" class="btn btn-primary">Create Template</button>
<a href="templates_dashboard.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="overlay toggle-icon"></div>
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
if (typeof jQuery === 'undefined') {
alert("Errore: jQuery non è caricato. Contatta l'amministratore.");
return;
}
const form = document.getElementById("insertTemplateForm");
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
const schemaLoadingStatus = document.getElementById("schemaLoadingStatus");
const routineSelect = document.getElementById("routineSelect");
const routineDetails = document.getElementById("routineDetails");
const routineName = document.getElementById("routineName");
const routineDescription = document.getElementById("routineDescription");
const routineAction1 = document.getElementById("routineAction1");
const routineAction2 = document.getElementById("routineAction2");
const routineAction3 = document.getElementById("routineAction3");
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
return;
}
$('#clientSelect').select2({
placeholder: "Search for a client...",
allowClear: true
});
$('#schemaSelect').select2({
placeholder: "Search for a schema...",
allowClear: true
});
$('#routineSelect').select2({
placeholder: "Select a routine...",
allowClear: true
});
async function loadClients() {
try {
clientLoadingStatus.style.display = 'inline';
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
const response = await fetch("get_clienti.php", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
const select = document.getElementById("clientSelect");
select.innerHTML = '<option value="">Select a client...</option>';
data.value.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
select.add(option);
});
$(select).trigger('change');
clientLoadingStatus.textContent = "Clienti caricati.";
} catch (error) {
clientLoadingStatus.textContent = "Errore nel caricamento.";
Swal.fire({
title: "Errore!",
text: "Impossibile caricare i clienti: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
}
}
async function loadSchemas() {
try {
schemaLoadingStatus.style.display = 'inline';
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
const response = await fetch("get_schemi.php", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
const select = document.getElementById("schemaSelect");
select.innerHTML = '<option value="">Select a schema...</option>';
data.value.forEach(schema => {
const nome = schema.Nome || "Nome non disponibile";
const id = schema.IdSchemaCustomFields || "ID non disponibile";
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
select.add(option);
});
$(select).trigger('change');
schemaLoadingStatus.textContent = "Schemi caricati.";
} catch (error) {
schemaLoadingStatus.textContent = "Errore nel caricamento.";
Swal.fire({
title: "Errore!",
text: "Impossibile caricare gli schemi: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => schemaLoadingStatus.style.display = 'none', 2000);
}
}
async function loadData() {
try {
await loadClients();
await loadSchemas();
} catch (error) {
Swal.fire({
title: "Errore!",
text: "Errore nel caricamento dei dati: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
}
}
loadData();
const routines = <?php echo json_encode($routines); ?>;
function updateRoutineDetails() {
const selectedId = routineSelect.value;
routineDetails.style.display = selectedId ? 'block' : 'none';
if (selectedId) {
const routine = routines.find(r => r.idroutine == selectedId);
if (routine) {
routineName.textContent = routine.name || 'N/A';
routineDescription.textContent = routine.description || 'N/A';
routineAction1.textContent = routine.action1 || 'N/A';
routineAction2.textContent = routine.action2 || 'N/A';
routineAction3.textContent = routine.action3 || 'N/A';
} else {
routineName.textContent = 'N/A';
routineDescription.textContent = 'N/A';
routineAction1.textContent = 'N/A';
routineAction2.textContent = 'N/A';
routineAction3.textContent = 'N/A';
}
} else {
routineName.textContent = '';
routineDescription.textContent = '';
routineAction1.textContent = '';
routineAction2.textContent = '';
routineAction3.textContent = '';
}
}
routineSelect.addEventListener('change', updateRoutineDetails);
updateRoutineDetails();
form.addEventListener("submit", function(e) {
e.preventDefault();
let formData = new FormData(this);
const clientSelect = document.getElementById("clientSelect");
const clientId = clientSelect.value;
const selectedClientOption = clientSelect.options[clientSelect.selectedIndex];
if (!clientId) {
Swal.fire({
title: "Errore!",
text: "Per favore seleziona un cliente.",
icon: "error",
confirmButtonText: "OK"
});
return;
}
let clientName = "";
if (selectedClientOption) {
const optionText = selectedClientOption.text.trim();
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
clientName = nameMatch ? nameMatch[1].trim() : optionText;
}
formData.append("client_name", clientName);
const schemaSelect = document.getElementById("schemaSelect");
const schemaId = schemaSelect.value;
const selectedSchemaOption = schemaSelect.options[schemaSelect.selectedIndex];
if (!schemaId) {
Swal.fire({
title: "Errore!",
text: "Per favore seleziona uno schema.",
icon: "error",
confirmButtonText: "OK"
});
return;
}
let schemaName = "";
if (selectedSchemaOption) {
const optionText = selectedSchemaOption.text.trim();
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
}
formData.append("idschema", schemaId);
formData.append("schemaname", schemaName);
const routineId = routineSelect.value;
formData.append("idroutine", routineId);
fetch("process_insert_template_xls.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
title: "Successo!",
text: "Template creato con successo!",
icon: "success",
confirmButtonText: "OK"
}).then(() => {
window.location.href = "templates_dashboard.php";
});
} else {
Swal.fire({
title: "Errore!",
text: data.message,
icon: "error",
confirmButtonText: "OK"
});
}
})
.catch(error => {
Swal.fire({
title: "Errore!",
text: "Si è verificato un errore imprevisto.",
icon: "error",
confirmButtonText: "OK"
});
});
});
});
</script>
</body>
</html>
-411
View File
@@ -1,411 +0,0 @@
<?php include('include/headscript.php'); ?>
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--favicon-->
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<title>Insert XLS Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head>
<body>
<!--wrapper-->
<div class="wrapper">
<!--sidebar wrapper -->
<?php include('include/navbar.php'); ?>
<!--end sidebar wrapper -->
<!--start header -->
<?php include('include/topbar.php'); ?>
<!--end header -->
<!--start page wrapper -->
<div class="page-wrapper">
<div class="page-content">
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-4">
<div class="col">
<div class="card radius-10 border-start border-0 border-4 border-info">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Total Orders</p>
<h4 class="my-1 text-info">4805</h4>
<p class="mb-0 font-13">+2.5% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-blues text-white ms-auto"><i class='bx bxs-cart'></i>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card radius-10 border-start border-0 border-4 border-danger">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Total Revenue</p>
<h4 class="my-1 text-danger">$84,245</h4>
<p class="mb-0 font-13">+5.4% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-burning text-white ms-auto"><i class='bx bxs-wallet'></i>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card radius-10 border-start border-0 border-4 border-success">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Bounce Rate</p>
<h4 class="my-1 text-success">34.6%</h4>
<p class="mb-0 font-13">-4.5% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-ohhappiness text-white ms-auto"><i class='bx bxs-bar-chart-alt-2'></i>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card radius-10 border-start border-0 border-4 border-warning">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Total Customers</p>
<h4 class="my-1 text-warning">8.4K</h4>
<p class="mb-0 font-13">+8.4% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-orange text-white ms-auto"><i class='bx bxs-group'></i>
</div>
</div>
</div>
</div>
</div>
</div><!--end row-->
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Insert New XLS Template</h6>
</div>
</div>
</div>
<div class="card-body">
<div class="col-12">
<form id="insertTemplateForm" method="POST">
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="number" name="header_row" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="start_column" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
<textarea name="description" class="form-control"></textarea>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="target_table" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Button Size</label>
<select name="button_size" class="form-control">
<option value="small">Small</option>
<option value="medium" selected>Medium</option>
<option value="large">Large</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Button Background Color</label>
<input type="color" name="button_bg_color" class="form-control" value="#007bff">
</div>
<div class="mb-3">
<label class="form-label">Button Text Color</label>
<input type="color" name="button_text_color" class="form-control" value="#ffffff">
</div>
<div class="mb-3">
<label class="form-label">Button Label</label>
<input type="text" name="button_label" class="form-control" value="Click Me">
</div>
<!-- Nuova sezione per i campi specifici del cliente -->
<div class="mb-3">
<label class="form-label">Client-Specific Fields</label>
<div id="clientSpecificFields">
<!-- Contenitore per i campi dinamici -->
<div class="client-field-row mb-2">
<div class="row">
<div class="col-md-3">
<input type="text" name="specific_fields[0][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
</div>
<div class="col-md-2">
<select name="specific_fields[0][type]" class="form-control">
<option value="text">Text</option>
<option value="dropdown">Dropdown</option>
<option value="date">Date</option>
<option value="boolean">Yes/No</option>
</select>
</div>
<div class="col-md-2 d-none dropdown-values" style="display: none;">
<input type="text" name="specific_fields[0][possible_values]" class="form-control" placeholder="Values (e.g., Red, Blue, Green)">
</div>
<div class="col-md-1">
<select name="specific_fields[0][required]" class="form-control">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-2">
<input type="text" name="specific_fields[0][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
</div>
<div class="col-md-1">
<input type="text" name="specific_fields[0][default_value]" class="form-control" placeholder="Default Value (optional)">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-field">-</button>
</div>
</div>
</div>
</div>
<button type="button" class="btn btn-primary mt-2" id="addField">Add Field</button>
</div>
<br>
<button type="submit" class="btn btn-primary">Create Template</button>
<a href="templeates_dashboard.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
</div>
</div>
</div>
</div>
<!--end page wrapper -->
<!--start overlay-->
<div class="overlay toggle-icon"></div>
<!--end overlay-->
<!--Start Back To Top Button-->
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<!--End Back To Top Button-->
<?php include('include/footer.php'); ?>
</div>
<!--end wrapper-->
<!-- search modal -->
<?php //include('include/searchmodal.php');
?>
<!-- end search modal -->
<!--start switcher-->
<?php //include('include/themeswitcher.php');
?>
<!--end switcher-->
<?php include('jsinclude.php'); ?>
<script>
// Debug iniziale
console.log("JavaScript is loaded and running!");
document.addEventListener("DOMContentLoaded", function() {
console.log("DOM is loaded");
const form = document.getElementById("insertTemplateForm");
const addFieldButton = document.getElementById("addField");
const container = document.getElementById("clientSpecificFields");
if (!form || !addFieldButton || !container) {
console.error("One or more DOM elements not found:", {
form,
addFieldButton,
container
});
return;
}
console.log("All DOM elements found");
// Gestione dinamica dei campi specifici
addFieldButton.addEventListener("click", function() {
console.log("Add Field button clicked");
const fieldCount = container.getElementsByClassName("client-field-row").length;
const newField = document.createElement("div");
newField.className = "client-field-row mb-2";
newField.innerHTML = `
<div class="row">
<div class="col-md-3">
<input type="text" name="specific_fields[${fieldCount}][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
</div>
<div class="col-md-2">
<select name="specific_fields[${fieldCount}][type]" class="form-control" onchange="toggleDropdownValues(this)">
<option value="text">Text</option>
<option value="dropdown">Dropdown</option>
<option value="date">Date</option>
<option value="boolean">Yes/No</option>
</select>
</div>
<div class="col-md-2 d-none dropdown-values" style="display: none;">
<input type="text" name="specific_fields[${fieldCount}][possible_values]" class="form-control" placeholder="Values (e.g., Red, Blue, Green)">
</div>
<div class="col-md-1">
<select name="specific_fields[${fieldCount}][required]" class="form-control">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-2">
<input type="text" name="specific_fields[${fieldCount}][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
</div>
<div class="col-md-1">
<input type="text" name="specific_fields[${fieldCount}][default_value]" class="form-control" placeholder="Default Value (optional)">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-field">-</button>
</div>
</div>
`;
container.appendChild(newField);
newField.querySelector(".remove-field").addEventListener("click", function() {
console.log("Remove Field button clicked");
container.removeChild(newField);
updateFieldIndices();
});
});
window.toggleDropdownValues = function(selectElement) {
console.log("Toggling dropdown values for:", selectElement.value);
const row = selectElement.closest(".row");
const dropdownValues = row.querySelector(".dropdown-values");
if (selectElement.value === "dropdown") {
dropdownValues.style.display = "block";
} else {
dropdownValues.style.display = "none";
}
};
document.querySelectorAll(".remove-field").forEach(button => {
button.addEventListener("click", function() {
console.log("Existing remove button clicked");
const container = document.getElementById("clientSpecificFields");
container.removeChild(button.closest(".client-field-row"));
updateFieldIndices();
});
});
function updateFieldIndices() {
console.log("Updating field indices");
const rows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < rows.length; i++) {
const inputs = rows[i].querySelectorAll("input, select");
inputs.forEach(input => {
const name = input.name.replace(/\[\d+\]/, `[${i}]`);
input.name = name;
});
}
}
form.addEventListener("submit", function(e) {
e.preventDefault();
console.log("Form submitted");
let formData = new FormData(this);
// Genera il JSON per client_specific_fields
let finalSpecificFields = {};
// Raccolta dei dati direttamente dal DOM
const fieldRows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < fieldRows.length; i++) {
const row = fieldRows[i];
const inputs = row.querySelectorAll("input, select");
let fieldData = {};
inputs.forEach(input => {
const nameMatch = input.name.match(/specific_fields\[\d+\]\[(.*?)\]/);
if (nameMatch) {
const fieldName = nameMatch[1];
fieldData[fieldName] = input.value.trim();
}
});
if (fieldData.name) {
finalSpecificFields[fieldData.name] = {
type: fieldData.type || "text",
possible_values: (fieldData.possible_values && fieldData.type === "dropdown") ? fieldData.possible_values.split(",").map(v => v.trim()) : [],
is_required: fieldData.required === "1",
export_column_name: fieldData.export_column_name || "",
default_value: fieldData.default_value || ""
};
console.log(`Field ${fieldData.name}:`, finalSpecificFields[fieldData.name]);
}
}
console.log("Generated JSON for client_specific_fields:", JSON.stringify(finalSpecificFields));
// Aggiungi il JSON al FormData
formData.append("client_specific_fields", JSON.stringify(finalSpecificFields));
// Debug del FormData
console.log("FormData contents:");
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
fetch("process_insert_template_xls.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
console.log("Fetch response:", data);
if (data.success) {
Swal.fire({
title: "Success!",
text: "Template created successfully!",
icon: "success",
confirmButtonText: "OK"
}).then(() => {
window.location.href = "templates_dashboard.php";
});
} else {
Swal.fire({
title: "Error!",
text: data.message,
icon: "error",
confirmButtonText: "OK"
});
}
})
.catch(error => {
console.error("Fetch error:", error);
Swal.fire({
title: "Error!",
text: "An unexpected error occurred.",
icon: "error",
confirmButtonText: "OK"
});
});
});
});
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
-27
View File
@@ -1,27 +0,0 @@
<?php
header('Content-Type: application/json');
require_once 'class/db-functions.php';
$response = ["success" => false, "data" => [], "message" => ""];
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
if (!$pdo) {
throw new Exception('Database connection failed.');
}
// Recupera solo i template attivi
$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;
$response["data"] = $templates;
} catch (PDOException $e) {
$response["message"] = "Database error: " . $e->getMessage();
} catch (Exception $e) {
$response["message"] = "Error: " . $e->getMessage();
}
echo json_encode($response);
@@ -1,60 +0,0 @@
<?php
require_once(__DIR__ . '/class/db-functions.php');
header('Content-Type: application/json');
if (!isset($_GET['template_id']) || !is_numeric($_GET['template_id'])) {
echo json_encode(["success" => false, "message" => "Invalid template ID"]);
exit;
}
$template_id = intval($_GET['template_id']);
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// 1️⃣ Recuperiamo il nome della tabella target da `excel_templates`
$stmt = $pdo->prepare("SELECT target_table FROM excel_templates WHERE id = ?");
$stmt->execute([$template_id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template || empty($template['target_table'])) {
echo json_encode(["success" => false, "message" => "Template not found or missing target table"]);
exit;
}
$target_table = $template['target_table'];
// 2️⃣ Recuperiamo le associazioni già esistenti per il template_id
$stmt = $pdo->prepare("SELECT excel_column, mysql_column, headerexcel FROM excel_column_mappings WHERE template_id = ?");
$stmt->execute([$template_id]);
$existing_mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Creiamo gli array delle colonne già mappate
$mapped_xls_columns = array_column($existing_mappings, 'excel_column');
$mapped_mysql_columns = array_column($existing_mappings, 'mysql_column'); // CORRETTO PER FILTRARE!
// 3️⃣ Recuperiamo tutte le colonne disponibili nella tabella MySQL target
$stmt = $pdo->prepare("SHOW COLUMNS FROM `$target_table`");
$stmt->execute();
$table_columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
// 🔥 FIX: Rimuoviamo le colonne MySQL che sono già state mappate!
$remaining_mysql_columns = array_values(array_diff($table_columns, $mapped_mysql_columns));
// 4️⃣ Se abbiamo salvato gli header XLSX in `headerexcel`, li usiamo per calcolare le colonne XLSX non mappate
$headerexcel = !empty($existing_mappings) ? $existing_mappings[0]['headerexcel'] : '';
$all_xls_columns = !empty($headerexcel) ? explode(',', $headerexcel) : [];
$remaining_xls_columns = array_values(array_diff($all_xls_columns, $mapped_xls_columns));
// 5️⃣ Invio dei dati al frontend
echo json_encode([
"success" => true,
"mappings" => $existing_mappings,
"remaining_xls_columns" => $remaining_xls_columns,
"remaining_mysql_columns" => $remaining_mysql_columns // 🔥 ORA LE COLONNE MYSQL SONO FILTRATE!
]);
} catch (PDOException $e) {
echo json_encode(["success" => false, "message" => "Database error: " . $e->getMessage()]);
}
-23
View File
@@ -1,23 +0,0 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$template_id = $_GET['template_id'] ?? 0;
try {
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, field_label FROM template_mapping WHERE template_id = ?");
$stmt->execute([$template_id]);
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(["success" => true, "mappings" => $mappings]);
} catch (Exception $e) {
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
}
exit;
-69
View File
@@ -1,69 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
// Parametri dalla richiesta AJAX
$template_id = intval($_GET['template_id'] ?? 0);
$status = $_GET['status'] ?? 'i';
$offset = intval($_GET['offset'] ?? 0);
$limit = intval($_GET['limit'] ?? 20);
if (!$template_id || !in_array($status, ['i', 'P', 'l'])) {
error_log("Errore in load_more_rows.php: Parametri non validi - template_id: $template_id, status: $status");
echo json_encode(['success' => false, 'message' => 'Parametri non validi']);
exit;
}
// Connessione al database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Recupera i dati
try {
$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 = ?
LIMIT ? OFFSET ?
");
$stmt->execute([$template_id, $status, $limit, $offset]);
$importedData = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Recupera i dettagli manuali
$insertedIds = array_column($importedData, 'iddatadb');
$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_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);
}
// Prepara i dati per il JSON
$rows = [];
foreach ($importedData as $row) {
$rowData = [
'iddatadb' => $row['iddatadb'] ?? '',
'importreferencecode' => $row['importreferencecode'] ?? '',
'filename_import' => $row['filename_import'] ?? '',
'status' => $row['status'] ?? '',
'importdate' => $row['importdate'] ?? '',
'details' => array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb'])
];
$rows[] = $rowData;
}
error_log("load_more_rows.php: Caricate " . count($rows) . " righe per template_id=$template_id, status=$status, offset=$offset");
echo json_encode(['success' => true, 'rows' => $rows]);
} catch (Exception $e) {
error_log("Errore in load_more_rows.php: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
}
exit;
-24
View File
@@ -1,24 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$iddatadb = $_GET['iddatadb'] ?? null;
if (!$iddatadb) {
echo json_encode(['success' => false, 'message' => 'ID TRF mancante']);
exit;
}
try {
$stmt = $pdo->prepare("SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC");
$stmt->execute([':iddatadb' => $iddatadb]);
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['success' => true, 'parts' => $parts]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
}
-23
View File
@@ -1,23 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$idquotations = $_GET['idquotations'] ?? null;
if (!$idquotations) {
echo json_encode(['success' => false, 'message' => 'ID quotations mancante']);
exit;
}
try {
$stmt = $pdo->prepare("SELECT id, idquotations, part_number, part_description FROM identification_parts WHERE idquotations = :idquotations ORDER BY part_number ASC");
$stmt->execute([':idquotations' => $idquotations]);
$parts = $stmt->fetchAll();
echo json_encode(['success' => true, 'parts' => $parts]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
}
-34
View File
@@ -1,34 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$iddatadb = $_GET['iddatadb'] ?? null;
if (!$iddatadb) {
echo json_encode(['success' => false, 'message' => 'ID TRF mancante']);
exit;
}
try {
// Adjust the query to select all photo paths for the given iddatadb
$stmt = $pdo->prepare("SELECT file_path FROM datadb_photos WHERE iddatadb = :iddatadb");
$stmt->execute([':iddatadb' => $iddatadb]);
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($photos && count($photos) > 0) {
// Prepare an array of full file paths
$photoPaths = array_map(function($photo) {
return '../photostrf/' . $photo['file_path']; // Assuming the photos are stored in the "photostrf" folder
}, $photos);
echo json_encode(['success' => true, 'photos' => $photoPaths]); // Return an array of photo paths
} else {
echo json_encode(['success' => false, 'message' => 'Nessuna foto trovata']);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
}
-33
View File
@@ -1,33 +0,0 @@
<?php
// load_photo_quotation.php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$idquotations = isset($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
if (!$idquotations) {
echo json_encode(['success' => false, 'message' => 'ID quotation mancante']);
exit;
}
try {
// Seleziona le foto per il dato idquotations dalla tabella datadb_photos
$stmt = $pdo->prepare("SELECT id, file_path FROM datadb_photos WHERE idquotations = ?");
$stmt->execute([$idquotations]);
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($photos && count($photos) > 0) {
$photoPaths = array_map(function ($photo) {
return '../photostrf/' . $photo['file_path'];
}, $photos);
echo json_encode(['success' => true, 'photos' => $photoPaths]);
} else {
echo json_encode(['success' => false, 'message' => 'Nessuna foto trovata']);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
}
-32
View File
@@ -1,32 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
// Recupera l'ID dell'utente loggato
$user_id = $iduserlogin ?? 1;
if (!$user_id) {
echo json_encode(['success' => false, 'message' => "ID dell'utente autenticato mancante"]);
exit;
}
try {
$stmt = $pdo->prepare(
"SELECT DISTINCT q.*
FROM quotations q
INNER JOIN identification_parts ip
ON ip.idquotations = q.id
AND ip.iddatadb IS NULL
WHERE q.iduser = :iduser"
);
$stmt->execute([':iduser' => $user_id]);
$quotations = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['success' => true, 'quotations' => $quotations]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento delle quotations: ' . $e->getMessage()]);
}
-35
View File
@@ -1,35 +0,0 @@
<?php
header('Content-Type: application/json');
require_once 'class/db-functions.php';
$response = ["success" => false, "columns" => []];
try {
if (!isset($_GET['table']) || empty($_GET['table'])) {
throw new Exception("Invalid table name.");
}
$table = preg_replace('/[^a-zA-Z0-9_]/', '', $_GET['table']); // Sanitize table name
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Retrieve table columns
$stmt = $pdo->query("SHOW COLUMNS FROM `$table`");
$columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
if (!$columns) {
throw new Exception("No columns found in table.");
}
$response["success"] = true;
$response["columns"] = $columns;
} catch (Exception $e) {
$response["message"] = $e->getMessage();
}
// Log per debugging
error_log(json_encode($response));
// Restituisce la risposta JSON
echo json_encode($response);
-39
View File
@@ -1,39 +0,0 @@
<?php
// Disabilita l'output automatico degli errori PHP
ini_set('display_errors', 1);
error_reporting(E_ALL);
// Assicurati che non ci sia output prima del header
ob_start();
ob_clean();
header('Content-Type: application/json');
// Connessione al database usando DBHandlerSelect
try {
// Usa un percorso relativo corretto
require_once 'class/db-functions.php';
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
if (!$pdo) {
throw new Exception('Connessione al database non valida');
}
// Query per selezionare i template dalla tabella excel_templates
$stmt = $pdo->query("SELECT * FROM excel_templates ORDER BY created_at DESC");
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['success' => true, 'data' => $templates]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Errore PDO: ' . $e->getMessage()]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Errore interno: ' . $e->getMessage()]);
} finally {
ob_end_flush();
}
error_log('Errore in load_templates.php: ' . (isset($e) ? $e->getMessage() : 'Nessun errore catturato'));
-448
View File
@@ -1,448 +0,0 @@
<?php include('include/headscript.php');
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
die("Invalid template ID");
}
$id = intval($_GET['id']);
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table, sample_xlsx FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
die("Template not found");
}
?>
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--favicon-->
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<title>Mapping XLS Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
</head>
<body>
<!--wrapper-->
<div class="wrapper">
<!--sidebar wrapper -->
<?php include('include/navbar.php'); ?>
<!--end sidebar wrapper -->
<!--start header -->
<?php include('include/topbar.php'); ?>
<!--end header -->
<!--start page wrapper -->
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Associate Columns - Template: <span id="templateName"><?php echo htmlspecialchars($template['name']); ?></h6>
<p>Header Row: <span id="headerRow"><?php echo $template['header_row']; ?></span> | Start Column: <span id="startColumn"><?php echo htmlspecialchars($template['start_column']); ?></span></p>
</div>
</div>
</div>
<div class="card-body">
<!-- Upload Section -->
<div class="mb-4">
<label class="form-label">Upload XLS Example:</label>
<input type="file" id="xlsUpload" class="form-control">
<small id="uploadStatus" class="text-muted">
<?php if (!empty($template['sample_xlsx'])): ?>
Current file: <a href="xlstemplates/<?php echo htmlspecialchars($template['sample_xlsx']); ?>" target="_blank">
<?php echo htmlspecialchars($template['sample_xlsx']); ?>
</a>
<?php else: ?>
No file uploaded yet.
<?php endif; ?>
</small>
</div>
<!-- Association Section -->
<div class="row">
<div class="col-md-5">
<h5>XLS Column</h5>
<ul id="xlsColumns" class="list-group border p-3" style="height: 300px; overflow-y: auto;"></ul>
</div>
<div class="col-md-2 text-center d-flex align-items-center justify-content-center">
<button class="btn btn-dark" id="addAssociation"> Add</button>
</div>
<div class="col-md-5">
<h5>Table Column</h5>
<ul id="tableColumns" class="list-group border p-3" style="height: 300px; overflow-y: auto;"></ul>
</div>
</div>
<!-- Associations List -->
<div class="mt-4">
<h5>Current Associations</h5>
<ul id="associationsList" class="list-group border p-3"></ul>
</div>
<!-- Save Button -->
<div class="mt-4 text-end">
<a href="templates_dashboard.php" class="btn btn-primary"> Back to Template Dashboard</a>
</div>
</div>
</div>
</div>
</div>
<!--end page wrapper -->
<!--start overlay-->
<div class="overlay toggle-icon"></div>
<!--end overlay-->
<!--Start Back To Top Button-->
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<!--End Back To Top Button-->
<?php include('include/footer.php'); ?>
</div>
<!--end wrapper-->
<!-- search modal -->
<?php //include('include/searchmodal.php');
?>
<!-- end search modal -->
<!--start switcher-->
<?php //include('include/themeswitcher.php');
?>
<!--end switcher-->
<?php include('jsinclude.php'); ?>
<script>
document.getElementById('xlsUpload').addEventListener('change', function(event) {
let file = event.target.files[0];
if (!file) {
console.error("❌ No file selected");
return;
}
console.log("📂 File selected:", file.name);
// 🔹 UPLOAD DEL FILE PRIMA DI PROCESSARE I DATI
let formData = new FormData();
formData.append("xls_file", file);
formData.append("template_id", <?php echo $id; ?>);
let statusText = document.getElementById('uploadStatus');
statusText.innerText = "Uploading...";
fetch('upload_xls_example.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error uploading file:", data.message);
statusText.innerText = "❌ Upload failed: " + data.message;
return;
}
console.log("✅ File uploaded successfully:", data.filepath);
statusText.innerHTML = `✅ File uploaded: <a href="xlstemplates/${data.filename}" target="_blank">${data.filename}</a>`;
// 🔹 UNA VOLTA CARICATO, ESTRAIAMO LE COLONNE
processXLSX(file);
})
.catch(error => {
console.error("❌ Fetch error:", error);
statusText.innerText = "❌ Upload failed. Check console.";
});
});
// 🔹 FUNZIONE PER PROCESSARE IL FILE ESTRARRE LE COLONNE
function processXLSX(file) {
let reader = new FileReader();
reader.onload = function(e) {
let data = new Uint8Array(e.target.result);
let workbook = XLSX.read(data, {
type: 'array'
});
let sheet = workbook.Sheets[workbook.SheetNames[0]];
console.log("📄 Sheet found:", workbook.SheetNames[0]);
// Recupera "Header Row" e "Start Column" dal template
let rowIndex = parseInt(document.getElementById('headerRow').textContent) || 1;
let startColumn = parseInt(document.getElementById('startColumn').textContent) || 1;
console.log("📌 Using Header Row:", rowIndex, "Start Column:", startColumn);
// Legge il foglio SENZA eliminare righe vuote
let sheetData = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: "",
raw: false,
range: 0
});
console.log("📊 Sheet Data:", sheetData);
// Verifica che la riga richiesta esista
if (!sheetData[rowIndex - 1]) {
console.warn("⚠️ No headers found in the specified row:", rowIndex);
document.getElementById('xlsColumns').innerHTML = "<li class='list-group-item text-danger'>No headers found</li>";
return;
}
// Prende la riga esatta specificata nel template
let headers = sheetData[rowIndex - 1] || [];
console.log("🔎 Raw Headers:", headers);
// Se la riga specificata è vuota, avvisa l'utente
if (!headers.length || headers.every(h => h.trim() === "")) {
console.warn("⚠️ Header row is empty at row:", rowIndex);
document.getElementById('xlsColumns').innerHTML = "<li class='list-group-item text-warning'>No headers found in the specified row</li>";
return;
}
// Rimuove le colonne prima di "Start Column"
let adjustedHeaders = headers.slice(startColumn - 1).filter(header => header !== undefined && header.trim() !== "");
console.log("✅ Extracted Headers:", adjustedHeaders);
// Popola la lista delle colonne XLSX
let xlsColumns = document.getElementById('xlsColumns');
xlsColumns.innerHTML = '';
adjustedHeaders.forEach(header => {
let li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = header;
xlsColumns.appendChild(li);
});
};
reader.readAsArrayBuffer(file);
}
document.addEventListener("DOMContentLoaded", function() {
let selectedXLSColumn = null;
let selectedTableColumn = null;
let templateId = <?php echo $id; ?>;
/** =======================
* CARICAMENTO COLONNE MYSQL DAL DATABASE
* ======================= */
/* fetch('load_table_columns.php?table=<?php echo urlencode($template['target_table']); ?>')
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("Error:", data.message);
return;
}
let tableColumns = document.getElementById('tableColumns');
tableColumns.innerHTML = '';
if (Array.isArray(data.columns)) {
data.columns.forEach(column => {
let li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = column;
tableColumns.appendChild(li);
});
} else {
console.error("Unexpected data format:", data);
}
})
.catch(error => console.error('Error loading table columns:', error));
*/
/** =======================
* CARICAMENTO MAPPATURE GIÀ ESISTENTI
* ======================= */
function loadMappings() {
fetch('load_existing_mappings.php?template_id=' + templateId)
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error loading mappings:", data.message);
return;
}
let associationsList = document.getElementById('associationsList');
let xlsColumnsList = document.getElementById('xlsColumns');
let tableColumnsList = document.getElementById('tableColumns');
// 🔥 FIX: Pulisce sempre la lista prima di aggiornare
associationsList.innerHTML = '';
xlsColumnsList.innerHTML = '';
tableColumnsList.innerHTML = '';
// Carica le associazioni esistenti
data.mappings.forEach(mapping => {
let li = document.createElement('li');
li.className = 'list-group-item d-flex justify-content-between align-items-center';
li.innerHTML = `
${mapping.excel_column} ${mapping.mysql_column}
<button class="btn btn-danger btn-sm removeAssociation" data-excel="${mapping.excel_column}" data-mysql="${mapping.mysql_column}">X</button>
`;
associationsList.appendChild(li);
});
// 🔥 FIX: Ora carica SOLO le colonne MySQL rimaste disponibili!
if (Array.isArray(data.remaining_mysql_columns)) {
data.remaining_mysql_columns.forEach(column => {
let li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = column;
tableColumnsList.appendChild(li);
});
}
// Carica le colonne XLSX rimanenti
if (Array.isArray(data.remaining_xls_columns)) {
data.remaining_xls_columns.forEach(header => {
let li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = header;
xlsColumnsList.appendChild(li);
});
}
console.log("✅ Loaded existing mappings and remaining columns");
})
.catch(error => console.error("❌ Error fetching mappings:", error));
}
// 🔥 Chiama il caricamento iniziale
loadMappings();
/** =======================
* SELEZIONE COLONNE XLS & MYSQL
* ======================= */
document.getElementById('xlsColumns').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
document.querySelectorAll('#xlsColumns li').forEach(el => el.classList.remove('active'));
event.target.classList.add('active');
selectedXLSColumn = event.target.textContent;
console.log("📌 Selected XLS Column:", selectedXLSColumn);
}
});
document.getElementById('tableColumns').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
document.querySelectorAll('#tableColumns li').forEach(el => el.classList.remove('active'));
event.target.classList.add('active');
selectedTableColumn = event.target.textContent;
console.log("📌 Selected MySQL Column:", selectedTableColumn);
}
});
/** =======================
* AGGIUNTA ASSOCIAZIONE
* ======================= */
document.getElementById('addAssociation').addEventListener('click', function() {
if (!selectedXLSColumn || !selectedTableColumn) {
alert("Please select both an XLS and MySQL column.");
return;
}
// Salva l'associazione nel database
saveAssociation(selectedXLSColumn, selectedTableColumn);
// Reset selezioni
selectedXLSColumn = null;
selectedTableColumn = null;
});
function saveAssociation(excelColumn, mysqlColumn) {
let allHeaders = [...document.querySelectorAll('#xlsColumns li')].map(el => el.textContent).join(",");
fetch('save_column_mapping.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
tablename: "<?php echo $template['target_table']; ?>",
excel_column: excelColumn,
mysql_column: mysqlColumn,
data_type: "VARCHAR",
is_required: 0,
default_value: "",
headerexcel: allHeaders
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error saving mapping:", data.message);
alert("Error saving mapping: " + data.message);
} else {
console.log("✅ Association saved:", data);
loadMappings(); // Ricarica le colonne dopo il salvataggio
}
})
.catch(error => console.error("❌ Fetch error:", error));
}
/** =======================
* RIMOZIONE ASSOCIAZIONE
* ======================= */
document.getElementById('associationsList').addEventListener('click', function(event) {
if (event.target.classList.contains('removeAssociation')) {
let excelColumn = event.target.getAttribute("data-excel");
let mysqlColumn = event.target.getAttribute("data-mysql");
removeAssociation(excelColumn, mysqlColumn);
}
});
function removeAssociation(excelColumn, mysqlColumn) {
fetch('remove_column_mapping.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
excel_column: excelColumn,
mysql_column: mysqlColumn,
tablename: "<?php echo $template['target_table']; ?>"
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error removing mapping:", data.message);
alert("Error removing mapping: " + data.message);
} else {
console.log("🗑️ Association removed:", data);
loadMappings(); // Ricarica la lista dopo la rimozione
}
})
.catch(error => console.error("❌ Fetch error:", error));
}
});
</script>
</body>
</html>
@@ -1,614 +0,0 @@
<?php include('include/headscript.php');
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
die("Invalid template ID");
}
$id = intval($_GET['id']);
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table, sample_xlsx, idclient, clientname, idschema, schemaname, schemajson FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
die("Template not found");
}
// Opzionale: se clientname o schemaname non sono disponibili, potresti volerli recuperare dinamicamente
$clientName = $template['clientname'] ?: '';
$schemaName = $template['schemaname'] ?: '';
$schemajson = $template['schemajson'] ?: ''; // Recupera il valore di schemajson
$isSchemajsonEmpty = empty(trim($template['schemajson']));
?>
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--favicon-->
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<title>Mapping XLS Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
</head>
<body>
<!--wrapper-->
<div class="wrapper">
<!--sidebar wrapper -->
<?php include('include/navbar.php'); ?>
<!--end sidebar wrapper -->
<!--start header -->
<?php include('include/topbar.php'); ?>
<!--end header -->
<!--start page wrapper -->
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Associate Columns - Template: <span id="templateName"><?php echo htmlspecialchars($template['name']); ?></span></h6>
<p>
Client: <span id="clientName"><?php echo htmlspecialchars($clientName); ?></span> |
Schema: <span id="schemaName"><?php echo htmlspecialchars($schemaName); ?></span> |
Header Row: <span id="headerRow"><?php echo $template['header_row']; ?></span> |
Start Column: <span id="startColumn"><?php echo htmlspecialchars($template['start_column']); ?></span>
</p>
</div>
</div>
</div>
<div class="card-body">
<!-- Upload Section -->
<div class="mb-4">
<label class="form-label">Upload XLS Example:</label>
<input type="file" id="xlsUpload" class="form-control">
<small id="uploadStatus" class="text-muted">
<?php if (!empty($template['sample_xlsx'])): ?>
Current file: <a href="xlstemplates/<?php echo htmlspecialchars($template['sample_xlsx']); ?>" target="_blank">
<?php echo htmlspecialchars($template['sample_xlsx']); ?>
</a>
<?php else: ?>
No file uploaded yet.
<?php endif; ?>
</small>
</div>
<!-- Association Section -->
<div class="row">
<div class="col-md-5">
<h5>XLS Column</h5>
<ul id="xlsColumns" class="list-group border p-3" style="height: 300px; overflow-y: auto;"></ul>
</div>
<div class="col-md-2 text-center d-flex align-items-center justify-content-center">
<button class="btn btn-dark" id="addAssociation"> Add</button>
</div>
<div class="col-md-5">
<div class="d-flex align-items-center mb-2">
<h5>Schema Fields</h5>
<button id="loadSchemaButton" class="btn btn-primary ms-2" data-empty="<?php echo $isSchemajsonEmpty ? 'true' : 'false'; ?>">
<?php echo $isSchemajsonEmpty ? 'Load Schema Details' : 'Update Schema Details'; ?>
</button>
</div>
<table id="schemaFieldsTable" class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>ID</th>
<th>Type</th>
</tr>
</thead>
<tbody id="schemaFieldsBody">
<!-- I dettagli dello schema verranno caricati qui -->
</tbody>
</table>
</div>
</div>
<!-- Associations List -->
<div class="mt-4">
<h5>Current Associations</h5>
<ul id="associationsList" class="list-group border p-3"></ul>
</div>
<!-- Save Button -->
<div class="mt-4 text-end">
<a href="templates_dashboard.php" class="btn btn-primary"> Back to Template Dashboard</a>
</div>
</div>
</div>
</div>
</div>
<!--end page wrapper -->
<!--start overlay-->
<div class="overlay toggle-icon"></div>
<!--end overlay-->
<!--Start Back To Top Button-->
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<!--End Back To Top Button-->
<?php include('include/footer.php'); ?>
</div>
<!--end wrapper-->
<!-- search modal -->
<?php //include('include/searchmodal.php');
?>
<!-- end search modal -->
<!--start switcher-->
<?php //include('include/themeswitcher.php');
?>
<!--end switcher-->
<?php include('jsinclude.php'); ?>
<script>
document.getElementById('xlsUpload').addEventListener('change', function(event) {
let file = event.target.files[0];
if (!file) {
console.error("❌ No file selected");
return;
}
console.log("📂 File selected:", file.name);
let formData = new FormData();
formData.append("xls_file", file);
formData.append("template_id", <?php echo $id; ?>);
let statusText = document.getElementById('uploadStatus');
statusText.innerText = "Uploading...";
fetch('upload_xls_example.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error uploading file:", data.message);
statusText.innerText = "❌ Upload failed: " + data.message;
return;
}
console.log("✅ File uploaded successfully:", data.filepath);
statusText.innerHTML = `✅ File uploaded: <a href="xlstemplates/${data.filename}" target="_blank">${data.filename}</a>`;
processXLSX(file);
})
.catch(error => {
console.error("❌ Fetch error:", error);
statusText.innerText = "❌ Upload failed. Check console.";
});
});
function processXLSX(file) {
let reader = new FileReader();
reader.onload = function(e) {
let data = new Uint8Array(e.target.result);
let workbook = XLSX.read(data, {
type: 'array'
});
let sheet = workbook.Sheets[workbook.SheetNames[0]];
console.log("📄 Sheet found:", workbook.SheetNames[0]);
let rowIndex = parseInt(document.getElementById('headerRow').textContent) || 1;
let startColumn = parseInt(document.getElementById('startColumn').textContent) || 1;
console.log("📌 Using Header Row:", rowIndex, "Start Column:", startColumn);
let sheetData = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: "",
raw: false,
range: 0
});
console.log("📊 Sheet Data:", sheetData);
if (!sheetData[rowIndex - 1]) {
console.warn("⚠️ No headers found in the specified row:", rowIndex);
document.getElementById('xlsColumns').innerHTML = "<li class='list-group-item text-danger'>No headers found</li>";
return;
}
let headers = sheetData[rowIndex - 1] || [];
console.log("🔎 Raw Headers:", headers);
if (!headers.length || headers.every(h => h.trim() === "")) {
console.warn("⚠️ Header row is empty at row:", rowIndex);
document.getElementById('xlsColumns').innerHTML = "<li class='list-group-item text-warning'>No headers found in the specified row</li>";
return;
}
let adjustedHeaders = headers.slice(startColumn - 1).filter(header => header !== undefined && header.trim() !== "");
console.log("✅ Extracted Headers:", adjustedHeaders);
let xlsColumns = document.getElementById('xlsColumns');
xlsColumns.innerHTML = '';
adjustedHeaders.forEach(header => {
let li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = header;
xlsColumns.appendChild(li);
});
};
reader.readAsArrayBuffer(file);
}
document.addEventListener("DOMContentLoaded", function() {
let selectedXLSColumn = null;
let selectedSchemaField = null;
let templateId = <?php echo $id; ?>;
let schemaId = <?php echo json_encode($template['idschema'] ?? 0); ?>;
let clientId = <?php echo json_encode($template['idclient'] ?? 0); ?>;
let isSchemajsonEmpty = <?php echo json_encode($isSchemajsonEmpty); ?>;
async function loadClientAndSchemaNames() {
if (clientId && clientId > 0) {
try {
const response = await fetch(`get_clienti.php?id=${clientId}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (response.ok && data.value && data.value.length > 0) {
document.getElementById('clientName').textContent = data.value[0].Nominativo || 'N/A';
}
} catch (error) {
console.error("❌ Error loading client name:", error);
}
}
if (schemaId && schemaId > 0) {
try {
const response = await fetch(`get_schemi.php?id=${schemaId}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (response.ok && data.value && data.value.length > 0) {
document.getElementById('schemaName').textContent = data.value[0].Nome || 'N/A';
}
} catch (error) {
console.error("❌ Error loading schema name:", error);
}
}
}
loadClientAndSchemaNames();
async function loadOrUpdateSchemaDetails() {
if (!schemaId || schemaId === 0) {
console.warn("⚠️ No schema ID available for template:", templateId);
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="3" class="text-warning">No schema associated with this template.</td></tr>';
return;
}
const loadSchemaButton = document.getElementById('loadSchemaButton');
loadSchemaButton.disabled = true;
loadSchemaButton.textContent = 'Loading...';
try {
// Chiamata a get_schema_details.php
const response = await fetch(`get_schema_details.php?id=${schemaId}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
console.log("📡 HTTP Status (get_schema_details):", response.status);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const rawResponse = await response.text();
console.log("📡 Raw Response (get_schema_details):", rawResponse);
let data;
try {
data = JSON.parse(rawResponse);
} catch (e) {
throw new Error('Invalid JSON response from get_schema_details: ' + e.message);
}
console.log("📡 Parsed Schema Details Response:", data);
if (!data || typeof data !== 'object') {
throw new Error('Invalid schema details response: Response is not an object');
}
if (!data.SchemiCustomFieldsDettagli) {
throw new Error('Invalid schema details response: Missing "SchemiCustomFieldsDettagli"');
}
const schemaJson = JSON.stringify(data);
// Salva il JSON
await saveSchemaJson(templateId, schemaJson);
// Rileggi i dati aggiornati
const updatedTemplateResponse = await fetch(`mapping_template_xls_scheme.php?id=${templateId}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
console.log("📡 HTTP Status (updated_template):", updatedTemplateResponse.status);
const rawUpdatedResponse = await updatedTemplateResponse.text();
console.log("📡 Raw Response (updated_template):", rawUpdatedResponse);
let updatedTemplateData;
try {
updatedTemplateData = JSON.parse(rawUpdatedResponse);
} catch (e) {
throw new Error('Invalid JSON response from updated_template: ' + e.message);
}
const updatedSchemaJson = updatedTemplateData.schemajson;
const schemaDetails = JSON.parse(updatedSchemaJson);
if (schemaDetails && schemaDetails.SchemiCustomFieldsDettagli) {
const schemaFieldsBody = document.getElementById('schemaFieldsBody');
schemaFieldsBody.innerHTML = '';
schemaDetails.SchemiCustomFieldsDettagli.forEach(detail => {
const field = detail.CustomField;
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${field.Titolo || 'N/A'}</td>
<td>${field.IdCustomField || 'N/A'}</td>
<td>${field.Tipo || 'N/A'}</td>
`;
tr.setAttribute('data-field', JSON.stringify({
id: field.IdCustomField,
title: field.Titolo,
type: field.Tipo
}));
tr.addEventListener('click', function() {
document.querySelectorAll('#schemaFieldsBody tr').forEach(el => el.classList.remove('table-active'));
tr.classList.add('table-active');
selectedSchemaField = JSON.parse(tr.getAttribute('data-field'));
console.log("📌 Selected Schema Field:", selectedSchemaField);
});
schemaFieldsBody.appendChild(tr);
});
} else {
throw new Error('No schema details available after update');
}
loadSchemaButton.textContent = 'Update Schema Details';
} catch (error) {
console.error("❌ Error details:", error);
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="3" class="text-danger">Error: ' + error.message + '</td></tr>';
} finally {
loadSchemaButton.disabled = false; // Assicura che il pulsante torni attivo
loadSchemaButton.textContent = isSchemajsonEmpty ? 'Load Schema Details' : 'Update Schema Details';
}
}
async function saveSchemaJson(templateId, schemaJson) {
console.log("Saving Schema JSON - templateId:", templateId, "schemaJson:", schemaJson); // Debug
try {
const response = await fetch('update_schemajson.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
schemajson: schemaJson
})
});
const data = await response.json();
console.log("Response from update_schemajson.php:", data); // Debug
if (!data.success) {
throw new Error(data.message || 'Failed to save schema JSON');
}
console.log("✅ Schema JSON saved:", data);
} catch (error) {
console.error("❌ Error saving schema JSON:", error);
throw error;
}
}
const loadSchemaButton = document.getElementById('loadSchemaButton');
if (isSchemajsonEmpty) {
loadSchemaButton.textContent = 'Load Schema Details';
} else {
const initialSchema = JSON.parse('<?php echo addslashes($schemajson); ?>');
if (initialSchema && initialSchema.SchemiCustomFieldsDettagli) {
const schemaFieldsBody = document.getElementById('schemaFieldsBody');
schemaFieldsBody.innerHTML = '';
initialSchema.SchemiCustomFieldsDettagli.forEach(detail => {
const field = detail.CustomField;
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${field.Titolo || 'N/A'}</td>
<td>${field.IdCustomField || 'N/A'}</td>
<td>${field.Tipo || 'N/A'}</td>
`;
tr.setAttribute('data-field', JSON.stringify({
id: field.IdCustomField,
title: field.Titolo,
type: field.Tipo
}));
tr.addEventListener('click', function() {
document.querySelectorAll('#schemaFieldsBody tr').forEach(el => el.classList.remove('table-active'));
tr.classList.add('table-active');
selectedSchemaField = JSON.parse(tr.getAttribute('data-field'));
console.log("📌 Selected Schema Field:", selectedSchemaField);
});
schemaFieldsBody.appendChild(tr);
});
}
}
loadSchemaButton.addEventListener('click', loadOrUpdateSchemaDetails);
function loadMappings() {
fetch('load_existing_mappings.php?template_id=' + templateId)
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error loading mappings:", data.message);
return;
}
let associationsList = document.getElementById('associationsList');
let xlsColumnsList = document.getElementById('xlsColumns');
data.mappings.forEach(mapping => {
let li = document.createElement('li');
li.className = 'list-group-item d-flex justify-content-between align-items-center';
li.innerHTML = `
${mapping.excel_column} ${mapping.schema_field_id || 'N/A'}
<button class="btn btn-danger btn-sm removeAssociation" data-excel="${mapping.excel_column}" data-mysql="${mapping.schema_field_id}">X</button>
`;
associationsList.appendChild(li);
});
if (Array.isArray(data.remaining_xls_columns)) {
data.remaining_xls_columns.forEach(header => {
let li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = header;
xlsColumnsList.appendChild(li);
});
}
// ❌ RIMOSSA: non ricaricare i dati dello schema automaticamente!
// if (!isSchemajsonEmpty) {
// loadOrUpdateSchemaDetails();
// }
})
.catch(error => console.error("❌ Error fetching mappings:", error));
}
loadMappings();
document.getElementById('xlsColumns').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
document.querySelectorAll('#xlsColumns li').forEach(el => el.classList.remove('active'));
event.target.classList.add('active');
selectedXLSColumn = event.target.textContent;
console.log("📌 Selected XLS Column:", selectedXLSColumn);
}
});
document.getElementById('schemaFieldsBody').addEventListener('click', function(event) {
if (event.target.tagName === 'TR') {
document.querySelectorAll('#schemaFieldsBody tr').forEach(el => el.classList.remove('table-active'));
event.target.classList.add('table-active');
selectedSchemaField = JSON.parse(event.target.getAttribute('data-field') || '{}');
console.log("📌 Selected Schema Field:", selectedSchemaField);
}
});
document.getElementById('addAssociation').addEventListener('click', function() {
if (!selectedXLSColumn || !selectedSchemaField) {
alert("Please select both an XLS column and a schema field.");
return;
}
saveAssociation(selectedXLSColumn, selectedSchemaField.id);
selectedXLSColumn = null;
selectedSchemaField = null;
});
function saveAssociation(excelColumn, schemaFieldId) {
let allHeaders = [...document.querySelectorAll('#xlsColumns li')].map(el => el.textContent).join(",");
fetch('save_column_mapping.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
tablename: "<?php echo $template['target_table']; ?>",
excel_column: excelColumn,
schema_field_id: schemaFieldId,
data_type: "VARCHAR",
is_required: 0,
default_value: "",
headerexcel: allHeaders
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error saving mapping:", data.message);
alert("Error saving mapping: " + data.message);
} else {
console.log("✅ Association saved:", data);
loadMappings();
}
})
.catch(error => console.error("❌ Fetch error:", error));
}
document.getElementById('associationsList').addEventListener('click', function(event) {
if (event.target.classList.contains('removeAssociation')) {
let excelColumn = event.target.getAttribute("data-excel");
let mysqlColumn = event.target.getAttribute("data-mysql");
removeAssociation(excelColumn, mysqlColumn);
}
});
function removeAssociation(excelColumn, mysqlColumn) {
fetch('remove_column_mapping.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
excel_column: excelColumn,
mysql_column: mysqlColumn,
tablename: "<?php echo $template['target_table']; ?>"
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error removing mapping:", data.message);
alert("Error removing mapping: " + data.message);
} else {
console.log("🗑️ Association removed:", data);
loadMappings();
}
})
.catch(error => console.error("❌ Fetch error:", error));
}
});
</script>
</body>
</html>
@@ -1,721 +0,0 @@
<?php include('include/headscript.php');
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
die("Invalid template ID");
}
$id = intval($_GET['id']);
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table, sample_xlsx, idclient, clientname, idschema, schemaname, schemajson, xls_headers FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
die("Template not found");
}
$clientName = $template['clientname'] ?: '';
$schemaName = $template['schemaname'] ?: '';
$schemajson = $template['schemajson'] ? json_decode($template['schemajson'], true) : [];
$isSchemajsonEmpty = empty(trim($template['schemajson']));
// Recupera i campi dalla tabella template_mapping
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label, main_field, is_visible_import FROM template_mapping WHERE template_id = ?");
$stmt->execute([$id]);
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Recupera le colonne già associate nel database
$usedColumnsFromDB = array_filter(array_column($mappings, 'excel_column'));
// Decodifica l'header XLS salvato, se presente
$xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], true) : [];
?>
<!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'); ?>
<title>Configure Template <?= htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'); ?></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<style>
.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);
}
#loading-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 9999;
justify-content: center;
align-items: center;
}
#loading-overlay div {
color: white;
font-size: 24px;
padding: 20px;
background: #333;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
#schemaFieldsTable th:first-child,
#schemaFieldsTable td:first-child,
#schemaFieldsTable th:nth-child(2),
#schemaFieldsTable td:nth-child(2) {
width: 100px;
text-align: center;
}
</style>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Configure Template: <span id="templateName"><?php echo htmlspecialchars($template['name']); ?></span></h6>
<p>
Client: <span id="clientName"><?php echo htmlspecialchars($clientName); ?></span> |
Schema: <span id="schemaName"><?php echo htmlspecialchars($schemaName); ?></span> |
Header Row: <span id="headerRow"><?php echo $template['header_row']; ?></span> |
Start Column: <span id="startColumn"><?php echo htmlspecialchars($template['start_column']); ?></span>
</p>
</div>
</div>
</div>
<div class="card-body">
<div class="mb-4">
<label class="form-label">Upload XLS Example:</label>
<input type="file" id="xlsUpload" class="form-control">
<small id="uploadStatus">
<?php if (!empty($template['sample_xlsx'])): ?>
Current file: <a href="xlstemplates/<?php echo htmlspecialchars($template['sample_xlsx']); ?>" target="_blank"><?php echo htmlspecialchars($template['sample_xlsx']); ?></a>
<?php else: ?>
No file uploaded yet.
<?php endif; ?>
</small>
</div>
<div class="row">
<div class="col-12">
<div class="d-flex align-items-center mb-2">
<h5>Schema Fields Configuration</h5>
<button id="updateSchemaButton" class="btn btn-primary ms-2" data-empty="<?php echo $isSchemajsonEmpty ? 'true' : 'false'; ?>">
<?php echo $isSchemajsonEmpty ? 'Load Schema Details' : 'Update Schema Details'; ?>
</button>
</div>
<table id="schemaFieldsTable" class="table table-striped">
<thead>
<tr>
<th>Main</th>
<th>Visible on Import</th>
<th>Title</th>
<th>Type</th>
<th>Mapping</th>
<th>Default Value</th>
</tr>
</thead>
<tbody id="schemaFieldsBody">
<?php foreach ($mappings as $mapping): ?>
<tr>
<td>
<input type="checkbox" class="main-field-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['main_field'] == 1 ? 'checked' : ''; ?>>
</td>
<td>
<input type="checkbox" class="visible-import-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo (isset($mapping['is_visible_import']) ? $mapping['is_visible_import'] : 1) == 1 ? 'checked' : ''; ?>>
</td>
<td>
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
<?php if ($mapping['is_required'] == 1): ?>
<span class="badge bg-danger ms-2">Required</span>
<?php endif; ?>
</td>
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
<td>
<?php
$isSceltaMultipla = $mapping['data_type'] === 'SceltaMultipla';
$mappingValue = $isSceltaMultipla ? 'manual' : ($mapping['excel_column'] ? 'xls' : ($mapping['is_manual'] ? 'manual' : ''));
?>
<select class="form-select mapping-select" data-id="<?php echo $mapping['id']; ?>" data-field-id="<?php echo $mapping['field_id']; ?>" <?php echo $isSceltaMultipla ? 'disabled' : ''; ?>>
<?php if (!$isSceltaMultipla): ?>
<option value="">Select Option</option>
<option value="xls" <?php echo $mappingValue === 'xls' ? 'selected' : ''; ?>>Map to XLS Column</option>
<?php endif; ?>
<option value="manual" <?php echo $mappingValue === 'manual' ? 'selected' : ''; ?>>Manual Entry</option>
</select>
<select class="form-select xls-columns" style="display:<?php echo $mappingValue === 'xls' ? 'block' : 'none'; ?>" data-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>></select>
<?php if ($mapping['excel_column']): ?>
<span class="mapped-column" style="margin-left: 5px;"><?php echo htmlspecialchars($mapping['excel_column']); ?></span>
<button class="btn btn-danger btn-sm remove-xls" data-id="<?php echo $mapping['id']; ?>" style="margin-left: 5px;">X</button>
<?php endif; ?>
</td>
<td>
<?php if ($mapping['data_type'] === 'SceltaMultipla'): ?>
<select class="form-select dropdown-select manual-default" data-id="<?php echo $mapping['id']; ?>" data-field-id="<?php echo $mapping['field_id']; ?>" data-manual-default="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>" style="display:block;">
<option value="">Seleziona...</option>
</select>
<?php else: ?>
<input type="text" class="form-control manual-default" placeholder="Default value" value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>" style="display:<?php echo $mapping['is_manual'] ? 'block' : 'none'; ?>" data-field-id="<?php echo $mapping['field_id']; ?>">
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="mt-4 text-end">
<a href="templates_dashboard.php" class="btn btn-primary"> Back to Template Dashboard</a>
</div>
</div>
</div>
</div>
</div>
<div class="overlay toggle-icon"></div>
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
let availableXlsColumns = <?php echo json_encode($xlsHeaders); ?> || [];
let usedColumnsFromDB = <?php echo json_encode($usedColumnsFromDB); ?> || [];
document.getElementById('xlsUpload').addEventListener('change', function(event) {
let file = event.target.files[0];
if (!file) return;
let formData = new FormData();
formData.append("xls_file", file);
formData.append("template_id", <?php echo $id; ?>);
let statusText = document.getElementById('uploadStatus');
statusText.innerText = "Uploading...";
fetch('upload_xls_example.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
statusText.innerText = "❌ Upload failed: " + data.message;
return;
}
statusText.innerHTML = `✅ File uploaded: <a href="xlstemplates/${data.filename}" target="_blank">${data.filename}</a>`;
processXLSX(file);
})
.catch(error => {
statusText.innerText = "❌ Upload failed. Check console.";
console.error(error);
});
});
function processXLSX(file) {
let reader = new FileReader();
reader.onload = function(e) {
let data = new Uint8Array(e.target.result);
let workbook = XLSX.read(data, {
type: 'array'
});
let sheet = workbook.Sheets[workbook.SheetNames[0]];
let rowIndex = parseInt(document.getElementById('headerRow').textContent) || 1;
let startColumn = parseInt(document.getElementById('startColumn').textContent) || 1;
let sheetData = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: "",
raw: false,
range: 0
});
console.log("Dati della riga " + rowIndex + ":", sheetData[rowIndex - 1]);
if (!sheetData[rowIndex - 1]) {
document.getElementById('schemaFieldsBody').querySelectorAll('select.xls-columns').forEach(select => {
select.innerHTML = '<option value="">Nessuna intestazione trovata</option>';
});
return;
}
let headers = sheetData[rowIndex - 1].slice(startColumn - 1).map(header => header === undefined ? "" : header);
console.log("Intestazioni estratte:", headers);
availableXlsColumns = [...headers];
usedColumnsFromDB = [];
saveXlsHeaders(headers);
updateXlsDropdowns();
};
reader.readAsArrayBuffer(file);
}
function saveXlsHeaders(headers) {
fetch('update_xls_headers.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: <?php echo $id; ?>,
xls_headers: JSON.stringify(headers)
})
}).then(response => response.json())
.then(data => {
if (!data.success) console.error("❌ Error saving XLS headers:", data.message);
})
.catch(error => console.error("❌ Fetch error:", error));
}
function updateXlsDropdowns() {
let usedColumns = Array.from(document.querySelectorAll('select.xls-columns'))
.filter(select => select.style.display === 'block' && select.value)
.map(select => select.value)
.concat(usedColumnsFromDB);
document.querySelectorAll('select.xls-columns').forEach(select => {
let currentValue = select.value || select.dataset.currentXls || '';
let options = availableXlsColumns
.filter(col => !usedColumns.includes(col) || col === currentValue)
.map(col => `<option value="${col}" ${col === currentValue ? 'selected' : ''}>${col}</option>`)
.join('');
select.innerHTML = '<option value="">Select XLS Column</option>' + options;
select.dataset.currentXls = currentValue;
if (currentValue && !options.includes(currentValue)) {
select.value = '';
}
});
}
document.addEventListener("DOMContentLoaded", function() {
let templateId = <?php echo $id; ?>;
let schemaId = <?php echo json_encode($template['idschema'] ?? 0); ?>;
let isSchemajsonEmpty = <?php echo json_encode($isSchemajsonEmpty); ?>;
// Crea l'overlay di caricamento
const loadingOverlay = document.createElement('div');
loadingOverlay.id = 'loading-overlay';
loadingOverlay.innerHTML = '<div>Loading Dropdown Options...</div>';
document.body.appendChild(loadingOverlay);
async function populateDropdowns() {
const dropdowns = document.querySelectorAll('.dropdown-select');
if (dropdowns.length === 0) {
console.warn('Nessun dropdown di tipo SceltaMultipla trovato.');
return;
}
console.log(`Trovati ${dropdowns.length} dropdown da popolare.`);
const uniqueFieldIds = [...new Set(Array.from(dropdowns).map(d => d.getAttribute('data-field-id')))].filter(fieldId => fieldId);
if (uniqueFieldIds.length === 0) {
console.warn('Nessun field_id valido trovato per i dropdown.');
dropdowns.forEach(dropdown => {
dropdown.innerHTML = '<option value="">Nessun field_id valido</option>';
dropdown.disabled = true;
});
return;
}
console.log('Field IDs univoci:', uniqueFieldIds);
// Mostra l'overlay di caricamento
const loadingOverlay = document.getElementById('loading-overlay');
loadingOverlay.style.display = 'flex';
let dropdownData = {};
try {
// Usa field_ids come previsto dal backend
const fieldIdsQuery = uniqueFieldIds.join(',');
console.log(`Recupero dati per field_ids: ${fieldIdsQuery}`);
const response = await fetch(`get_customfield_values.php?field_ids=${fieldIdsQuery}`);
if (!response.ok) {
throw new Error(`Errore HTTP: ${response.status} ${response.statusText}`);
}
const data = await response.json();
if (data.error) {
throw new Error(`Errore API: ${data.error}`);
}
dropdownData = data; // data è un oggetto come { "146": [], "156": [...] }
console.log('Dati totali restituiti:', dropdownData);
// Popola i dropdown
dropdowns.forEach(dropdown => {
const fieldId = dropdown.getAttribute('data-field-id');
const manualDefault = dropdown.getAttribute('data-manual-default') || '';
console.log(`Popolamento dropdown per field_id: ${fieldId}, manual_default: ${manualDefault}`);
dropdown.innerHTML = '<option value="">Seleziona...</option>';
if (!fieldId || !dropdownData[fieldId] || dropdownData[fieldId].length === 0) {
console.warn(`Nessun dato disponibile per field_id ${fieldId}`);
dropdown.innerHTML = `<option value="">Nessun valore (field_id ${fieldId} vuoto)</option>`;
dropdown.disabled = true; // Disabilita per evitare selezioni inutili
return;
}
dropdownData[fieldId].forEach(value => {
const option = document.createElement('option');
option.value = value.IdCustomFieldsValue;
option.textContent = value.Valore;
if (manualDefault && String(value.IdCustomFieldsValue) === String(manualDefault)) {
option.selected = true;
}
dropdown.appendChild(option);
});
dropdown.disabled = false;
});
} catch (error) {
console.error('Errore generale nel caricamento dei dropdown:', error);
dropdowns.forEach(dropdown => {
dropdown.innerHTML = '<option value="">Errore nel caricamento</option>';
dropdown.disabled = true;
});
} finally {
console.log('Nascondo overlay di caricamento.');
loadingOverlay.style.display = 'none';
}
}
// Carica i dropdown con overlay
async function loadDropdownsWithOverlay() {
console.log('Inizio caricamento tendine');
loadingOverlay.style.display = 'flex';
await new Promise(resolve => setTimeout(resolve, 500));
await populateDropdowns();
console.log('Caricamento tendine completato');
loadingOverlay.style.display = 'none';
}
loadDropdownsWithOverlay();
async function loadClientAndSchemaNames() {
if (<?php echo json_encode($template['idclient'] ?? 0); ?> > 0) {
let response = await fetch(`get_clienti.php?id=<?php echo $template['idclient']; ?>`);
let data = await response.json();
if (response.ok && data.value?.length) document.getElementById('clientName').textContent = data.value[0].Nominativo || 'N/A';
}
if (schemaId > 0) {
let response = await fetch(`get_schemi.php?id=${schemaId}`);
let data = await response.json();
if (response.ok && data.value?.length) document.getElementById('schemaName').textContent = data.value[0].Nome || 'N/A';
}
}
loadClientAndSchemaNames();
async function updateSchemaDetails() {
if (!schemaId) {
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-warning">No schema associated.</td></tr>';
return;
}
let updateSchemaButton = document.getElementById('updateSchemaButton');
updateSchemaButton.disabled = true;
updateSchemaButton.textContent = 'Loading...';
try {
let response = await fetch(`get_schema_details.php?id=${schemaId}`);
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
let data = JSON.parse(await response.text());
if (!data.SchemiCustomFieldsDettagli) throw new Error('Missing "SchemiCustomFieldsDettagli"');
await saveSchemaJson(templateId, JSON.stringify(data));
alert('Schema updated successfully. Refresh the page to see changes.');
} catch (error) {
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-danger">Error: ' + error.message + '</td></tr>';
} finally {
updateSchemaButton.disabled = false;
updateSchemaButton.textContent = 'Update Schema Details';
}
}
async function saveSchemaJson(templateId, schemaJson) {
let response = await fetch('update_schemajson.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
schemajson: schemaJson
})
});
let data = await response.json();
if (!data.success) throw new Error(data.message || 'Failed to save schema JSON');
}
document.getElementById('updateSchemaButton').addEventListener('click', updateSchemaDetails);
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
if (event.target.classList.contains('mapping-select')) {
let tr = event.target.closest('tr');
let mappingId = event.target.getAttribute('data-id');
let xlsSelect = tr.querySelector('.xls-columns');
let manualInput = tr.querySelector('.manual-default');
let mappedColumn = tr.querySelector('.mapped-column');
let removeBtn = tr.querySelector('.remove-xls');
if (event.target.value === 'xls') {
xlsSelect.style.display = 'block';
manualInput.style.display = 'none';
manualInput.value = '';
if (mappedColumn) mappedColumn.style.display = 'none';
if (removeBtn) removeBtn.style.display = xlsSelect.value ? 'inline-block' : 'none';
} else if (event.target.value === 'manual') {
xlsSelect.style.display = 'none';
manualInput.style.display = 'block';
if (mappedColumn) mappedColumn.style.display = 'none';
if (removeBtn) removeBtn.style.display = 'none';
} else {
xlsSelect.style.display = 'none';
manualInput.style.display = 'none';
manualInput.value = '';
if (mappedColumn) mappedColumn.style.display = 'none';
if (removeBtn) removeBtn.style.display = 'none';
}
saveMapping(mappingId, event.target.value, manualInput.value, xlsSelect.value);
updateXlsDropdowns();
} else if (event.target.classList.contains('main-field-checkbox')) {
const checkbox = event.target;
const mappingId = checkbox.dataset.mappingId;
const value = checkbox.checked ? 1 : 0;
// Se checked, deseleziona tutti gli altri visivamente
if (checkbox.checked) {
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
if (cb !== checkbox) cb.checked = false;
});
}
// Salva l'aggiornamento
fetch('update_main_field.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: <?php echo $id; ?>,
mapping_id: mappingId,
value: value
})
}).then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error updating main_field:", data.message);
checkbox.checked = !checkbox.checked;
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
cb.checked = cb.dataset.originalChecked === 'true';
});
} else {
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
cb.dataset.originalChecked = cb.checked;
});
}
})
.catch(error => {
console.error("❌ Fetch error:", error);
checkbox.checked = !checkbox.checked;
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
cb.checked = cb.dataset.originalChecked === 'true';
});
});
} else if (event.target.classList.contains('visible-import-checkbox')) {
const checkbox = event.target;
const mappingId = checkbox.dataset.mappingId;
const value = checkbox.checked ? 1 : 0;
fetch('update_visible_import.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: <?php echo $id; ?>,
mapping_id: mappingId,
value: value
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error updating is_visible_import:", data.message);
checkbox.checked = !checkbox.checked;
}
})
.catch(error => {
console.error("❌ Fetch error:", error);
checkbox.checked = !checkbox.checked;
});
}
});
// Salva lo stato originale dei checkbox al caricamento
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
cb.dataset.originalChecked = cb.checked;
});
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
if (event.target.classList.contains('xls-columns')) {
let tr = event.target.closest('tr');
let mappingId = event.target.getAttribute('data-id');
let manualInput = tr.querySelector('.manual-default');
let mappedColumn = tr.querySelector('.mapped-column');
let removeBtn = tr.querySelector('.remove-xls');
if (!mappedColumn) {
mappedColumn = document.createElement('span');
mappedColumn.className = 'mapped-column';
mappedColumn.style.marginLeft = '5px';
tr.querySelector('td:nth-child(5)').appendChild(mappedColumn);
}
if (!removeBtn) {
removeBtn = document.createElement('button');
removeBtn.className = 'btn btn-danger btn-sm remove-xls';
removeBtn.textContent = 'X';
removeBtn.style.marginLeft = '5px';
removeBtn.setAttribute('data-id', mappingId);
tr.querySelector('td:nth-child(5)').appendChild(removeBtn);
removeBtn.addEventListener('click', function(e) {
let tr = e.target.closest('tr');
let xlsSelect = tr.querySelector('.xls-columns');
let mappingSelect = tr.querySelector('.mapping-select');
xlsSelect.value = '';
xlsSelect.style.display = 'none';
mappingSelect.value = '';
if (mappedColumn) mappedColumn.style.display = 'none';
e.target.style.display = 'none';
saveMapping(mappingId, '', '', null);
updateXlsDropdowns();
});
}
console.log("XLS Column changed:", {
id: mappingId,
value: event.target.value
});
if (mappedColumn) {
mappedColumn.textContent = event.target.value ? `(${event.target.value})` : '';
mappedColumn.style.display = event.target.value ? 'inline' : 'none';
}
if (removeBtn) removeBtn.style.display = event.target.value ? 'inline-block' : 'none';
saveMapping(mappingId, 'xls', manualInput.value, event.target.value);
updateXlsDropdowns();
}
});
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
if (event.target.classList.contains('manual-default') && event.target.tagName === 'SELECT') {
let tr = event.target.closest('tr');
let mappingId = event.target.getAttribute('data-id');
let xlsSelect = tr.querySelector('.xls-columns');
console.log("Manual default dropdown changed:", {
id: mappingId,
value: event.target.value
});
saveMapping(mappingId, 'manual', event.target.value, xlsSelect.value);
}
});
document.getElementById('schemaFieldsBody').addEventListener('input', function(event) {
if (event.target.classList.contains('manual-default') && event.target.tagName === 'INPUT') {
let tr = event.target.closest('tr');
let mappingId = tr.querySelector('.mapping-select').getAttribute('data-id');
let xlsSelect = tr.querySelector('.xls-columns');
console.log("Manual default input changed:", {
id: mappingId,
value: event.target.value
});
saveMapping(mappingId, 'manual', event.target.value, xlsSelect.value);
}
});
document.getElementById('schemaFieldsBody').addEventListener('click', function(event) {
if (event.target.classList.contains('remove-xls')) {
let mappingId = event.target.getAttribute('data-id');
let tr = event.target.closest('tr');
let xlsSelect = tr.querySelector('.xls-columns');
let mappingSelect = tr.querySelector('.mapping-select');
let mappedColumn = tr.querySelector('.mapped-column');
xlsSelect.value = '';
xlsSelect.style.display = 'none';
mappingSelect.value = '';
if (mappedColumn) mappedColumn.style.display = 'none';
event.target.style.display = 'none';
console.log("Removing XLS mapping:", {
id: mappingId
});
saveMapping(mappingId, '', '', null);
updateXlsDropdowns();
}
});
function saveMapping(mappingId, mappingType, defaultValue, excelColumn) {
console.log("Saving mapping:", {
id: mappingId,
mapping_type: mappingType,
excel_column: excelColumn,
manual_default: defaultValue
});
fetch('save_mapping_json.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: mappingId,
mapping_type: mappingType,
excel_column: mappingType === 'xls' ? excelColumn : null,
manual_default: mappingType === 'manual' ? defaultValue : null,
tablename: "<?php echo $template['target_table']; ?>"
})
}).then(response => response.json())
.then(data => {
console.log("Save response:", data);
if (!data.success) console.error("❌ Error saving mapping:", data.message);
if (data.success && excelColumn) {
usedColumnsFromDB = usedColumnsFromDB.filter(col => col !== excelColumn);
usedColumnsFromDB.push(excelColumn);
updateXlsDropdowns();
}
})
.catch(error => console.error("❌ Fetch error:", error));
}
if (availableXlsColumns.length) updateXlsDropdowns();
});
</script>
</body>
</html>
@@ -1,464 +0,0 @@
<?php include('include/headscript.php');
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
die("Invalid template ID");
}
$id = intval($_GET['id']);
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table, sample_xlsx, idclient, clientname, idschema, schemaname, schemajson, xls_headers FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
die("Template not found");
}
$clientName = $template['clientname'] ?: '';
$schemaName = $template['schemaname'] ?: '';
$schemajson = $template['schemajson'] ? json_decode($template['schemajson'], true) : [];
$isSchemajsonEmpty = empty(trim($template['schemajson']));
// Recupera i campi dalla tabella template_mapping
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label FROM template_mapping WHERE template_id = ?");
$stmt->execute([$id]);
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Recupera le colonne già associate nel database
$usedColumnsFromDB = array_filter(array_column($mappings, 'excel_column'));
// Decodifica l'header XLS salvato, se presente
$xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], true) : [];
?>
<!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'); ?>
<title>Configure Template <?= htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'); ?></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Configure Template: <span id="templateName"><?php echo htmlspecialchars($template['name']); ?></span></h6>
<p>
Client: <span id="clientName"><?php echo htmlspecialchars($clientName); ?></span> |
Schema: <span id="schemaName"><?php echo htmlspecialchars($schemaName); ?></span> |
Header Row: <span id="headerRow"><?php echo $template['header_row']; ?></span> |
Start Column: <span id="startColumn"><?php echo htmlspecialchars($template['start_column']); ?></span>
</p>
</div>
</div>
</div>
<div class="card-body">
<div class="mb-4">
<label class="form-label">Upload XLS Example:</label>
<input type="file" id="xlsUpload" class="form-control">
<small id="uploadStatus">
<?php if (!empty($template['sample_xlsx'])): ?>
Current file: <a href="xlstemplates/<?php echo htmlspecialchars($template['sample_xlsx']); ?>" target="_blank"><?php echo htmlspecialchars($template['sample_xlsx']); ?></a>
<?php else: ?>
No file uploaded yet.
<?php endif; ?>
</small>
</div>
<div class="row">
<div class="col-12">
<div class="d-flex align-items-center mb-2">
<h5>Schema Fields Configuration</h5>
<button id="updateSchemaButton" class="btn btn-primary ms-2" data-empty="<?php echo $isSchemajsonEmpty ? 'true' : 'false'; ?>">
<?php echo $isSchemajsonEmpty ? 'Load Schema Details' : 'Update Schema Details'; ?>
</button>
</div>
<table id="schemaFieldsTable" class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>ID</th>
<th>Type</th>
<th>Mapping</th>
<th>Default Value</th>
</tr>
</thead>
<tbody id="schemaFieldsBody">
<?php foreach ($mappings as $mapping): ?>
<tr>
<td><?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?></td>
<td><?php echo htmlspecialchars($mapping['field_id'] ?? 'N/A'); ?></td>
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
<td>
<select class="form-select mapping-select" data-id="<?php echo $mapping['id']; ?>" data-field-id="<?php echo $mapping['field_id']; ?>">
<option value="">Select Option</option>
<option value="xls" <?php echo !$mapping['is_manual'] && $mapping['excel_column'] ? 'selected' : ''; ?>>Map to XLS Column</option>
<option value="manual" <?php echo $mapping['is_manual'] ? 'selected' : ' '; ?>>Manual Entry</option>
</select>
<select class="form-select xls-columns" style="display:<?php echo !$mapping['is_manual'] && $mapping['excel_column'] ? 'block' : 'none'; ?>" data-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>></select>
<?php if ($mapping['excel_column']): ?>
<span class="mapped-column" style="margin-left: 5px;"><?php echo htmlspecialchars($mapping['excel_column']); ?></span>
<button class="btn btn-danger btn-sm remove-xls" data-id="<?php echo $mapping['id']; ?>" style="margin-left: 5px;">X</button>
<?php endif; ?>
</td>
<td>
<input type="text" class="form-control manual-default" placeholder="Default value" value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>" style="display:<?php echo $mapping['is_manual'] ? 'block' : 'none'; ?>" data-field-id="<?php echo $mapping['field_id']; ?>">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="mt-4 text-end">
<a href="templates_dashboard.php" class="btn btn-primary"> Back to Template Dashboard</a>
</div>
</div>
</div>
</div>
</div>
<div class="overlay toggle-icon"></div>
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script>
let availableXlsColumns = <?php echo json_encode($xlsHeaders); ?> || [];
let usedColumnsFromDB = <?php echo json_encode($usedColumnsFromDB); ?> || [];
document.getElementById('xlsUpload').addEventListener('change', function(event) {
let file = event.target.files[0];
if (!file) return;
let formData = new FormData();
formData.append("xls_file", file);
formData.append("template_id", <?php echo $id; ?>);
let statusText = document.getElementById('uploadStatus');
statusText.innerText = "Uploading...";
fetch('upload_xls_example.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
statusText.innerText = "❌ Upload failed: " + data.message;
return;
}
statusText.innerHTML = `✅ File uploaded: <a href="xlstemplates/${data.filename}" target="_blank">${data.filename}</a>`;
processXLSX(file);
})
.catch(error => {
statusText.innerText = "❌ Upload failed. Check console.";
console.error(error);
});
});
function processXLSX(file) {
let reader = new FileReader();
reader.onload = function(e) {
let data = new Uint8Array(e.target.result);
let workbook = XLSX.read(data, {
type: 'array'
});
let sheet = workbook.Sheets[workbook.SheetNames[0]];
let rowIndex = parseInt(document.getElementById('headerRow').textContent) || 1; // Usa header_row dal template
let startColumn = parseInt(document.getElementById('startColumn').textContent) || 1; // Usa start_column come numero
let sheetData = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: "",
raw: false,
range: 0
});
console.log("Dati della riga " + rowIndex + ":", sheetData[rowIndex - 1]); // Debug: stampa la riga delle intestazioni
if (!sheetData[rowIndex - 1]) {
document.getElementById('schemaFieldsBody').querySelectorAll('select.xls-columns').forEach(select => {
select.innerHTML = '<option value="">Nessuna intestazione trovata</option>';
});
return;
}
// Estrai le intestazioni a partire dalla colonna specificata, includendo le vuote
let headers = sheetData[rowIndex - 1].slice(startColumn - 1).map(header => header === undefined ? "" : header);
console.log("Intestazioni estratte:", headers); // Debug: stampa le intestazioni estratte
availableXlsColumns = [...headers];
usedColumnsFromDB = []; // Resetta le colonne usate dopo un nuovo caricamento
saveXlsHeaders(headers);
updateXlsDropdowns();
};
reader.readAsArrayBuffer(file);
}
function saveXlsHeaders(headers) {
fetch('update_xls_headers.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: <?php echo $id; ?>,
xls_headers: JSON.stringify(headers)
})
}).then(response => response.json())
.then(data => {
if (!data.success) console.error("❌ Error saving XLS headers:", data.message);
})
.catch(error => console.error("❌ Fetch error:", error));
}
function updateXlsDropdowns() {
let usedColumns = Array.from(document.querySelectorAll('select.xls-columns'))
.filter(select => select.style.display === 'block' && select.value)
.map(select => select.value)
.concat(usedColumnsFromDB); // Aggiunge le colonne già salvate nel DB
document.querySelectorAll('select.xls-columns').forEach(select => {
let currentValue = select.value || select.dataset.currentXls || '';
let options = availableXlsColumns
.filter(col => !usedColumns.includes(col) || col === currentValue) // Esclude colonne già usate, tranne la corrente
.map(col => `<option value="${col}" ${col === currentValue ? 'selected' : ''}>${col}</option>`)
.join('');
select.innerHTML = '<option value="">Select XLS Column</option>' + options;
select.dataset.currentXls = currentValue;
if (currentValue && !options.includes(currentValue)) {
select.value = ''; // Reset se il valore non è più valido
}
});
}
document.addEventListener("DOMContentLoaded", function() {
let templateId = <?php echo $id; ?>;
let schemaId = <?php echo json_encode($template['idschema'] ?? 0); ?>;
let isSchemajsonEmpty = <?php echo json_encode($isSchemajsonEmpty); ?>;
async function loadClientAndSchemaNames() {
if (<?php echo json_encode($template['idclient'] ?? 0); ?> > 0) {
let response = await fetch(`get_clienti.php?id=<?php echo $template['idclient']; ?>`);
let data = await response.json();
if (response.ok && data.value?.length) document.getElementById('clientName').textContent = data.value[0].Nominativo || 'N/A';
}
if (schemaId > 0) {
let response = await fetch(`get_schemi.php?id=${schemaId}`);
let data = await response.json();
if (response.ok && data.value?.length) document.getElementById('schemaName').textContent = data.value[0].Nome || 'N/A';
}
}
loadClientAndSchemaNames();
async function updateSchemaDetails() {
if (!schemaId) {
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="5" class="text-warning">No schema associated.</td></tr>';
return;
}
let updateSchemaButton = document.getElementById('updateSchemaButton');
updateSchemaButton.disabled = true;
updateSchemaButton.textContent = 'Loading...';
try {
let response = await fetch(`get_schema_details.php?id=${schemaId}`);
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
let data = JSON.parse(await response.text());
if (!data.SchemiCustomFieldsDettagli) throw new Error('Missing "SchemiCustomFieldsDettagli"');
await saveSchemaJson(templateId, JSON.stringify(data));
alert('Schema updated successfully. Refresh the page to see changes.');
} catch (error) {
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="5" class="text-danger">Error: ' + error.message + '</td></tr>';
} finally {
updateSchemaButton.disabled = false;
updateSchemaButton.textContent = 'Update Schema Details';
}
}
async function saveSchemaJson(templateId, schemaJson) {
let response = await fetch('update_schemajson.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
schemajson: schemaJson
})
});
let data = await response.json();
if (!data.success) throw new Error(data.message || 'Failed to save schema JSON');
}
document.getElementById('updateSchemaButton').addEventListener('click', updateSchemaDetails);
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
if (event.target.classList.contains('mapping-select')) {
let tr = event.target.closest('tr');
let mappingId = event.target.getAttribute('data-id');
let xlsSelect = tr.querySelector('.xls-columns');
let manualInput = tr.querySelector('.manual-default');
let mappedColumn = tr.querySelector('.mapped-column');
let removeBtn = tr.querySelector('.remove-xls');
if (event.target.value === 'xls') {
xlsSelect.style.display = 'block';
manualInput.style.display = 'none';
manualInput.value = '';
if (mappedColumn) mappedColumn.style.display = 'none';
if (removeBtn) removeBtn.style.display = xlsSelect.value ? 'inline-block' : 'none';
} else if (event.target.value === 'manual') {
xlsSelect.style.display = 'none';
manualInput.style.display = 'block';
if (mappedColumn) mappedColumn.style.display = 'none';
if (removeBtn) removeBtn.style.display = 'none';
} else {
xlsSelect.style.display = 'none';
manualInput.style.display = 'none';
manualInput.value = '';
if (mappedColumn) mappedColumn.style.display = 'none';
if (removeBtn) removeBtn.style.display = 'none';
}
saveMapping(mappingId, event.target.value, manualInput.value, xlsSelect.value);
updateXlsDropdowns();
}
});
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
if (event.target.classList.contains('xls-columns')) {
let tr = event.target.closest('tr');
let mappingId = event.target.getAttribute('data-id');
let manualInput = tr.querySelector('.manual-default');
let mappedColumn = tr.querySelector('.mapped-column');
let removeBtn = tr.querySelector('.remove-xls');
// Aggiungi dinamicamente mappedColumn e removeBtn se non esistono
if (!mappedColumn) {
mappedColumn = document.createElement('span');
mappedColumn.className = 'mapped-column';
mappedColumn.style.marginLeft = '5px';
tr.querySelector('td:nth-child(4)').appendChild(mappedColumn);
}
if (!removeBtn) {
removeBtn = document.createElement('button');
removeBtn.className = 'btn btn-danger btn-sm remove-xls';
removeBtn.textContent = 'X';
removeBtn.style.marginLeft = '5px';
removeBtn.setAttribute('data-id', mappingId);
tr.querySelector('td:nth-child(4)').appendChild(removeBtn);
// Aggiungi l'event listener per il nuovo pulsante
removeBtn.addEventListener('click', function(e) {
let tr = e.target.closest('tr');
let xlsSelect = tr.querySelector('.xls-columns');
let mappingSelect = tr.querySelector('.mapping-select');
xlsSelect.value = '';
xlsSelect.style.display = 'none';
mappingSelect.value = '';
if (mappedColumn) mappedColumn.style.display = 'none';
e.target.style.display = 'none';
saveMapping(mappingId, '', '', null);
updateXlsDropdowns();
});
}
console.log("XLS Column changed:", {
id: mappingId,
value: event.target.value
});
if (mappedColumn) {
mappedColumn.textContent = event.target.value ? `(${event.target.value})` : '';
mappedColumn.style.display = event.target.value ? 'inline' : 'none';
}
if (removeBtn) removeBtn.style.display = event.target.value ? 'inline-block' : 'none';
saveMapping(mappingId, 'xls', manualInput.value, event.target.value);
updateXlsDropdowns();
}
});
document.getElementById('schemaFieldsBody').addEventListener('input', function(event) {
if (event.target.classList.contains('manual-default')) {
let tr = event.target.closest('tr');
let mappingId = tr.querySelector('.mapping-select').getAttribute('data-id');
let xlsSelect = tr.querySelector('.xls-columns');
console.log("Manual default changed:", {
id: mappingId,
value: event.target.value
});
saveMapping(mappingId, 'manual', event.target.value, xlsSelect.value);
}
});
document.getElementById('schemaFieldsBody').addEventListener('click', function(event) {
if (event.target.classList.contains('remove-xls')) {
let mappingId = event.target.getAttribute('data-id');
let tr = event.target.closest('tr');
let xlsSelect = tr.querySelector('.xls-columns');
let mappingSelect = tr.querySelector('.mapping-select');
let mappedColumn = tr.querySelector('.mapped-column');
xlsSelect.value = '';
xlsSelect.style.display = 'none';
mappingSelect.value = '';
if (mappedColumn) mappedColumn.style.display = 'none';
event.target.style.display = 'none';
console.log("Removing XLS mapping:", {
id: mappingId
});
saveMapping(mappingId, '', '', null);
updateXlsDropdowns();
}
});
function saveMapping(mappingId, mappingType, defaultValue, excelColumn) {
console.log("Saving mapping:", {
id: mappingId,
mapping_type: mappingType,
excel_column: excelColumn,
manual_default: defaultValue
});
fetch('save_mapping_json.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: mappingId,
mapping_type: mappingType,
excel_column: mappingType === 'xls' ? excelColumn : null,
manual_default: mappingType === 'manual' ? defaultValue : null,
tablename: "<?php echo $template['target_table']; ?>"
})
}).then(response => response.json())
.then(data => {
console.log("Save response:", data);
if (!data.success) console.error("❌ Error saving mapping:", data.message);
if (data.success && excelColumn) {
usedColumnsFromDB = usedColumnsFromDB.filter(col => col !== excelColumn); // Rimuovi dalla lista se salvata
usedColumnsFromDB.push(excelColumn); // Aggiungi la nuova colonna usata
updateXlsDropdowns();
}
})
.catch(error => console.error("❌ Fetch error:", error));
}
if (availableXlsColumns.length) updateXlsDropdowns();
});
</script>
</body>
</html>
File diff suppressed because one or more lines are too long
-274
View File
@@ -1,274 +0,0 @@
<!-- Modal per la gestione delle annotazioni -->
<div class="modal fade" id="annotationsModal" tabindex="-1" aria-labelledby="annotationsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" style="max-width: 90% !important; width: 90% !important;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="annotationsModalLabel">Annotazioni per TRF: <span id="trfHeaderAnnotations"></span></h5>
<!-- SLIDER PER DIMENSIONE MARKER -->
<div style="display: flex; align-items: center; gap: 10px; margin-left: 20px;">
<label for="markerSizeSlider" style="margin: 0; font-size: 0.9rem; white-space: nowrap;">Dimensione marker:</label>
<input type="range" id="markerSizeSlider" min="16" max="48" value="16" step="2" style="width: 120px;">
<span id="markerSizeValue" style="font-weight: bold; min-width: 30px;">24px</span>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<!-- COLONNA SINISTRA RIDOTTA -->
<div class="col-md-6">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<h6 style="margin: 0;">Elenco Parti</h6>
<div style="display: flex; align-items: center;">
<input type="checkbox" id="showMixPartsAnnotations" name="showMixPartsAnnotations" style="margin-right: 5px;">
<label for="showMixPartsAnnotations" style="font-size: 0.9rem;">Mix</label>
</div>
</div>
<ul id="partsListAnnotations" class="list-group" style="max-height: 500px; overflow-y: auto;"></ul>
</div>
<!-- COLONNA DESTRA PIÙ GRANDE -->
<div class="col-md-6">
<h6>Foto del Campione</h6>
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
<div id="photoSelectorContainerAnnotations" style="display: none;"></div>
</div>
<div style="position: relative; width: 100%; min-height: 500px; border: 1px solid #ddd; border-radius: 4px; overflow: hidden;">
<img id="samplePhotoAnnotations" src="" alt="Foto del campione" style="max-width: 100%; max-height: 100%; object-fit: contain; position: absolute; top: 0; left: 0;">
<canvas id="photoCanvasAnnotations" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
<canvas id="overlayCanvasAnnotations" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000;"></canvas>
<div id="descriptionListAnnotations" class="draggable-description" style="display: none;"></div>
<div id="markerContainerAnnotations"></div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-sm" id="addDescriptionsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Aggiungi Lista Descrizioni</button>
<button type="button" class="btn btn-danger btn-sm" id="removeAnnotationsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rimuovi Descrizioni</button>
<button type="button" class="btn btn-warning btn-sm" id="undoMarkerBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Undo Marker</button>
<button type="button" class="btn btn-success btn-sm" id="savePhotoBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Salva Foto con Nome</button>
<button type="button" class="btn btn-primary btn-sm" id="backToPartsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Torna alle Parti</button>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Chiudi</button>
</div>
</div>
</div>
</div>
<style>
#annotationsModal {
z-index: 1070 !important;
}
#annotationsModal .modal-backdrop {
z-index: 1065 !important;
}
#annotationsModal .modal-content {
width: 100% !important;
max-width: 100% !important;
}
#partsListAnnotations .list-group-item {
cursor: pointer;
transition: background-color 0.2s;
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 10px;
}
#partsListAnnotations .list-group-item:hover {
background-color: #f5f5f5;
}
#partsListAnnotations .list-group-item.active {
background-color: #e9ecef;
border-color: #dee2e6;
}
.draggable-description {
position: absolute;
background: rgba(255, 255, 255, 0.8);
padding: 5px;
font-family: Arial, sans-serif;
color: #000000;
cursor: move;
user-select: none;
z-index: 1000;
min-width: 100px;
min-height: 50px;
overflow: visible;
border: 1px solid #ccc;
}
.draggable-description.active-interaction {
border: 2px dashed #000;
}
.draggable-description div {
white-space: nowrap;
}
.resize-handle {
position: absolute;
bottom: 0;
right: 0;
width: 10px;
height: 10px;
background: #888;
cursor: se-resize;
}
.draggable-marker {
position: absolute;
width: 24px;
height: 24px;
border-radius: 50%;
color: #ffffff;
text-align: center;
line-height: 24px;
font-size: 12px;
cursor: move;
user-select: none;
z-index: 1000;
}
#markerContainerAnnotations {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#savePhotoBtnAnnotations {
transition: all 0.3s ease-in-out;
}
#savePhotoBtnAnnotations.unsaved {
background-color: #dc3545 !important;
border-color: #dc3545 !important;
color: white !important;
animation: pulse 1.2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
}
70% {
box-shadow: 0 0 10px 15px rgba(220, 53, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
}
}
.color-picker-container {
position: relative;
display: inline-block;
overflow: visible;
z-index: 2000;
}
.color-picker {
display: none;
position: absolute;
right: 0;
top: 25px;
background: #fff;
border: 1px solid #ccc;
padding: 5px;
z-index: 2000;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
flex-wrap: wrap;
width: 120px;
}
.color-option {
width: 24px;
height: 24px;
margin: 3px;
border: 1px solid #000;
cursor: pointer;
display: inline-block;
pointer-events: auto;
}
.color-option:hover {
border: 2px solid #000;
margin: 2px;
}
.selected-color {
width: 24px;
height: 24px;
margin-left: 5px;
border: 1px solid #000;
cursor: pointer;
display: inline-block;
pointer-events: auto;
}
.selected-color:hover {
border: 2px solid #000;
margin-left: 4px;
}
.temp-alert {
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
z-index: 3000;
}
/* Stile per lo slider */
#markerSizeSlider {
-webkit-appearance: none;
height: 6px;
border-radius: 3px;
background: #ddd;
outline: none;
}
#markerSizeSlider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #007bff;
cursor: pointer;
}
#markerSizeSlider::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: #007bff;
cursor: pointer;
border: none;
}
#partsListAnnotations .list-group-item.dragging {
opacity: 0.6;
background-color: #d1e7dd !important;
transform: rotate(3deg);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
#partsListAnnotations .placeholder {
background-color: #f8d7da !important;
border: 2px dashed #dc3545;
height: 40px;
margin: 2px 0;
border-radius: 4px;
}
</style>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
-310
View File
@@ -1,310 +0,0 @@
<!-- Modal modificato con pulsante per riconoscimento vocale, download e selezione matrici -->
<div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" style="max-width: 80% !important; width: 80% !important;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="partsModalLabel">Parti per TRF: <span id="trfHeader"></span></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; min-width: 0;">
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
<div style="display: flex; align-items: center; min-width: 0;">
<select id="global-matrice" class="ms-2" style="width: 250px !important; min-width: 250px !important;">
</select>
<input type="checkbox" id="showMixParts" name="showMixParts" style="margin-right: 5px; margin-left: 10px;">
<label for="showMixParts" style="font-size: 0.9rem; margin-right: 10px;">Mix</label>
<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-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>
</div>
</div>
<ul id="partsList" class="list-group"></ul>
<table class="table table-striped table-sm mt-3" id="partsTable">
<thead>
<tr>
<th>Num. Parte</th>
<th>Descrizione Parte</th>
<th>Azioni</th>
</tr>
</thead>
<tbody id="partsTableBody">
<tr data-part-id="new">
<td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 80px;"></td>
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione" style="width: 100%;"></td>
<td>
<button type="button" class="btn btn-success btn-sm add-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
<button type="button" class="btn btn-primary btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M</button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-6">
<h6>Foto del Campione</h6>
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
<div id="photoSelectorContainer" style="display: none;"></div>
</div>
<div style="position: relative; width: 100%; min-height: 400px;">
<img id="samplePhoto" src="" alt="Foto del campione" style="max-width: 100%; max-height: 100%; object-fit: contain; position: absolute; top: 0; left: 0;">
<canvas id="photoCanvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000;"></canvas>
<div id="descriptionList" class="draggable-description" style="display: none;"></div>
<div id="markerContainer"></div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-sm" id="addDescriptionsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Aggiungi Lista Descrizioni</button>
<button type="button" class="btn btn-danger btn-sm" id="removeAnnotationsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rimuovi Descrizioni</button>
<button type="button" class="btn btn-warning btn-sm" id="undoMarkerBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Undo Marker</button>
<button type="button" class="btn btn-success btn-sm" id="savePhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Salva Foto con Nome</button>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Chiudi</button>
</div>
</div>
</div>
</div>
<style>
#partsTable tr {
display: table-row !important;
}
#partsTable tr:hover {
background-color: #f5f5f5;
display: table-row !important;
}
#partsTable td,
#partsTable th {
padding: 0.2rem;
vertical-align: middle;
}
#partsTable input {
height: 24px;
padding: 0.1rem 0.3rem;
}
#partsTable button {
padding: 0.1rem 0.3rem;
margin: 0 2px;
}
#partsTable i {
font-size: 0.6rem !important;
}
#partsModal {
z-index: 1060 !important;
}
#partsModal .modal-backdrop {
z-index: 1055 !important;
}
#partsModal .modal-content {
width: 100% !important;
max-width: 100% !important;
}
#partsList .list-group-item {
cursor: pointer;
transition: background-color 0.2s;
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 10px;
}
#partsList .list-group-item:hover {
background-color: #f5f5f5;
}
#partsList input[type="color"] {
width: 30px;
height: 24px;
padding: 0;
margin-left: 5px;
cursor: pointer;
}
.draggable-description {
position: absolute;
background: rgba(255, 255, 255, 0.8);
padding: 5px;
font-family: Arial, sans-serif;
color: #000000;
cursor: move;
user-select: none;
z-index: 1000;
min-width: 100px;
min-height: 50px;
overflow: visible;
border: 1px solid #ccc;
}
.draggable-description.active-interaction {
border: 2px dashed #000;
}
.draggable-description div {
white-space: nowrap;
}
.resize-handle {
position: absolute;
bottom: 0;
right: 0;
width: 10px;
height: 10px;
background: #888;
cursor: se-resize;
}
.draggable-marker {
position: absolute;
width: 24px;
height: 24px;
border-radius: 50%;
color: #ffffff;
text-align: center;
line-height: 24px;
font-size: 12px;
cursor: move;
user-select: none;
z-index: 1000;
}
#markerContainer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.add-to-mix-btn {
padding: 0.1rem 0.3rem;
font-size: 0.8rem;
}
#savePhotoBtn {
transition: all 0.3s ease-in-out;
}
#savePhotoBtn.unsaved {
background-color: #dc3545 !important;
border-color: #dc3545 !important;
color: white !important;
animation: pulse 1.2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
}
70% {
box-shadow: 0 0 10px 15px rgba(220, 53, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
}
}
.color-picker-container {
position: relative;
display: inline-block;
}
.color-picker {
display: none;
position: absolute;
top: 25px;
left: 0;
background: #fff;
border: 1px solid #ccc;
padding: 5px;
z-index: 1002;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
flex-wrap: wrap;
width: 120px;
}
.color-option {
width: 24px;
height: 24px;
margin: 3px;
border: 1px solid #000;
cursor: pointer;
display: inline-block;
}
.color-option:hover {
border: 2px solid #000;
margin: 2px;
}
/* Stili per Select2 in #partsList e #global-matrice */
#partsList .select2-container,
.select2-container--default #global-matrice {
width: 250px !important;
min-width: 250px !important;
margin-left: 10px;
}
#partsList .select2-selection--single,
.select2-container--default #global-matrice .select2-selection--single {
height: 26px !important;
padding: 0.2rem 0.5rem !important;
font-size: 0.9rem !important;
border: 1px solid #ced4da !important;
background-color: #fff !important;
}
#partsList .select2-selection__rendered,
.select2-container--default #global-matrice .select2-selection__rendered {
line-height: 24px !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
#partsList .select2-selection__arrow,
.select2-container--default #global-matrice .select2-selection__arrow {
height: 26px !important;
}
#partsList .save-status,
#partsList .save-loading {
margin-left: 5px;
}
.select2-container--open .select2-dropdown {
z-index: 1051 !important;
border: 1px solid #aaa !important;
border-radius: 4px !important;
background: white !important;
overflow-y: auto !important;
max-height: 200px !important;
}
.select2-container--default .select2-search--dropdown .select2-search__field {
width: 100% !important;
padding: 0.2rem !important;
}
.select2-container--default .part-matrice,
.select2-container--default #global-matrice {
width: 250px !important;
min-width: 250px !important;
}
</style>
-496
View File
@@ -1,496 +0,0 @@
<div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" style="max-width: 80% !important;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="partsModalLabel">Parti per TRF: <span id="trfHeader"></span></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row parts-row">
<div class="col-md-9">
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<h6 style="margin: 0;">Elenco Parti</h6>
<div style="display: flex; align-items: center;">
<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-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>
<button type="button" class="btn btn-info btn-sm ms-2 d-none" id="quotationeBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Add Quotation</button>
<button type="button" class="btn btn-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i>
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
</button>
</div>
</div>
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<button type="button" class="btn btn-success btn-sm add-row-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 5px;"><i class="fas fa-plus fa-xs"></i></button>
<button type="button" class="btn btn-primary btn-sm add-mix-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 10px;">M</button>
<select id="macro-matrice-filter" class="form-control form-control-sm ms-2" style="width: 200px !important; min-width: 200px !important; margin-right: 10px;">
<option value="">Tutte le MacroMatrici</option>
</select>
<select id="global-matrice" class="form-control form-control-sm" style="width: 350px !important; margin-right: 10px;"></select>
<button type="button" class="btn btn-primary btn-sm propagate-all-btn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-arrow-right fa-xs"></i> Propaga a tutte</button>
</div>
<table class="table table-striped table-sm" id="partsTable">
<thead>
<tr>
<th style="width: 80px;">Numero</th>
<th>Descrizione</th>
<th style="width: 200px;">Matrice</th>
<th style="width: 150px;">
<input type="date" class="form-control form-control-sm propagate-date-input" style="width: 130px; margin-left: 5px; display: inline-block;" title="Propaga data a tutte le parti">
</th>
<th style="width: 200px;">
<button type="button" class="btn btn-light btn-sm propagate-note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem; margin-left: 5px;" title="Propaga nota a tutte le parti">
<i class="fas fa-sticky-note"></i>
</button>
Azioni
</th>
</tr>
</thead>
<tbody id="partsTableBody">
<tr data-part-id="new">
<td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 80px;"></td>
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione"></td>
<td>
<div style="display: flex; align-items: center;">
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right fa-xs"></i></button>
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
</div>
</td>
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
<td>
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem;" title="Aggiungi/Modifica nota"><i class="fas fa-sticky-note"></i></button>
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-3">
<h6>Foto del Campione</h6>
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
<div id="photoSelectorContainer" style="display: none;"></div>
</div>
<div style="position: relative; width: 100%; min-height: 400px;">
<img id="samplePhoto" src="" alt="Foto del campione" style="max-width: 100%; max-height: 400px; object-fit: contain;">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-sm" id="openAnnotationsBtn">Apri Annotazioni</button>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Chiudi</button>
</div>
</div>
</div>
</div>
<!-- Modale per la nota -->
<div class="modal fade" id="noteModal" tabindex="-1" aria-labelledby="noteModalLabel" aria-hidden="true">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="noteModalLabel">Nota per Parte</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<textarea class="form-control part-note" rows="5" placeholder="Inserisci una nota"></textarea>
</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 save-note-btn">Salva</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="commonNoteModal" tabindex="-1" aria-labelledby="commonNoteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="commonNoteModalLabel">Nota comune per tutte le parti</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<textarea class="form-control part-note" rows="4" placeholder="Inserisci nota comune"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Chiudi</button>
<button type="button" class="btn btn-primary save-common-note-btn">Salva</button>
</div>
</div>
</div>
</div>
<!-- Modale di conferma per l'eliminazione -->
<div class="modal fade" id="confirmDeleteModal" tabindex="-1" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="confirmDeleteModalLabel">Conferma Eliminazione</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Sei sicuro di voler eliminare questa parte?
</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="confirmDeleteBtn">Elimina</button>
</div>
</div>
</div>
</div>
<!-- Finestra modale "Aggiungi preventivo" -->
<div class="modal fade" id="addQuotationModal" tabindex="-1" aria-labelledby="addQuotationModalLabel" aria-hidden="true">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addQuotationModalLabel">Choose a quotation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<select id="addQuotationSelect" class="form-control form-control-sm" style="width: 100% !important; min-width: 100% !important"></select>
</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="addQuotationBtn">Confirm</button>
</div>
</div>
</div>
</div>
<style>
/* --- Base --- */
#partsModal {
z-index: 1060 !important
}
#partsModal .modal-backdrop {
z-index: 1055 !important
}
#partsModal .modal-content {
width: 100% !important;
max-width: 100% !important
}
#addQuotationModal {
z-index: 1060 !important
}
#addQuotationModal .modal-backdrop {
z-index: 1055 !important
}
/* Tabelle */
#partsTable tr {
display: table-row !important
}
#partsTable tr:hover {
background: #f5f5f5
}
#partsTable td,
#partsTable th {
padding: .2rem;
vertical-align: middle
}
#partsTable input,
#partsTable select {
height: 24px;
padding: .1rem .3rem
}
#partsTable button {
padding: .1rem .3rem;
margin: 0 2px
}
#partsTable i {
font-size: .6rem !important
}
/* --- Larghezze fisse header --- */
/* MacroMatrici = 250px */
#macro-matrice-filter {
width: 250px !important;
min-width: 250px !important;
max-width: 250px !important;
flex: 0 0 250px !important;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#macro-matrice-filter.select2-hidden-accessible+.select2 {
width: 250px !important;
min-width: 250px !important;
max-width: 250px !important;
flex: 0 0 250px !important;
}
#macro-matrice-filter.select2-hidden-accessible+.select2 .select2-selection__rendered {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Matrice globale = 450px */
#global-matrice {
width: 450px !important;
min-width: 450px !important;
max-width: 450px !important;
flex: 0 0 450px !important;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#global-matrice.select2-hidden-accessible+.select2 {
width: 450px !important;
min-width: 450px !important;
max-width: 450px !important;
flex: 0 0 450px !important;
}
#global-matrice.select2-hidden-accessible+.select2 .select2-selection__rendered {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Select delle righe (colonna Matrice) = 150px */
.part-matrice {
width: 300px !important;
min-width: 300px !important;
max-width: 300px !important;
flex: 0 0 300px !important;
}
.part-matrice.select2-hidden-accessible+.select2 {
width: 300px !important;
min-width: 300px !important;
max-width: 300px !important;
flex: 0 0 300px !important;
}
/* Colonna Descrizione (2ª colonna) = 420px */
#partsTable th:nth-child(2),
#partsTable td:nth-child(2) {
width: 350 !important;
min-width: 350px !important;
max-width: 350px !important;
}
#partsTable td:nth-child(2) .part-description {
width: 100% !important;
max-width: 100% !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Aspetto Select2 */
.select2-container--default .select2-selection--single {
height: 24px !important;
padding: .1rem .3rem !important;
font-size: .8rem !important;
border: 1px solid #ced4da !important;
}
.select2-container--default .select2-selection__arrow {
height: 24px !important
}
.select2-container--open .select2-dropdown {
z-index: 1061 !important;
border: 1px solid #aaa !important;
border-radius: 4px !important;
background: #fff !important;
max-height: 200px !important;
overflow-y: auto !important;
}
/* Evita stretching del flex nella riga dei filtri */
#partsModal .modal-body>.row .col-md-9>div[style*="display: flex"]>* {
flex: 0 0 auto;
}
/* Altri modali e pulsanti */
.propagate-matrice-btn,
.propagate-all-btn {
padding: .1rem .3rem !important;
font-size: .8rem !important
}
.save-status,
.save-loading {
margin-left: 5px
}
#confirmDeleteModal {
z-index: 1070 !important
}
#confirmDeleteModal .modal-backdrop {
z-index: 1065 !important
}
.note-btn {
padding: .2rem .4rem !important;
font-size: .9rem !important
}
.note-btn.has-note {
color: #dc3545 !important
}
#noteModal {
z-index: 1090 !important
}
#noteModal .modal-backdrop {
z-index: 1085 !important
}
#noteModal .modal-dialog {
position: relative;
z-index: 1090 !important
}
#noteModal textarea,
#commonNoteModal textarea {
resize: vertical
}
#commonNoteModal {
z-index: 1095 !important
}
#commonNoteModal .modal-backdrop {
z-index: 1090 !important
}
#commonNoteModal .modal-dialog {
position: relative;
z-index: 1095 !important
}
/* Evidenza salvataggio riga nel parts table */
/* Aumenta la specificità per le classi di flash */
table#partsTable tr.row-saving {
background-color: #f0ad4e !important;
/* Arancione per salvataggio in corso */
transition: background-color 0.3s ease;
}
table#partsTable tr.row-success {
background-color: #5cb85c !important;
/* Verde per successo */
transition: background-color 0.3s ease;
}
table#partsTable tr.row-error {
background-color: #d9534f !important;
/* Rosso per errore */
transition: background-color 0.3s ease;
}
/* Stato base: nascosti (verrà sovrascritto dallo style inline di jQuery) */
#partsModal .save-loading,
#partsModal .save-status {
display: none;
align-items: center;
gap: 6px;
padding: 2px 8px;
border-radius: 999px;
font-weight: 700;
font-size: 12px;
line-height: 1.2;
margin-left: 5px;
}
/* Quando NON sono nascosti via style inline (jQuery .show()), forzali a inline-flex */
#partsModal .save-loading:not([style*="display: none"]),
#partsModal .save-status:not([style*="display: none"]) {
display: inline-flex;
}
/* Loading (giallo) */
/* Loading (giallo) */
#partsModal .save-loading {
background: #ffd753ff;
border: 1px solid #ffd042ff;
color: #111;
/* testo nero */
}
#partsModal .save-loading i {
color: #111;
}
/* icona nera */
#partsModal .save-loading::after {
content: " Salvataggio…";
color: #111;
}
/* Salvato (verde) */
#partsModal .save-status {
background: #5dff83ff;
border: 1px solid #4effafff;
color: #111;
/* testo nero */
}
#partsModal .save-status i {
color: #111;
}
/* icona nera */
#partsModal .save-status::after {
content: " Salvato";
color: #111;
}
/* Animazioni */
@keyframes pulse {
0%,
100% {
transform: scale(1);
opacity: .9
}
50% {
transform: scale(1.05);
opacity: 1
}
}
@keyframes pop {
0% {
transform: scale(.85);
opacity: 0
}
100% {
transform: scale(1);
opacity: 1
}
}
/* rosso */
</style>
-572
View File
@@ -1,572 +0,0 @@
$(document).ready(function () {
console.log("parts.js caricato correttamente");
$(".parts-btn").on("click", function () {
console.log("Pulsante Parts cliccato");
const iddatadb = $(this).data("iddatadb");
const rowIndex = $(this).data("row");
const importRef = $("table tbody tr")
.eq(rowIndex)
.find("td")
.eq(1)
.text();
const description =
$("table tbody tr").eq(rowIndex).find("td").eq(2).text() ||
"Sconosciuto";
$("#trfHeader").text(`${iddatadb} - ${importRef} - ${description}`);
$("#partsModal").data("iddatadb", iddatadb);
loadPhoto(iddatadb);
loadExistingParts(iddatadb);
$("#partsModal").modal("show");
});
function loadPhoto(iddatadb) {
$.ajax({
url: "load_photo.php",
method: "GET",
data: { iddatadb: iddatadb },
success: function (response) {
if (response.success && response.file_path) {
const img = $("#samplePhoto");
img.attr("src", response.file_path);
img.on("load", function () {
const container = img.parent();
const canvas = document.getElementById("photoCanvas");
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img[0].naturalWidth;
const scaleY = containerHeight / img[0].naturalHeight;
const scale = Math.min(scaleX, scaleY);
canvas.width = img[0].naturalWidth * scale;
canvas.height = img[0].naturalHeight * scale;
canvas.style.width = `${containerWidth}px`;
canvas.style.height = `${containerHeight}px`;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(
img.get(0),
0,
0,
canvas.width,
canvas.height,
);
updateMarkers();
});
} else {
$("#samplePhoto").attr("src", "");
alert("Nessuna foto trovata per questo TRF.");
}
},
error: function (xhr, status, error) {
alert("Errore nel caricamento della foto: " + error);
},
});
}
function addNewRow(nextPartNumber) {
const newRow = `
<tr data-part-id="new">
<td><input type="number" class="form-control form-control-sm part-number" value="${nextPartNumber || 1}" style="width: 80px;"></td>
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione" style="width: 100%;"></td>
<td>
<button type="button" class="btn btn-success btn-sm add-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
</td>
</tr>
`;
$("#partsTableBody").append(newRow);
updateRowButtons();
}
function updateRowButtons() {
const rowCount = $("#partsTableBody tr").length;
$("#partsTableBody tr").each(function (index) {
const $removeBtn = $(this).find(".remove-row");
if (rowCount > 1) {
$removeBtn.show();
} else {
$removeBtn.hide();
}
});
}
$(document).on("click", ".add-row", function (e) {
e.preventDefault();
console.log("Pulsante Aggiungi riga cliccato");
const maxPartNumber = Math.max(
...$("#partsTableBody tr")
.map(function () {
return parseInt($(this).find(".part-number").val()) || 0;
})
.get(),
);
addNewRow(maxPartNumber + 1);
updatePartsList();
});
$(document).on("click", ".remove-row", function (e) {
e.preventDefault();
console.log("Pulsante Rimuovi riga cliccato");
const $row = $(this).closest("tr");
const partId = $row.data("part-id");
console.log("ID parte da eliminare:", partId);
if (partId !== "new" && partId !== undefined && partId !== null) {
console.log("Procedo con la cancellazione dal database");
$.ajax({
url: "delete_part.php",
method: "POST",
data: JSON.stringify({ part_id: partId }),
contentType: "application/json",
beforeSend: function () {
console.log(
"Invio richiesta AJAX a delete_part.php con part_id:",
partId,
);
},
success: function (response) {
console.log("Risposta da delete_part.php:", response);
if (response.success) {
$row.remove();
updateRowButtons();
updatePartsList();
clearCanvasMarkers();
} else {
alert("Errore nell'eliminazione: " + response.message);
}
},
error: function (xhr, status, error) {
console.log("Errore AJAX:", status, error);
alert(
"Errore nell'eliminazione: " +
error +
". Stato: " +
xhr.status +
" - " +
xhr.responseText,
);
},
});
} else {
console.log(
'Riga non salvata nel database (partId = "new" o non definito), rimuovo solo visivamente',
);
$row.remove();
updateRowButtons();
updatePartsList();
}
});
$(document).on("blur", ".part-description, .part-number", function () {
const $input = $(this);
const $row = $input.closest("tr");
const partNumber = $row.find(".part-number").val();
const partDescription = $row.find(".part-description").val().trim();
const $saveStatus = $row.find(".save-status");
const $saveLoading = $row.find(".save-loading");
const iddatadb = $("#partsModal").data("iddatadb");
console.log("Evento blur su input:", { partNumber, partDescription });
if (partDescription && iddatadb) {
$saveLoading.show();
$saveStatus.hide();
$.ajax({
url: "save_parts.php",
method: "POST",
data: JSON.stringify({
iddatadb: iddatadb,
parts: [
{
part_number: partNumber,
part_description: partDescription,
},
],
}),
contentType: "application/json",
success: function (response) {
if (response.success) {
if (response.part_id) {
$row.data("part-id", response.part_id);
console.log(
"Aggiornato partId della riga:",
response.part_id,
);
}
$saveLoading.hide();
$saveStatus.show();
updatePartsList();
setTimeout(() => $saveStatus.hide(), 2000);
} else {
alert("Errore nel salvataggio: " + response.message);
$saveLoading.hide();
}
},
error: function (xhr, status, error) {
alert("Errore nel salvataggio delle parti: " + error);
$saveLoading.hide();
},
});
}
});
function loadExistingParts(iddatadb) {
$.ajax({
url: "load_parts.php",
method: "GET",
data: { iddatadb: iddatadb },
success: function (response) {
if (response.success) {
$("#partsTableBody").empty();
if (response.parts.length > 0) {
response.parts.forEach((part) => {
const newRow = `
<tr data-part-id="${part.id}">
<td><input type="number" class="form-control form-control-sm part-number" value="${part.part_number}" style="width: 80px;"></td>
<td><input type="text" class="form-control form-control-sm part-description" value="${part.part_description}" style="width: 100%;"></td>
<td>
<button type="button" class="btn btn-success btn-sm add-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-trash fa-xs"></i></button>
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
</td>
</tr>
`;
$("#partsTableBody").append(newRow);
});
} else {
addNewRow(1);
}
updateRowButtons();
updatePartsList();
} else {
alert(
"Errore nel caricamento delle parti: " +
response.message,
);
addNewRow(1);
}
},
error: function (xhr, status, error) {
alert("Errore nel caricamento delle parti: " + error);
addNewRow(1);
},
});
}
function updatePartsList() {
$("#partsList").empty();
$("#partsTableBody tr").each(function () {
const partNumber = $(this).find(".part-number").val();
const partDescription = $(this).find(".part-description").val();
if (partNumber && partDescription) {
const listItem = `<li class="list-group-item" data-part-number="${partNumber}">${partNumber} - ${partDescription}</li>`;
$("#partsList").append(listItem);
}
});
}
let selectedPartNumber = null;
let markers = [];
let descriptionPosition = { x: 10, y: 10 };
let hasDescriptions = false;
$("#partsList").on("click", "li", function () {
selectedPartNumber = $(this).data("part-number");
console.log("Part number selezionato:", selectedPartNumber);
$(this).addClass("active").siblings().removeClass("active");
});
const canvas = document.getElementById("photoCanvas");
const ctx = canvas.getContext("2d");
$("#markerContainer").on("click", function (e) {
console.log("Click sul markerContainer rilevato"); // Debug
if (selectedPartNumber !== null) {
const img = $("#samplePhoto");
const canvas = document.getElementById("photoCanvas");
const rect = canvas.getBoundingClientRect();
const container = img.parent();
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img.get(0).naturalWidth;
const scaleY = containerHeight / img.get(0).naturalHeight;
const scale = Math.min(scaleX, scaleY);
const x = (e.clientX - rect.left) / scale;
const y = (e.clientY - rect.top) / scale;
console.log("Coordinate cliccate (x, y):", x, y); // Debug
const existingMarker = markers.find(
(m) => m.partNumber == selectedPartNumber,
);
if (existingMarker) {
existingMarker.x = x;
existingMarker.y = y;
} else {
markers.push({ partNumber: selectedPartNumber, x, y });
}
console.log("Markers aggiornati:", markers); // Debug
updateMarkers();
if (hasDescriptions) {
drawDescriptions(descriptionPosition.x, descriptionPosition.y);
}
selectedPartNumber = null;
$("#partsList li").removeClass("active");
} else {
console.log("Nessun part number selezionato"); // Debug
}
});
function updateMarkers() {
const img = $("#samplePhoto");
const container = img.parent();
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img.get(0).naturalWidth;
const scaleY = containerHeight / img.get(0).naturalHeight;
const scale = Math.min(scaleX, scaleY);
const markerContainer = $("#markerContainer");
markerContainer.empty();
markers.forEach((marker) => {
const scaledX = marker.x * scale;
const scaledY = marker.y * scale;
console.log(
"Aggiungo marker:",
marker.partNumber,
"a posizione (scaledX, scaledY):",
scaledX,
scaledY,
); // Debug
const $marker = $(
`<div class="draggable-marker">${marker.partNumber}</div>`,
).css({
left: scaledX - 8 + "px",
top: scaledY - 8 + "px",
});
markerContainer.append($marker);
makeDraggable($marker, marker, scale);
});
}
function makeDraggable($element, item, scale) {
let isDragging = false;
let currentX = parseFloat($element.css("left")) || 0;
let currentY = parseFloat($element.css("top")) || 0;
let initialX, initialY;
$element.on("mousedown", function (e) {
e.preventDefault();
isDragging = true;
initialX = e.clientX - currentX;
initialY = e.clientY - currentY;
$element.css("z-index", 1001);
});
$(document).on("mousemove", function (e) {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
const container = $("#photoCanvas").parent();
const containerWidth = container.width();
const containerHeight = container.height();
const maxX = containerWidth - $element.width();
const maxY = containerHeight - $element.height();
currentX = Math.max(0, Math.min(currentX, maxX));
currentY = Math.max(0, Math.min(currentY, maxY));
$element.css({
left: currentX + "px",
top: currentY + "px",
});
if (item.partNumber) {
// È un marker
item.x = (currentX + 8) / scale;
item.y = (currentY + 8) / scale;
} else {
// È la lista
descriptionPosition.x = (currentX + 5) / scale;
descriptionPosition.y = (currentY + 5) / scale;
}
}
});
$(document).on("mouseup", function () {
isDragging = false;
$element.css("z-index", 1000);
});
}
function drawDescriptions(x, y) {
const img = $("#samplePhoto");
const container = img.parent();
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img.get(0).naturalWidth;
const scaleY = containerHeight / img.get(0).naturalHeight;
const scale = Math.min(scaleX, scaleY);
const partsList = [];
$("#partsTableBody tr").each(function () {
const partNumber = $(this).find(".part-number").val();
const partDescription = $(this).find(".part-description").val();
if (partNumber && partDescription) {
partsList.push(`${partNumber} ${partDescription}`);
}
});
const descriptionList = $("#descriptionList");
descriptionList.empty();
descriptionList.css({
display: "block",
top: y * scale + "px",
left: x * scale + "px",
width: "200px",
});
partsList.forEach((part) => {
descriptionList.append(`<div>${part}</div>`);
});
updateMarkers();
}
function clearCanvasMarkers() {
markers = [];
hasDescriptions = false;
$("#descriptionList").css("display", "none");
$("#markerContainer").empty();
const canvas = document.getElementById("photoCanvas");
const img = $("#samplePhoto");
const ctx = canvas.getContext("2d");
const container = img.parent();
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img.get(0).naturalWidth;
const scaleY = containerHeight / img.get(0).naturalHeight;
const scale = Math.min(scaleX, scaleY);
canvas.width = img.get(0).naturalWidth * scale;
canvas.height = img.get(0).naturalHeight * scale;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img.get(0), 0, 0, canvas.width, canvas.height);
}
$("#addDescriptionsBtn").on("click", function () {
hasDescriptions = true;
descriptionPosition = { x: 10, y: 10 };
drawDescriptions(descriptionPosition.x, descriptionPosition.y);
makeDraggable(
$("#descriptionList"),
descriptionPosition,
Math.min(
$("#photoCanvas").parent().width() /
$("#samplePhoto").get(0).naturalWidth,
$("#photoCanvas").parent().height() /
$("#samplePhoto").get(0).naturalHeight,
),
);
});
$("#removeAnnotationsBtn").on("click", function () {
clearCanvasMarkers();
});
$("#savePhotoBtn").on("click", function () {
const canvas = document.getElementById("photoCanvas");
const img = $("#samplePhoto");
const ctx = canvas.getContext("2d");
canvas.width = img.get(0).naturalWidth;
canvas.height = img.get(0).naturalHeight;
ctx.drawImage(img.get(0), 0, 0);
const partsList = [];
$("#partsTableBody tr").each(function () {
const partNumber = $(this).find(".part-number").val();
const partDescription = $(this).find(".part-description").val();
if (partNumber && partDescription) {
partsList.push(`${partNumber} ${partDescription}`);
}
});
if (hasDescriptions) {
ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
ctx.fillRect(
descriptionPosition.x,
descriptionPosition.y,
200,
partsList.length * 12 + 10,
);
ctx.fillStyle = "#000000";
ctx.font = "10px Arial";
partsList.forEach((part, index) => {
ctx.fillText(
part,
descriptionPosition.x + 5,
descriptionPosition.y + 12 + index * 12,
);
});
}
markers.forEach((marker) => {
ctx.beginPath();
ctx.arc(marker.x, marker.y, 8, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
ctx.fill();
ctx.strokeStyle = "#ff0000";
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillStyle = "#ffffff";
ctx.font = "bold 8px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(marker.partNumber, marker.x, marker.y);
});
const dataURL = canvas.toDataURL("image/png");
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
const defaultName = `photo_${$("#partsModal").data("iddatadb")}_${timestamp}.png`;
const newName = prompt(
"Inserisci il nome del file (senza estensione):",
defaultName.split(".png")[0],
);
if (newName) {
const finalName = newName + "_" + timestamp + ".png";
$.ajax({
url: "save_annotated_photo.php",
method: "POST",
data: { dataURL: dataURL, filename: finalName },
success: function (response) {
if (response.success) {
alert(
"Foto salvata con successo: " + response.file_path,
);
} else {
alert("Errore nel salvataggio: " + response.message);
}
},
error: function (xhr, status, error) {
alert("Errore nel salvataggio della foto: " + error);
},
});
}
});
$(document).on("mouseenter", "tr", function () {
console.log("Mouse entrato su riga");
});
$(document).on("mouseleave", "tr", function () {
console.log("Mouse uscito da riga");
});
});
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-5
View File
@@ -1,5 +0,0 @@
<?php
// Include il modal delle parti (già esistente)
include 'modal_parts.php';
?>
<script src="parts.js"></script>
-668
View File
@@ -1,668 +0,0 @@
$(document).ready(function () {
console.log("parts.js caricato correttamente");
const partsButtons = document.querySelectorAll(".parts-btn");
const partsModal = document.getElementById("partsModal");
const overlay = document.querySelector(".overlay");
partsButtons.forEach((button) => {
button.addEventListener("click", function () {
console.log(
"Pulsante Parts cliccato, iddatadb:",
$(this).data("iddatadb"),
);
const iddatadb = $(this).data("iddatadb");
const rowIndex = $(this).data("row");
console.log("Row index:", rowIndex);
const importRef = $("table tbody tr")
.eq(rowIndex)
.find("td")
.eq(1)
.text();
console.log("ImportRef:", importRef);
const description =
$("table tbody tr").eq(rowIndex).find("td").eq(2).text() ||
"Sconosciuto";
console.log("Description:", description);
$("#trfHeader").text(`${iddatadb} - ${importRef} - ${description}`);
$("#partsModal").data("iddatadb", iddatadb);
if (partsModal) {
console.log(
"Tento di aprire il modal, bootstrap:",
typeof bootstrap !== "undefined"
? "definito"
: "non definito",
);
try {
console.log("Inizio try per creare modal instance");
const modalInstance = new bootstrap.Modal(partsModal, {
focus: true,
}); // Forza la gestione del focus
console.log("Modal instance creata");
console.log("Inizio show del modal");
modalInstance.show();
console.log("Modal mostrato");
if (overlay) overlay.style.display = "none"; // Nascondi overlay solo se esiste
} catch (error) {
console.error("Errore nell'apertura del modal:", error);
}
} else {
console.error("Modal Parts non trovato");
}
console.log("Prima di chiamare loadPhoto e loadExistingParts");
loadPhoto(iddatadb)
.then(() => {
console.log("loadPhoto completata");
loadExistingParts(iddatadb)
.then(() => {
console.log("loadExistingParts completata");
})
.catch((error) => {
console.error(
"Errore in loadExistingParts:",
error,
);
});
})
.catch((error) => {
console.error("Errore in loadPhoto:", error);
});
console.log("Dopo l'avvio delle chiamate AJAX");
});
});
// Gestione della chiusura del modal Parts
if (closeBtn) {
closeBtn.addEventListener("click", function () {
partsModal.style.display = "none";
overlay.style.display = "none"; // Nascondi overlay
document.body.style.pointerEvents = "auto"; // Riattiva la pagina
});
}
if (partsModal) {
window.addEventListener("click", function (event) {
if (event.target === partsModal) {
partsModal.style.display = "none";
overlay.style.display = "none"; // Nascondi overlay
document.body.style.pointerEvents = "auto"; // Riattiva la pagina
}
});
}
function loadPhoto(iddatadb) {
console.log("Inizio loadPhoto per iddatadb:", iddatadb);
return $.ajax({
url: "load_photo.php",
method: "GET",
data: { iddatadb: iddatadb },
})
.then(function (response) {
console.log("Risposta loadPhoto:", response);
if (response.success && response.file_path) {
const img = $("#samplePhoto");
img.attr("src", response.file_path);
return new Promise((resolve) => {
img.on("load", function () {
const container = img.parent();
const canvas =
document.getElementById("photoCanvas");
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img[0].naturalWidth;
const scaleY =
containerHeight / img[0].naturalHeight;
const scale = Math.min(scaleX, scaleY);
canvas.width = img[0].naturalWidth * scale;
canvas.height = img[0].naturalHeight * scale;
canvas.style.width = `${containerWidth}px`;
canvas.style.height = `${containerHeight}px`;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(
img.get(0),
0,
0,
canvas.width,
canvas.height,
);
updateMarkers();
resolve();
});
});
} else {
$("#samplePhoto").attr("src", "");
console.log(
"Nessuna foto trovata:",
response.message || "Nessun messaggio",
);
return Promise.resolve(); // Risolvi comunque la promessa
}
})
.catch(function (xhr, status, error) {
console.error("Errore in loadPhoto:", {
status,
error,
response: xhr.responseText,
});
$("#samplePhoto").attr("src", "");
alert("Errore nel caricamento della foto: " + error);
return Promise.reject(error); // Rifiuta la promessa in caso di errore
});
}
function addNewRow(nextPartNumber) {
const newRow = `
<tr data-part-id="new">
<td><input type="number" class="form-control form-control-sm part-number" value="${nextPartNumber || 1}" style="width: 80px;"></td>
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione" style="width: 100%;"></td>
<td>
<button type="button" class="btn btn-success btn-sm add-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
</td>
</tr>
`;
$("#partsTableBody").append(newRow);
updateRowButtons();
}
function updateRowButtons() {
const rowCount = $("#partsTableBody tr").length;
$("#partsTableBody tr").each(function (index) {
const $removeBtn = $(this).find(".remove-row");
if (rowCount > 1) {
$removeBtn.show();
} else {
$removeBtn.hide();
}
});
}
$(document).on("click", ".add-row", function (e) {
e.preventDefault();
console.log("Pulsante Aggiungi riga cliccato");
const maxPartNumber = Math.max(
...$("#partsTableBody tr")
.map(function () {
return parseInt($(this).find(".part-number").val()) || 0;
})
.get(),
);
addNewRow(maxPartNumber + 1);
updatePartsList();
});
$(document).on("click", ".remove-row", function (e) {
e.preventDefault();
console.log("Pulsante Rimuovi riga cliccato");
const $row = $(this).closest("tr");
const partId = $row.data("part-id");
console.log("ID parte da eliminare:", partId);
if (partId !== "new" && partId !== undefined && partId !== null) {
console.log("Procedo con la cancellazione dal database");
$.ajax({
url: "delete_part.php",
method: "POST",
data: JSON.stringify({ part_id: partId }),
contentType: "application/json",
beforeSend: function () {
console.log(
"Invio richiesta AJAX a delete_part.php con part_id:",
partId,
);
},
success: function (response) {
console.log("Risposta da delete_part.php:", response);
if (response.success) {
$row.remove();
updateRowButtons();
updatePartsList();
clearCanvasMarkers();
} else {
alert("Errore nell'eliminazione: " + response.message);
}
},
error: function (xhr, status, error) {
console.log("Errore AJAX:", status, error);
alert(
"Errore nell'eliminazione: " +
error +
". Stato: " +
xhr.status +
" - " +
xhr.responseText,
);
},
});
} else {
console.log(
'Riga non salvata nel database (partId = "new" o non definito), rimuovo solo visivamente',
);
$row.remove();
updateRowButtons();
updatePartsList();
}
});
$(document).on("blur", ".part-description, .part-number", function () {
const $input = $(this);
const $row = $input.closest("tr");
const partNumber = $row.find(".part-number").val();
const partDescription = $row.find(".part-description").val().trim();
const $saveStatus = $row.find(".save-status");
const $saveLoading = $row.find(".save-loading");
const iddatadb = $("#partsModal").data("iddatadb");
console.log("Evento blur su input:", { partNumber, partDescription });
if (partDescription && iddatadb) {
$saveLoading.show();
$saveStatus.hide();
$.ajax({
url: "save_parts.php",
method: "POST",
data: JSON.stringify({
iddatadb: iddatadb,
parts: [
{
part_number: partNumber,
part_description: partDescription,
},
],
}),
contentType: "application/json",
success: function (response) {
if (response.success) {
if (response.part_id) {
$row.data("part-id", response.part_id);
console.log(
"Aggiornato partId della riga:",
response.part_id,
);
}
$saveLoading.hide();
$saveStatus.show();
updatePartsList();
setTimeout(() => $saveStatus.hide(), 2000);
} else {
alert("Errore nel salvataggio: " + response.message);
$saveLoading.hide();
}
},
error: function (xhr, status, error) {
alert("Errore nel salvataggio delle parti: " + error);
$saveLoading.hide();
},
});
}
});
function loadExistingParts(iddatadb) {
console.log("Inizio loadExistingParts per iddatadb:", iddatadb);
return $.ajax({
url: "load_parts.php",
method: "GET",
data: { iddatadb: iddatadb },
})
.then(function (response) {
console.log("Risposta loadExistingParts:", response);
if (response.success) {
$("#partsTableBody").empty();
if (response.parts.length > 0) {
response.parts.forEach((part) => {
const newRow = `
<tr data-part-id="${part.id}">
<td><input type="number" class="form-control form-control-sm part-number" value="${part.part_number}" style="width: 80px;"></td>
<td><input type="text" class="form-control form-control-sm part-description" value="${part.part_description}" style="width: 100%;"></td>
<td>
<button type="button" class="btn btn-success btn-sm add-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-trash fa-xs"></i></button>
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
</td>
</tr>
`;
$("#partsTableBody").append(newRow);
});
} else {
addNewRow(1);
}
updateRowButtons();
updatePartsList();
} else {
console.log(
"Errore nel caricamento delle parti:",
response.message,
);
addNewRow(1);
}
return Promise.resolve(); // Risolvi la promessa
})
.catch(function (xhr, status, error) {
console.error("Errore in loadExistingParts:", {
status,
error,
response: xhr.responseText,
});
alert("Errore nel caricamento delle parti: " + error);
addNewRow(1);
return Promise.reject(error); // Rifiuta la promessa in caso di errore
});
}
function updatePartsList() {
$("#partsList").empty();
$("#partsTableBody tr").each(function () {
const partNumber = $(this).find(".part-number").val();
const partDescription = $(this).find(".part-description").val();
if (partNumber && partDescription) {
const listItem = `<li class="list-group-item" data-part-number="${partNumber}">${partNumber} - ${partDescription}</li>`;
$("#partsList").append(listItem);
}
});
}
let selectedPartNumber = null;
let markers = [];
let descriptionPosition = { x: 10, y: 10 };
let hasDescriptions = false;
$("#partsList").on("click", "li", function () {
selectedPartNumber = $(this).data("part-number");
console.log("Part number selezionato:", selectedPartNumber);
$(this).addClass("active").siblings().removeClass("active");
});
const canvas = document.getElementById("photoCanvas");
const ctx = canvas.getContext("2d");
$("#markerContainer").on("click", function (e) {
console.log("Click sul markerContainer rilevato");
if (selectedPartNumber !== null) {
const img = $("#samplePhoto");
const canvas = document.getElementById("photoCanvas");
const rect = canvas.getBoundingRect();
const container = img.parent();
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img.get(0).naturalWidth;
const scaleY = containerHeight / img.get(0).naturalHeight;
const scale = Math.min(scaleX, scaleY);
const x = (e.clientX - rect.left) / scale;
const y = (e.clientY - rect.top) / scale;
console.log("Coordinate cliccate (x, y):", x, y);
const existingMarker = markers.find(
(m) => m.partNumber == selectedPartNumber,
);
if (existingMarker) {
existingMarker.x = x;
existingMarker.y = y;
} else {
markers.push({ partNumber: selectedPartNumber, x, y });
}
console.log("Markers aggiornati:", markers);
updateMarkers();
if (hasDescriptions) {
drawDescriptions(descriptionPosition.x, descriptionPosition.y);
}
selectedPartNumber = null;
$("#partsList li").removeClass("active");
} else {
console.log("Nessun part number selezionato");
}
});
function updateMarkers() {
const img = $("#samplePhoto");
const container = img.parent();
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img.get(0).naturalWidth;
const scaleY = containerHeight / img.get(0).naturalHeight;
const scale = Math.min(scaleX, scaleY);
const markerContainer = $("#markerContainer");
markerContainer.empty();
markers.forEach((marker) => {
const scaledX = marker.x * scale;
const scaledY = marker.y * scale;
console.log(
"Aggiungo marker:",
marker.partNumber,
"a posizione (scaledX, scaledY):",
scaledX,
scaledY,
);
const $marker = $(
`<div class="draggable-marker">${marker.partNumber}</div>`,
).css({
left: scaledX - 8 + "px",
top: scaledY - 8 + "px",
});
markerContainer.append($marker);
makeDraggable($marker, marker, scale);
});
}
function makeDraggable($element, item, scale) {
let isDragging = false;
let currentX = parseFloat($element.css("left")) || 0;
let currentY = parseFloat($element.css("top")) || 0;
let initialX, initialY;
$element.on("mousedown", function (e) {
e.preventDefault();
isDragging = true;
initialX = e.clientX - currentX;
initialY = e.clientY - currentY;
$element.css("z-index", 1001);
});
$(document).on("mousemove", function (e) {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
const container = $("#photoCanvas").parent();
const containerWidth = container.width();
const containerHeight = container.height();
const maxX = containerWidth - $element.width();
const maxY = containerHeight - $element.height();
currentX = Math.max(0, Math.min(currentX, maxX));
currentY = Math.max(0, Math.min(currentY, maxY));
$element.css({
left: currentX + "px",
top: currentY + "px",
});
if (item.partNumber) {
item.x = (currentX + 8) / scale;
item.y = (currentY + 8) / scale;
} else {
descriptionPosition.x = (currentX + 5) / scale;
descriptionPosition.y = (currentY + 5) / scale;
}
}
});
$(document).on("mouseup", function () {
isDragging = false;
$element.css("z-index", 1000);
});
}
function drawDescriptions(x, y) {
const img = $("#samplePhoto");
const container = img.parent();
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img.get(0).naturalWidth;
const scaleY = containerHeight / img.get(0).naturalHeight;
const scale = Math.min(scaleX, scaleY);
const partsList = [];
$("#partsTableBody tr").each(function () {
const partNumber = $(this).find(".part-number").val();
const partDescription = $(this).find(".part-description").val();
if (partNumber && partDescription) {
partsList.push(`${partNumber} ${partDescription}`);
}
});
const descriptionList = $("#descriptionList");
descriptionList.empty();
descriptionList.css({
display: "block",
top: y * scale + "px",
left: x * scale + "px",
width: "200px",
});
partsList.forEach((part) => {
descriptionList.append(`<div>${part}</div>`);
});
updateMarkers();
}
function clearCanvasMarkers() {
markers = [];
hasDescriptions = false;
$("#descriptionList").css("display", "none");
$("#markerContainer").empty();
const canvas = document.getElementById("photoCanvas");
const img = $("#samplePhoto");
const ctx = canvas.getContext("2d");
const container = img.parent();
const containerWidth = container.width();
const containerHeight = container.height();
const scaleX = containerWidth / img.get(0).naturalWidth;
const scaleY = containerHeight / img.get(0).naturalHeight;
const scale = Math.min(scaleX, scaleY);
canvas.width = img.get(0).naturalWidth * scale;
canvas.height = img.get(0).naturalHeight * scale;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img.get(0), 0, 0, canvas.width, canvas.height);
}
$("#addDescriptionsBtn").on("click", function () {
hasDescriptions = true;
descriptionPosition = { x: 10, y: 10 };
drawDescriptions(descriptionPosition.x, descriptionPosition.y);
makeDraggable(
$("#descriptionList"),
descriptionPosition,
Math.min(
$("#photoCanvas").parent().width() /
$("#samplePhoto").get(0).naturalWidth,
$("#photoCanvas").parent().height() /
$("#samplePhoto").get(0).naturalHeight,
),
);
});
$("#removeAnnotationsBtn").on("click", function () {
clearCanvasMarkers();
});
$("#savePhotoBtn").on("click", function () {
const canvas = document.getElementById("photoCanvas");
const img = $("#samplePhoto");
const ctx = canvas.getContext("2d");
canvas.width = img.get(0).naturalWidth;
canvas.height = img.get(0).naturalHeight;
ctx.drawImage(img.get(0), 0, 0);
const partsList = [];
$("#partsTableBody tr").each(function () {
const partNumber = $(this).find(".part-number").val();
const partDescription = $(this).find(".part-description").val();
if (partNumber && partDescription) {
partsList.push(`${partNumber} ${partDescription}`);
}
});
if (hasDescriptions) {
ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
ctx.fillRect(
descriptionPosition.x,
descriptionPosition.y,
200,
partsList.length * 12 + 10,
);
ctx.fillStyle = "#000000";
ctx.font = "10px Arial";
partsList.forEach((part, index) => {
ctx.fillText(
part,
descriptionPosition.x + 5,
descriptionPosition.y + 12 + index * 12,
);
});
}
markers.forEach((marker) => {
ctx.beginPath();
ctx.arc(marker.x, marker.y, 8, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
ctx.fill();
ctx.strokeStyle = "#ff0000";
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillStyle = "#ffffff";
ctx.font = "bold 8px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(marker.partNumber, marker.x, marker.y);
});
const dataURL = canvas.toDataURL("image/png");
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
const defaultName = `photo_${$("#partsModal").data("iddatadb")}_${timestamp}.png`;
const newName = prompt(
"Inserisci il nome del file (senza estensione):",
defaultName.split(".png")[0],
);
if (newName) {
const finalName = newName + "_" + timestamp + ".png";
$.ajax({
url: "save_annotated_photo.php",
method: "POST",
data: { dataURL: dataURL, filename: finalName },
success: function (response) {
if (response.success) {
alert(
"Foto salvata con successo: " + response.file_path,
);
} else {
alert("Errore nel salvataggio: " + response.message);
}
},
error: function (xhr, status, error) {
alert("Errore nel salvataggio della foto: " + error);
},
});
}
});
$(document).on("mouseenter", "tr", function () {
console.log("Mouse entrato su riga");
});
$(document).on("mouseleave", "tr", function () {
console.log("Mouse uscito da riga");
});
});
File diff suppressed because it is too large Load Diff
-68
View File
@@ -1,68 +0,0 @@
<?php
// HTML per il modal delle foto
?>
<div id="photosModal" class="modal">
<div class="modal-content">
<span class="close-btn">×</span>
<div id="popupContent">
<p>Loading...</p>
</div>
</div>
</div>
<div id="imageModal" class="image-modal">
<span class="image-modal-close">&times;</span>
<img class="image-modal-content" id="enlargedImage">
</div>
<style>
.photo-item {
transition: background-color 0.3s;
}
.photo-item:hover {
background-color: #f8f9fa;
}
#dropArea.highlight {
border-color: #28a745;
background-color: #e9ecef;
}
.image-modal {
display: none;
position: fixed;
z-index: 1100;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.8);
}
.image-modal-content {
margin: auto;
display: block;
max-width: 90%;
max-height: 90vh;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.image-modal-close {
position: absolute;
top: 15px;
right: 35px;
color: #fff;
font-size: 40px;
font-weight: bold;
cursor: pointer;
}
.image-modal-close:hover,
.image-modal-close:focus {
color: #bbb;
text-decoration: none;
}
</style>
-428
View File
@@ -1,428 +0,0 @@
<?php
// photos_popup.php
include('include/headscript.php');
// Includi Fabric.js solo per questa pagina
?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<?php
// Includi l'autoloader di Composer
require_once __DIR__ . '/../../vendor/autoload.php';
use Endroid\QrCode\Color\Color;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\RoundBlockSizeMode;
use Endroid\QrCode\Writer\PngWriter;
// Abilita logging per debug
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/photos_popup_debug.log');
// Log iniziale
error_log("Richiesta a photos_popup.php: " . print_r($_GET, true));
// Carica le variabili d'ambiente
try {
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../../');
$dotenv->load();
} catch (Exception $e) {
error_log("Errore nel caricamento del file .env: " . $e->getMessage());
?>
<div class="popup-content">
<p>Errore: Impossibile caricare il file di configurazione.</p>
</div>
<?php
exit;
}
// Verifica che BASE_URL sia definito
if (!isset($_ENV['BASE_URL'])) {
error_log("Errore: la variabile BASE_URL non è definita nel file .env");
?>
<div class="popup-content">
<p>Errore: Variabile BASE_URL non definita.</p>
</div>
<?php
exit;
}
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Verifica che almeno uno degli ID sia passato
$iddatadb = isset($_GET['iddatadb']) && !empty($_GET['iddatadb']) ? intval($_GET['iddatadb']) : null;
$idquotations = isset($_GET['idquotations']) && !empty($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
if (!$iddatadb && !$idquotations) {
error_log("Errore: ID riga o ID quotations non fornito");
?>
<div class="popup-content">
<p>Errore: ID riga o ID quotations non fornito.</p>
</div>
<?php
exit;
}
if ($iddatadb && $idquotations) {
error_log("Errore: Non è possibile specificare sia iddatadb che idquotations");
?>
<div class="popup-content">
<p>Errore: Non è possibile specificare sia iddatadb che idquotations.</p>
</div>
<?php
exit;
}
// Determina quale ID e tabella usare
$paramName = $iddatadb ? 'iddatadb' : 'id'; // Usa 'id' per quotations
$paramValue = $iddatadb ?: $idquotations;
$table = $iddatadb ? 'datadb' : 'quotations';
$field = $iddatadb ? 'sample_code' : 'description'; // Usa 'description' per quotations
$photoTable = 'datadb_photos'; // Usa sempre datadb_photos
$photoParamName = $iddatadb ? 'iddatadb' : 'idquotations'; // Usa 'idquotations' per datadb_photos
// Recupera i dettagli della riga
try {
$stmt = $pdo->prepare("SELECT {$paramName}, {$field} FROM {$table} WHERE {$paramName} = ?");
$stmt->execute([$paramValue]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
error_log("Errore: Riga non trovata per {$paramName} = {$paramValue}");
?>
<div class="popup-content">
<p>Errore: Riga non trovata.</p>
</div>
<?php
exit;
}
} catch (Exception $e) {
error_log("Errore query dettagli riga: " . $e->getMessage());
?>
<div class="popup-content">
<p>Errore: Impossibile recuperare i dettagli della riga.</p>
</div>
<?php
exit;
}
$id = $row[$paramName];
$code = $row[$field] ?? 'Non disponibile';
// Recupera le foto associate alla riga
try {
$stmt = $pdo->prepare("SELECT id, file_path, file_name, description, uploaded_at FROM {$photoTable} WHERE {$photoParamName} = ? ORDER BY uploaded_at DESC");
$stmt->execute([$paramValue]);
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
error_log("Errore query foto: " . $e->getMessage());
$photos = []; // Imposta array vuoto in caso di errore
}
// Definisci il percorso base per le foto
$photoBasePath = '../photostrf/';
// Usa la variabile d'ambiente BASE_URL
$baseUrl = rtrim($_ENV['BASE_URL'], '/');
$uploadUrl = $iddatadb
? $baseUrl . "/upload_photos_mobile.php?iddatadb=" . $iddatadb
: $baseUrl . "/upload_photos_mobile.php?idquotations=" . $idquotations;
// Genera il QR code con endroid/qr-code 6.0.6
$qrCodeDir = '../photostrf/qrcodes/';
if (!is_dir($qrCodeDir)) {
mkdir($qrCodeDir, 0755, true);
}
$qrCodeFile = $qrCodeDir . "qrcode_{$id}.png";
$writer = new PngWriter();
$qrCode = new QrCode(
data: $uploadUrl,
encoding: new Encoding('UTF-8'),
errorCorrectionLevel: ErrorCorrectionLevel::Low,
size: 150,
margin: 10,
roundBlockSizeMode: RoundBlockSizeMode::Margin,
foregroundColor: new Color(0, 0, 0),
backgroundColor: new Color(255, 255, 255)
);
$result = $writer->write($qrCode);
$result->saveToFile($qrCodeFile);
?>
<div class="popup-content">
<div id="loader" class="loader" style="display: none;">
<div style="text-align: center; color: white;">
<i class="fas fa-spinner fa-spin" style="font-size: 40px;"></i>
<p>Caricamento in corso...</p>
</div>
</div>
<h3>Manage Photos</h3>
<p><strong>ID:</strong> <?= htmlspecialchars($id) ?></p>
<p><strong>Code:</strong> <?= htmlspecialchars($code) ?></p>
<!-- QR Code per il caricamento da mobile -->
<div style="text-align: center; margin-bottom: 20px;">
<p>Scan the QR Code with the mobile to take photo with camera:</p>
<img src="../photostrf/qrcodes/qrcode_<?= htmlspecialchars($id) ?>.png" alt="QR Code" style="max-width: 150px;">
<p style="margin-top: 10px;">
<a href="<?= htmlspecialchars($uploadUrl) ?>" target="_blank"><?= htmlspecialchars($uploadUrl) ?></a>
</p>
</div>
<!-- Area drag-and-drop per il caricamento delle foto -->
<div id="dropArea" style="border: 2px dashed #ccc; padding: 20px; text-align: center; margin-bottom: 20px;">
<p>Drag the photo here or click to select</p>
<input type="file" id="photoInput" multiple accept="image/*" style="display: none;">
</div>
<!-- Area per la webcam -->
<div id="webcamArea" style="display: none; text-align: center; margin-bottom: 20px;">
<p>Webcam Preview</p>
<select id="webcamSelect" style="margin-bottom: 10px; padding: 5px;">
<option value="">Select a webcam</option>
</select>
<video id="webcamVideo" autoplay playsinline style="max-width: 100%; max-height: 300px;"></video>
<div style="margin-top: 10px;">
<button id="captureBtn" style="padding: 10px 20px; background: #28a745; color: white; border: none; cursor: pointer;">Capture Photo</button>
<button id="closeWebcamBtn" style="padding: 10px 20px; background: #dc3545; color: white; border: none; cursor: pointer;">Close Webcam</button>
</div>
</div>
<button id="openWebcamBtn" style="padding: 10px 20px; background: #007bff; color: white; border: none; cursor: pointer; margin-bottom: 20px;">Take Photo with Webcam</button>
<!-- Elenco delle foto -->
<div id="photosList">
<?php if (empty($photos)): ?>
<p>Nessuna foto presente.</p>
<?php else: ?>
<?php foreach ($photos as $photo): ?>
<?php
$filePath = $photoBasePath . $photo['file_path'];
$fileExists = file_exists($filePath);
?>
<div class="photo-item" data-photo-id="<?= $photo['id'] ?>" style="display: flex; align-items: center; margin-bottom: 10px; border-bottom: 1px solid #eee; padding-bottom: 10px;">
<div style="flex: 1;">
<?php if ($fileExists): ?>
<img src="../photostrf/<?= htmlspecialchars($photo['file_path']) ?>" alt="<?= htmlspecialchars($photo['file_name']) ?>" class="thumbnail" style="max-width: 100px; max-height: 100px; margin-right: 10px; cursor: pointer;">
<?php else: ?>
<p style="color: red;">[File non trovato]</p>
<?php endif; ?>
<p style="margin: 0;">
<strong>Nome:</strong> <?= htmlspecialchars($photo['file_name']) ?><br>
<strong>Caricata il:</strong> <?= htmlspecialchars($photo['uploaded_at']) ?><br>
<strong>Descrizione:</strong> <?= htmlspecialchars($photo['description'] ?? 'Nessuna descrizione') ?>
</p>
</div>
<button class="delete-photo-btn" data-photo-id="<?= $photo['id'] ?>" style="background: none; border: none; color: #dc3545; cursor: pointer;">
<i class="fas fa-trash"></i>
</button>
</div>
<?php endforeach; ?>
<!-- Bottone Crea Collage -->
<button id="createCollageBtn" style="padding: 10px 20px; background: #ffc107; color: white; border: none; cursor: pointer; margin-top: 20px;">Crea Collage</button>
<?php endif; ?>
</div>
<!-- Modale per l'immagine ingrandita -->
<div id="imageModal" class="image-modal" style="display: none;">
<span class="image-modal-close">&times;</span>
<img id="enlargedImage" class="image-modal-content" src="" alt="Immagine ingrandita">
</div>
<!-- Modale per collage -->
<div id="collageModal" class="modal" style="display: none; position: fixed; z-index: 1002; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.8);">
<div class="modal-content" style="background: white; margin: 5% auto; padding: 20px; width: 80%; max-width: 1200px; position: relative;">
<span class="close-collage" style="position: absolute; top: 10px; right: 20px; font-size: 30px; font-weight: bold; color: #333; cursor: pointer; z-index: 1003; background: #fff; padding: 5px 10px; border-radius: 50%;">&times;</span>
<h3>Crea Collage</h3>
<!-- Lista foto selezionabili -->
<div id="collagePhotoList" style="max-height: 200px; overflow-y: auto; margin-bottom: 20px;">
<?php foreach ($photos as $photo): ?>
<div style="display: inline-block; margin: 10px;">
<input type="checkbox" class="photo-checkbox" data-path="../photostrf/<?= htmlspecialchars($photo['file_path']) ?>">
<img src="../photostrf/<?= htmlspecialchars($photo['file_path']) ?>" alt="" style="width: 80px; height: 80px;">
</div>
<?php endforeach; ?>
</div>
<!-- Bottone per aggiungere selezionate al canvas -->
<button id="addToCanvasBtn">Aggiungi Selezionate al Canvas</button>
<!-- Canvas per editing -->
<canvas id="collageCanvas" width="960" height="720" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
<!-- Pannello dei livelli -->
<div id="layersPanel" style="width: 120px; max-height: 600px; overflow-y: auto; background: #f8f9fa; padding: 10px; position: absolute; right: 0; top: 60px;">
<h4 style="margin: 0 0 10px 0; font-size: 16px;">Livelli</h4>
<ul id="layersList" style="list-style: none; padding: 0;"></ul>
</div>
<!-- Bottoni azioni -->
<div style="margin-top: 20px; display: flex; flex-wrap: wrap; gap: 5px;">
<button id="saveCollageBtn" title="Salva il collage"><i class="fas fa-save"></i></button>
<button id="bringToFrontBtn" title="Porta in primo piano"><i class="fas fa-arrow-up"></i></button>
<button id="sendToBackBtn" title="Manda in fondo"><i class="fas fa-arrow-down"></i></button>
<button id="bringForwardBtn" title="Sposta avanti di un livello"><i class="fas fa-arrow-circle-up"></i></button>
<button id="sendBackwardBtn" title="Sposta indietro di un livello"><i class="fas fa-arrow-circle-down"></i></button>
<button id="cropImageBtn" title="Ritaglia immagine selezionata" disabled><i class="fas fa-crop"></i></button>
<button id="applyCropBtn" title="Applica ritaglio" disabled><i class="fas fa-crop"></i> Applica</button>
<button id="cancelCropBtn" title="Annulla ritaglio" disabled><i class="fas fa-crop"></i> Annulla</button>
<button id="removeBackgroundBtn" title="Rimuovi sfondo immagine selezionata" disabled><i class="fas fa-eraser"></i> Rimuovi Sfondo</button>
<button id="removeImageBtn" title="Rimuovi immagine selezionata" disabled><i class="fas fa-trash-alt"></i> Rimuovi</button>
<button id="undoBtn" title="Annulla ultima azione" disabled><i class="fas fa-undo"></i></button>
<p id="backgroundRemovalInstruction" style="display: none; color: #007bff;">Clicca sull'immagine per selezionare il colore dello sfondo da rimuovere</p>
</div>
</div>
</div>
</div>
<style>
.photo-item {
transition: background-color 0.3s;
}
.photo-item:hover {
background-color: #f8f9fa;
}
#dropArea.highlight {
border-color: #28a745;
background-color: #e9ecef;
}
.image-modal {
display: none;
position: fixed;
z-index: 1001;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.8);
}
.image-modal-content {
margin: auto;
display: block;
max-width: 90%;
max-height: 90vh;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.image-modal-close {
position: absolute;
top: 15px;
right: 35px;
color: #fff;
font-size: 40px;
font-weight: bold;
cursor: pointer;
}
.image-modal-close:hover,
.image-modal-close:focus {
color: #bbb;
text-decoration: none;
}
.loader {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1002;
display: flex;
align-items: center;
justify-content: center;
}
.loader .fa-spinner {
font-size: 40px;
color: white;
}
.loader p {
margin-top: 10px;
font-size: 16px;
color: white;
}
/* Stile per i pulsanti del modale collage */
#collageModal button {
padding: 8px 12px;
margin: 5px;
border: none;
cursor: pointer;
border-radius: 4px;
display: flex;
align-items: center;
gap: 5px;
font-size: 14px;
}
#saveCollageBtn {
background: #28a745;
color: white;
}
#bringToFrontBtn,
#sendToBackBtn,
#bringForwardBtn,
#sendBackwardBtn {
background: #007bff;
color: white;
padding: 8px;
}
#cropImageBtn,
#applyCropBtn,
#cancelCropBtn,
#removeBackgroundBtn,
#removeImageBtn,
#undoBtn {
background: #ffc107;
color: white;
}
#collageModal button:disabled {
background: #ccc;
cursor: not-allowed;
}
#collageModal button i {
font-size: 16px;
}
/* Stile per il pannello dei livelli */
#layersPanel {
z-index: 1002;
}
#layersPanel li {
margin-bottom: 5px;
}
#layersPanel img {
width: 50px;
height: 50px;
border: 2px solid #ccc;
cursor: pointer;
object-fit: cover;
}
#layersPanel img:hover {
border-color: #007bff;
}
</style>
+30
View File
@@ -0,0 +1,30 @@
<?php
require_once(__DIR__ . '/class/db-functions.php');
header('Content-Type: application/json; charset=utf-8');
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$sql = "
SELECT
pm.id,
c.client_name,
pm.price_ref,
pm.price,
pm.source_file,
pm.created_at
FROM price_mapping pm
LEFT JOIN clients c ON pm.idclient = c.idclient
ORDER BY pm.id DESC
";
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($data, JSON_UNESCAPED_UNICODE);
} catch (Throwable $e) {
echo json_encode(['error' => $e->getMessage()]);
}
@@ -1,76 +0,0 @@
<?php
header('Content-Type: application/json');
require_once 'class/db-functions.php';
$response = ["success" => false, "message" => ""];
try {
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
throw new Exception("Invalid request method.");
}
// Recupera e sanifica i dati
$id = intval($_POST['id']);
$name = trim($_POST['name']);
$header_row = intval($_POST['header_row']);
$start_column = trim($_POST['start_column']);
$description = trim($_POST['description'] ?? '');
$target_table = trim($_POST['target_table']);
$idclient = intval($_POST['client_id'] ?? 0); // Usa client_id dal form
$clientname = trim($_POST['client_name'] ?? ''); // Usa client_name dal form
$idschema = intval($_POST['idschema'] ?? 0); // Nuovo campo
$schemaname = trim($_POST['schemaname'] ?? ''); // Corretto da schemamaname
$idroutine = isset($_POST['idroutine']) && $_POST['idroutine'] !== '' ? intval($_POST['idroutine']) : null; // Aggiunto idroutine
$button_size = trim($_POST['button_size'] ?? 'medium'); // Nuovo campo
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff'); // Nuovo campo
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff'); // Nuovo campo
$button_label = trim($_POST['button_label'] ?? 'Click Me'); // Nuovo campo
// Controllo sui campi obbligatori
if (empty($id) || empty($name) || empty($header_row) || empty($start_column) || empty($target_table) || $idschema <= 0) {
throw new Exception("All fields marked with * are required, including schema.");
}
// Validazione del idclient
if ($idclient <= 0) {
throw new Exception("Please select a valid client.");
}
// Connessione al database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Aggiorna il database, includendo i nuovi campi
$stmt = $pdo->prepare("UPDATE excel_templates
SET name = ?, header_row = ?, start_column = ?, description = ?, target_table = ?,
idclient = ?, clientname = ?, schemaname = ?, idschema = ?, idroutine = ?,
button_size = ?, button_bg_color = ?, button_text_color = ?, button_label = ?,
updated_at = NOW()
WHERE id = ?");
$stmt->execute([
$name,
$header_row,
$start_column,
$description,
$target_table,
$idclient,
$clientname,
$schemaname,
$idschema,
$idroutine,
$button_size,
$button_bg_color,
$button_text_color,
$button_label,
$id
]);
// rowCount potrebbe essere 0 se non ci sono modifiche, quindi consideriamo comunque un successo
$response["success"] = true;
$response["message"] = "Template updated successfully!";
} catch (Exception $e) {
$response["message"] = $e->getMessage();
}
// Restituisce un JSON per il fetch
echo json_encode($response);
-117
View File
@@ -1,117 +0,0 @@
<?php
// Sopprime eventuali output di errori (li logghiamo invece di mostrarli)
ob_start();
ini_set('display_errors', 1);
error_reporting(E_ALL);
// Inizia la sessione
session_start();
// Includi PHPSpreadsheet
require_once '../../vendor/autoload.php';
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => ''];
try {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['excel_file'])) {
$template_id = isset($_POST['template_id']) ? intval($_POST['template_id']) : 0;
$header_row = isset($_POST['header_row']) ? intval($_POST['header_row']) : 1;
$start_column = isset($_POST['start_column']) ? intval($_POST['start_column']) : 1;
$file = $_FILES['excel_file'];
$fileError = $file['error'];
if ($fileError === UPLOAD_ERR_OK) {
// Recupera l'ID dell'utente loggato (assumiamo sia disponibile in $iduserlogin)
if (!isset($iduserlogin)) {
$iduserlogin = 1; // Valore di default
error_log("Warning: iduserlogin non definito, usando 1 come default");
}
// Genera il nome del file rinominato
$timestamp = date('YmdHis');
$originalFilename = basename($file['name']);
$newFilename = "{$iduserlogin}-{$timestamp}-{$originalFilename}";
$importFolder = __DIR__ . '/imported_trf/';
if (!file_exists($importFolder)) {
mkdir($importFolder, 0777, true);
}
$destination = $importFolder . $newFilename;
// Sposta il file
if (!move_uploaded_file($file['tmp_name'], $destination)) {
throw new Exception("Errore durante lo spostamento del file in $destination");
}
error_log("File spostato con successo in: $destination");
// Carica il file rinominato con PHPSpreadsheet
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($destination);
$worksheet = $spreadsheet->getActiveSheet();
$highestRow = $worksheet->getHighestRow();
$highestColumn = $worksheet->getHighestColumn();
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
$startRow = max(1, $header_row);
$startColumn = max(1, $start_column);
// Debug dei parametri
error_log("Processing - startRow: $startRow, startColumn: $startColumn, highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex");
// Validazione degli indici
if ($startRow > $highestRow) {
$response['error'] = "La riga di partenza ($startRow) supera il numero totale di righe ($highestRow).";
} elseif ($startColumn > $highestColumnIndex) {
$response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex).";
} else {
$excelData = [];
// Estrai la riga degli header
$headerRowData = [];
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
$cell = $worksheet->getCell($columnLetter . $header_row);
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
$headerRowData[] = htmlspecialchars($cellValue ?: '');
}
// Estrai i dati a partire dalla riga successiva
for ($row = $startRow + 1; $row <= $highestRow; $row++) {
$rowData = [];
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
$cell = $worksheet->getCell($columnLetter . $row);
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
$rowData[] = htmlspecialchars($cellValue ?: '');
}
if (!empty(array_filter($rowData))) {
$excelData[] = $rowData;
}
}
// Salva i dati in sessione
$_SESSION['excel_data'] = $excelData;
$_SESSION['template_id'] = $template_id;
$_SESSION['headers'] = $headerRowData; // Salva gli header in sessione
$response['rows'] = $excelData;
$response['columns'] = $headerRowData; // Usa gli header reali
$response['template_id'] = $template_id;
$response['filename'] = $newFilename; // Aggiungi il nome del file rinominato
}
} else {
$response['error'] = "Errore nell'upload del file: Codice errore $fileError.";
}
} else {
$response['error'] = "Richiesta non valida.";
}
} catch (Exception $e) {
$response['error'] = "Errore durante il caricamento del file: " . $e->getMessage();
error_log("Exception in process_import_xls.php: " . $e->getMessage());
}
// Pulisce qualsiasi output indesiderato
ob_end_clean();
// Invia la risposta JSON
header('Content-Type: application/json');
echo json_encode($response);
exit;
-170
View File
@@ -1,170 +0,0 @@
<?php
// Sopprime eventuali output di errori (li logghiamo invece di mostrarli)
ob_start();
ini_set('display_errors', 0);
error_reporting(E_ALL);
// Inizia la sessione
session_start();
// Includi PHPSpreadsheet e la classe DBHandler
require_once '../../vendor/autoload.php';
require_once __DIR__ . '/class/db-functions.php';
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'apply_routine' => false];
try {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['excel_file'])) {
$template_id = isset($_POST['template_id']) ? intval($_POST['template_id']) : 0;
$header_row = isset($_POST['header_row']) ? intval($_POST['header_row']) : 1;
$start_column = isset($_POST['start_column']) ? intval($_POST['start_column']) : 1;
// Debug del template_id ricevuto
error_log("Received template_id from POST: " . print_r($_POST['template_id'], true));
error_log("Converted template_id: $template_id");
$file = $_FILES['excel_file'];
$fileError = $file['error'];
if ($fileError === UPLOAD_ERR_OK) {
// Recupera l'ID dell'utente loggato
if (!isset($iduserlogin)) {
$iduserlogin = 1;
error_log("Warning: iduserlogin non definito, usando 1 come default");
}
// Genera il nome del file rinominato
$timestamp = date('YmdHis');
$originalFilename = basename($file['name']);
$newFilename = "{$iduserlogin}-{$timestamp}-{$originalFilename}";
$importFolder = __DIR__ . '/imported_trf/';
if (!file_exists($importFolder)) {
mkdir($importFolder, 0777, true);
}
$destination = $importFolder . $newFilename;
// Sposta il file
if (!move_uploaded_file($file['tmp_name'], $destination)) {
throw new Exception("Errore durante lo spostamento del file in $destination");
}
error_log("File spostato con successo in: $destination");
// Connessione al database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Recupera il mapping da template_mapping
$stmt = $pdo->prepare("SELECT field_id AS excel_column, field_id AS mysql_column, data_type, is_required, default_value, is_manual FROM template_mapping WHERE template_id = ?");
$stmt->execute([$template_id]);
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Debug dei mapping
error_log("Mappings found for template_id $template_id: " . print_r($mappings, true));
if (empty($mappings)) {
$response['error'] = "Nessun mapping trovato per il template con ID $template_id";
} else {
// Carica il file rinominato con PHPSpreadsheet
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($destination);
$worksheet = $spreadsheet->getActiveSheet();
$highestRow = $worksheet->getHighestRow();
$highestColumn = $worksheet->getHighestColumn();
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
$startRow = max(1, $header_row);
$startColumn = max(1, $start_column);
// Debug dei parametri
error_log("Processing - template_id: $template_id, startRow: $startRow, startColumn: $startColumn, highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex");
// Validazione degli indici
if ($startRow > $highestRow) {
$response['error'] = "La riga di partenza ($startRow) supera il numero totale di righe ($highestRow).";
} elseif ($startColumn > $highestColumnIndex) {
$response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex).";
} else {
$excelData = [];
// Estrai la riga degli header
$headerRowData = [];
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
$cell = $worksheet->getCell($columnLetter . $header_row);
$cellValue = $cell ? $cell->getCalculatedValue() : '';
$headerRowData[] = htmlspecialchars($cellValue ?: '');
}
// Estrai i dati a partire dalla riga successiva, includendo excelrow
for ($row = $startRow + 1; $row <= $highestRow; $row++) {
$rowData = [];
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
$cell = $worksheet->getCell($columnLetter . $row);
$cellValue = $cell ? $cell->getCalculatedValue() : '';
$rowData[] = htmlspecialchars($cellValue ?: '');
}
if (!empty(array_filter($rowData))) {
$excelData[] = ['data' => $rowData, 'excelrow' => $row];
}
}
// Recupera routine dal template
$stmt = $pdo->prepare("SELECT idroutine, idclient FROM excel_templates WHERE id = ?");
$stmt->execute([$template_id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if ($template && $template['idroutine']) {
$stmtRoutine = $pdo->prepare("SELECT idroutine, name, filename, headerrow, instruction FROM routine WHERE idroutine = ?");
$stmtRoutine->execute([$template['idroutine']]);
$routineData = $stmtRoutine->fetch(PDO::FETCH_ASSOC);
if ($routineData) {
$response['apply_routine'] = true;
$response['routine_data'] = [
'name' => $routineData['name'] ?? 'Routine Sconosciuta',
'instruction' => $routineData['instruction'] ?? 'Nessuna descrizione disponibile',
'filename' => $routineData['filename'] ?? '',
'headerrow' => $routineData['headerrow'] ?? $header_row
];
error_log("Routine rilevata per template {$template_id}: " . print_r($routineData, true));
} else {
error_log("Errore: Nessuna routine trovata per idroutine {$template['idroutine']}");
}
} else {
error_log("Nessuna routine associata al template {$template_id}");
}
// Aggiungi idclient alla risposta
$response['idclient'] = $template['idclient'] ?? null;
// Salva i dati in sessione
$_SESSION['excel_data'] = $excelData;
$_SESSION['template_id'] = $template_id;
$_SESSION['headers'] = $headerRowData;
$_SESSION['mappings'] = $mappings;
// Includi excel_data nella risposta JSON in ogni caso
$response['excel_data'] = $excelData;
$response['rows'] = array_column($excelData, 'data');
$response['columns'] = $headerRowData;
$response['template_id'] = $template_id;
$response['filename'] = $newFilename;
}
}
} else {
$response['error'] = "Errore nell'upload del file: Codice errore $fileError.";
}
} else {
$response['error'] = "Richiesta non valida.";
}
} catch (Exception $e) {
$response['error'] = "Errore durante il caricamento del file: " . $e->getMessage();
error_log("Exception in process_import_xls2.php: " . $e->getMessage());
}
// Pulisce qualsiasi output indesiderato
ob_end_clean();
// Invia la risposta JSON
header('Content-Type: application/json');
echo json_encode($response);
exit;
@@ -1,67 +0,0 @@
<?php
header('Content-Type: application/json');
require_once 'class/db-functions.php';
$response = ["success" => false, "message" => ""];
try {
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
throw new Exception("Invalid request method.");
}
// Recupera e sanifica i dati
$name = trim($_POST['name']);
$header_row = intval($_POST['header_row']);
$start_column = trim($_POST['start_column']);
$description = trim($_POST['description'] ?? '');
$target_table = trim($_POST['target_table']);
$idclient = intval($_POST['client_id'] ?? 0);
$clientname = trim($_POST['client_name'] ?? '');
$idschema = intval($_POST['idschema'] ?? 0);
$schemaname = trim($_POST['schemaname'] ?? '');
$idroutine = isset($_POST['idroutine']) && $_POST['idroutine'] !== '' ? intval($_POST['idroutine']) : null;
$button_size = trim($_POST['button_size'] ?? 'medium');
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff');
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff');
$button_label = trim($_POST['button_label'] ?? 'Click Me');
// Controllo sui campi obbligatori
if (empty($name) || empty($header_row) || empty($start_column) || empty($target_table) || $idclient <= 0 || $idschema <= 0) {
throw new Exception("All fields marked with * are required, including client and schema.");
}
// Connessione al database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Inserisci il nuovo template
$stmt = $pdo->prepare("
INSERT INTO excel_templates
(name, header_row, start_column, description, target_table, idclient, clientname, idschema, schemaname, idroutine,
button_size, button_bg_color, button_text_color, button_label, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
");
$stmt->execute([
$name,
$header_row,
$start_column,
$description,
$target_table,
$idclient,
$clientname,
$idschema,
$schemaname,
$idroutine,
$button_size,
$button_bg_color,
$button_text_color,
$button_label
]);
$response["success"] = true;
$response["message"] = "Template created successfully!";
} catch (Exception $e) {
$response["message"] = $e->getMessage();
}
echo json_encode($response);
-574
View File
@@ -1,574 +0,0 @@
<?php
// Abilita errori per debug
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/quotations_debug.log');
if (!file_exists(__DIR__ . '/quotations_debug.log')) {
file_put_contents(__DIR__ . '/quotations_debug.log', "Inizio operazioni alle " . date('Y-m-d H:i:s') . "\n", FILE_APPEND);
}
// Log iniziale
error_log("Inizio operazioni alle " . date('Y-m-d H:i:s'));
include('include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Recupera l'ID dell'utente loggato
$user_id = $iduserlogin ?? 1;
// Gestione creazione nuova quotation (crea record vuoto su conferma)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create') {
$description = '';
$customer = '';
try {
$stmt = $pdo->prepare("INSERT INTO quotations (description, customer, iduser) VALUES (?, ?, ?)");
$success = $stmt->execute([$description, $customer, $user_id]);
if ($success) {
$newId = $pdo->lastInsertId();
error_log("Creata nuova quotation ID: $newId");
header("Location: quotations.php?edit_id=" . $newId . "&status=success&message=" . urlencode("Quotation creata con successo"));
} else {
error_log("Errore: Impossibile creare la quotation, nessun ID generato.");
header("Location: quotations.php?status=error&message=" . urlencode("Errore durante la creazione della quotation"));
}
} catch (PDOException $e) {
error_log("Errore PDO durante la creazione della quotation: " . $e->getMessage());
header("Location: quotations.php?status=error&message=" . urlencode("Errore database: " . $e->getMessage()));
}
exit;
}
// Gestione modifica quotation
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update' && isset($_POST['id'])) {
$id = intval($_POST['id']);
$description = $_POST['description'] ?? '';
$customer = $_POST['customer'] ?? '';
try {
$stmt = $pdo->prepare("UPDATE quotations SET description = ?, customer = ? WHERE id = ? AND iduser = ?");
$stmt->execute([$description, $customer, $id, $user_id]);
error_log("Modificata quotation ID: $id");
header("Location: quotations.php?status=success&message=" . urlencode("Quotation modificata con successo"));
} catch (PDOException $e) {
error_log("Errore PDO durante la modifica della quotation: " . $e->getMessage());
header("Location: quotations.php?status=error&message=" . urlencode("Errore database: " . $e->getMessage()));
}
exit;
}
// Gestione cancellazione quotation
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'delete' && isset($_POST['id'])) {
$id = intval($_POST['id']);
try {
$stmt = $pdo->prepare("DELETE FROM quotations WHERE id = ? AND iduser = ?");
$stmt->execute([$id, $user_id]);
error_log("Cancellata quotation ID: $id");
header("Location: quotations.php?status=success&message=" . urlencode("Quotation cancellata con successo"));
} catch (PDOException $e) {
error_log("Errore PDO durante la cancellazione della quotation: " . $e->getMessage());
header("Location: quotations.php?status=error&message=" . urlencode("Errore database: " . $e->getMessage()));
}
exit;
}
// Recupera tutte le quotations per l'utente
try {
$stmt = $pdo->prepare("SELECT * FROM quotations WHERE iduser = ? ORDER BY creation_date DESC");
$stmt->execute([$user_id]);
$quotations = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("Errore PDO durante il recupero delle quotations: " . $e->getMessage());
$quotations = [];
}
// Verifica se è richiesta la modifica di una quotation
$editQuotation = null;
if (isset($_GET['edit_id'])) {
$editId = intval($_GET['edit_id']);
try {
$stmt = $pdo->prepare("SELECT * FROM quotations WHERE id = ? AND iduser = ?");
$stmt->execute([$editId, $user_id]);
$editQuotation = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$editQuotation) {
error_log("Nessuna quotation trovata per id: $editId");
}
} catch (PDOException $e) {
error_log("Errore PDO durante il recupero della quotation per modifica: " . $e->getMessage());
$editQuotation = null;
}
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2Lw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<style>
.cell-changed {
background-color: #fff3b0 !important;
transition: background-color 0.3s ease;
}
input.manual-input,
select.manual-input {
background-color: #fff3cd;
}
input.required-input,
select.required-input {
background-color: #f8d7da;
}
input,
select,
textarea {
width: 100%;
box-sizing: border-box;
border: 1px solid #ced4da;
border-radius: 4px;
padding: 5px;
font-size: 14px;
}
.action-btn {
padding: 6px 8px;
margin-right: 5px;
border: none;
border-radius: 5px;
cursor: pointer;
width: 35px;
box-sizing: border-box;
}
.save-btn {
background-color: #28a745;
color: white;
}
.delete-btn {
background-color: #dc3545;
color: white;
}
.photos-btn {
background-color: #007bff;
color: white;
}
.parts-btn {
background-color: #ffc107;
color: white;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group textarea {
height: 100px;
resize: vertical;
}
.flash-success {
background-color: #d4edda !important;
transition: background-color 0.3s ease;
}
.quotation-actions {
margin-top: 20px;
}
.modal {
display: none;
position: fixed;
z-index: 1050;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 600px;
border-radius: 8px;
position: relative;
}
.close-btn {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close-btn:hover,
.close-btn:focus {
color: #000;
text-decoration: none;
}
.modal.fade {
z-index: 1060 !important;
}
.modal-backdrop {
z-index: 1055 !important;
}
.overlay.toggle-icon {
z-index: 1000 !important;
}
.alert {
margin-bottom: 15px;
padding: 10px;
border-radius: 4px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
</style>
<title>Gestione Quotations - <?= 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="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Gestione Quotations</h6>
</div>
</div>
</div>
<div class="card-body">
<?php if (isset($_GET['status']) && isset($_GET['message'])): ?>
<div class="alert alert-<?= $_GET['status'] === 'success' ? 'success' : 'danger' ?>">
<?= htmlspecialchars(urldecode($_GET['message'])) ?>
</div>
<?php endif; ?>
<?php if ($editQuotation): ?>
<!-- Modifica Quotation -->
<h6 class="mb-3">Modifica Quotation ID: <?= $editQuotation['id'] ?></h6>
<form id="editForm" method="post">
<input type="hidden" name="action" value="update">
<input type="hidden" name="id" value="<?= $editQuotation['id'] ?>">
<div class="form-group">
<label for="description">Descrizione</label>
<textarea id="description" name="description" class="manual-input required-input" required><?= htmlspecialchars($editQuotation['description']) ?></textarea>
</div>
<div class="form-group">
<label for="customer">Cliente</label>
<input type="text" id="customer" name="customer" class="manual-input required-input" value="<?= htmlspecialchars($editQuotation['customer']) ?>" required>
</div>
<button type="submit" class="btn btn-primary">Salva Modifiche</button>
<a href="quotations.php" class="btn btn-secondary">Torna alla Lista</a>
</form>
<div class="quotation-actions">
<h6 class="mb-3">Azioni</h6>
<button type="button" class="photos-btn action-btn" data-row="0" data-idquotations="<?= $editQuotation['id'] ?>" style="background: #007bff; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-camera"></i></button>
<button type="button" class="parts-btn action-btn" data-iddatadb="" data-idquotations="<?= $editQuotation['id'] ?>" data-row="0">Parti</button>
</div>
<?php else: ?>
<!-- Lista Quotations -->
<div class="mb-3">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createModal">Crea Nuova Quotation</button>
</div>
<h6 class="mb-3">Quotations Esistenti</h6>
<table id="quotationsTable" class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Data Creazione</th>
<th>Descrizione</th>
<th>Cliente</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($quotations as $index => $row): ?>
<tr data-id="<?= $row['id'] ?>">
<td><?= htmlspecialchars($row['id']) ?></td>
<td><?= htmlspecialchars($row['creation_date']) ?></td>
<td>
<textarea name="description" class="cell-input manual-input form-control"><?= htmlspecialchars($row['description']) ?></textarea>
</td>
<td>
<input type="text" name="customer" class="cell-input manual-input form-control" value="<?= htmlspecialchars($row['customer']) ?>">
</td>
<td>
<button type="button" class="save-btn action-btn edit-btn" data-id="<?= $row['id'] ?>" title="Salva Modifiche"><i class="fas fa-save"></i></button>
<button type="button" class="delete-btn action-btn" data-id="<?= $row['id'] ?>" title="Cancella" data-bs-toggle="modal" data-bs-target="#deleteModal"><i class="fas fa-trash"></i></button>
<button type="button" class="photos-btn action-btn" data-entity-type="quotation" data-idquotations="<?= $row['id'] ?>" data-row="<?= $index ?>" title="Photos"><i class="fas fa-camera"></i></button>
<button type="button" class="parts-btn action-btn" data-entity-type="quotation" data-idquotations="<?= $row['id'] ?>" data-row="<?= $index ?>" title="Parts"><i class="fas fa-puzzle-piece"></i></button>
<a href="quotations.php?edit_id=<?= $row['id'] ?>" class="btn btn-secondary action-btn" title="Modifica Dettagliata"><i class="fas fa-edit"></i></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Modal per conferma creazione nuova quotation -->
<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createModalLabel">Conferma Creazione Nuova Quotation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Vuoi creare una nuova quotation?</p>
<form id="createModalForm" method="post">
<input type="hidden" name="action" value="create">
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-primary" id="confirmCreate">Conferma</button>
</div>
</div>
</div>
</div>
<!-- Modal per conferma cancellazione -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Conferma Cancellazione</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Sicuro di voler cancellare questa quotation?</p>
<form id="deleteForm" method="post">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" id="deleteQuotationId">
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-danger" id="confirmDelete">Conferma</button>
</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>
<div id="partsModalContainer"></div>
<div id="annotationsModalContainer"></div>
<?php include 'photos_functions.php'; ?>
<?php include('jsinclude.php'); ?>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script src="photos.js"></script>
<script src="annotationsModal.js"></script>
<script src="partsTable.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Mostra messaggi di stato se presenti
const urlParams = new URLSearchParams(window.location.search);
const status = urlParams.get('status');
const message = urlParams.get('message');
if (status && message) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${status === 'success' ? 'success' : 'danger'} temp-alert`;
alertDiv.textContent = decodeURIComponent(message);
document.querySelector('.card-body').prepend(alertDiv);
setTimeout(() => {
alertDiv.remove();
}, 5000);
}
$(document).on('click', '.parts-btn', function() {
const idquotations = $(this).data('idquotations');
$.ajax({
url: 'modal_partsTable.php',
method: 'GET',
data: {
idquotations: idquotations
},
success: function(response) {
$('#partsModalContainer').html(response);
const modalElement = document.getElementById('partsModal');
if (!modalElement) return;
$("#trfHeader").text(`Quotation #${idquotations}`);
$("#partsModal").data("idquotations", idquotations);
let modal = bootstrap.Modal.getInstance(modalElement) || new bootstrap.Modal(modalElement, {
backdrop: true
});
modal.show();
if (typeof window.loadParts === 'function') window.loadParts(null, idquotations);
},
error: function(xhr, status, error) {
alert('Errore nel caricamento del modale: ' + error);
}
});
});
$(document).on('hidden.bs.modal', '#partsModal', function() {
$('#partsModalContainer').empty();
$('.modal-backdrop').remove();
$('body').removeClass('modal-open').css('padding-right', '');
});
$(document).on('hidden.bs.modal', '#annotationsModal', function() {
$('#annotationsModalContainer').empty();
$('.modal-backdrop').remove();
$('body').removeClass('modal-open').css('padding-right', '');
});
// Inizializza DataTables se non siamo in modalità modifica
if (!document.querySelector('#editForm')) {
$('#quotationsTable').DataTable({
paging: true,
searching: true,
ordering: true,
info: true,
autoWidth: false,
responsive: true
});
}
// Quando il modale di creazione si apre, nascondi l'overlay
$('#createModal').on('show.bs.modal', function() {
$('.overlay.toggle-icon').css('display', 'none');
});
// Quando il modale si chiude, ripristina l'overlay
$('#createModal').on('hide.bs.modal', function() {
$('.overlay.toggle-icon').css('display', '');
});
// Gestione conferma creazione nel modal
document.getElementById('confirmCreate').addEventListener('click', function() {
const createModal = bootstrap.Modal.getInstance(document.getElementById('createModal'));
createModal.hide();
const form = document.getElementById('createModalForm');
const formData = new FormData(form);
fetch('quotations.php', {
method: 'POST',
body: formData
}).then(response => {
if (!response.ok) {
throw new Error('Errore HTTP: ' + response.status);
}
return response.text();
}).then(() => {
window.location.href = 'quotations.php?status=success&message=' + encodeURIComponent('Quotation creata con successo');
}).catch(error => {
console.error('Errore durante la creazione della quotation:', error);
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger temp-alert';
alertDiv.textContent = 'Errore durante la creazione della quotation: ' + error.message;
document.querySelector('.card-body').prepend(alertDiv);
setTimeout(() => {
alertDiv.remove();
}, 5000);
});
});
// Gestione modifica inline e save nella lista
document.querySelectorAll('.edit-btn').forEach(btn => {
btn.addEventListener('click', function() {
const row = this.closest('tr');
const id = row.dataset.id;
const description = row.querySelector('textarea[name="description"]').value;
const customer = row.querySelector('input[name="customer"]').value;
const formData = new FormData();
formData.append('action', 'update');
formData.append('id', id);
formData.append('description', description);
formData.append('customer', customer);
fetch('quotations.php', {
method: 'POST',
body: formData
}).then(response => {
if (response.ok) {
row.classList.add('flash-success');
setTimeout(() => row.classList.remove('flash-success'), 500);
alert('Quotation modificata con successo!');
} else {
alert('Errore durante la modifica.');
}
}).catch(error => {
console.error('Errore durante la modifica della quotation:', error);
alert('Errore durante la modifica: ' + error.message);
});
});
});
// Gestione apertura modal di cancellazione
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', function() {
const id = this.dataset.id;
document.getElementById('deleteQuotationId').value = id;
});
});
// Gestione conferma cancellazione nel modal
document.getElementById('confirmDelete').addEventListener('click', function() {
document.getElementById('deleteForm').submit();
});
// I bottoni photos e parts usano gli script esistenti (photos.js, parts.js), passando data-idquotations
});
</script>
<!-- Modale per le foto in quotations.php -->
</body>
</html>
@@ -1,11 +0,0 @@
{
"@odata.context": "https:\/\/93.43.5.102\/limsapi\/api\/odata\/$metadata#Rapporto\/$entity",
"IdRapporto": 515081,
"DataUltimaModifica": "2025-05-14T18:11:21.02+02:00",
"DaLeggere": false,
"CodiceRapporto": "2523026",
"Data": "2025-05-14T14:05:30+02:00",
"Versione": 0,
"DataStampa": "2025-05-14T14:11:00+02:00",
"Firmato": true
}
File diff suppressed because it is too large Load Diff
-62
View File
@@ -1,62 +0,0 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$data = json_decode(file_get_contents("php://input"), true);
if (!isset($data['template_id'], $data['excel_column'], $data['mysql_column'], $data['tablename'])) {
echo json_encode(["success" => false, "message" => "Missing required fields"]);
exit;
}
// Rimuove l'associazione
$stmtDelete = $pdo->prepare("
DELETE FROM excel_column_mappings
WHERE template_id = ? AND excel_column = ? AND mysql_column = ? AND tablename = ?
");
$result = $stmtDelete->execute([
$data['template_id'],
$data['excel_column'],
$data['mysql_column'],
$data['tablename']
]);
if (!$result) {
echo json_encode(["success" => false, "message" => "Failed to delete mapping"]);
exit;
}
// Dopo la rimozione, aggiorna la lista delle colonne disponibili
$stmtColumns = $pdo->prepare("SHOW COLUMNS FROM " . $data['tablename']);
$stmtColumns->execute();
$all_mysql_columns = array_column($stmtColumns->fetchAll(PDO::FETCH_ASSOC), 'Field');
$stmtHeader = $pdo->prepare("SELECT headerexcel FROM excel_column_mappings WHERE template_id = ? LIMIT 1");
$stmtHeader->execute([$data['template_id']]);
$headerRow = $stmtHeader->fetch(PDO::FETCH_ASSOC);
$xls_headers = isset($headerRow['headerexcel']) ? explode(",", $headerRow['headerexcel']) : [];
// Ricalcola le colonne non associate
$stmtMappings = $pdo->prepare("SELECT excel_column, mysql_column FROM excel_column_mappings WHERE template_id = ?");
$stmtMappings->execute([$data['template_id']]);
$existingMappings = $stmtMappings->fetchAll(PDO::FETCH_ASSOC);
$mapped_xls_columns = array_column($existingMappings, 'excel_column');
$mapped_mysql_columns = array_column($existingMappings, 'mysql_column');
$remaining_xls_columns = array_diff($xls_headers, $mapped_xls_columns);
$remaining_mysql_columns = array_diff($all_mysql_columns, $mapped_mysql_columns);
echo json_encode([
"success" => true,
"remaining_xls_columns" => array_values($remaining_xls_columns),
"remaining_mysql_columns" => array_values($remaining_mysql_columns)
]);
exit;
-63
View File
@@ -1,63 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$iddatadb = $data['iddatadb'] ?? null;
$parts = $data['parts'] ?? [];
if (!$iddatadb || empty($parts)) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit;
}
try {
$pdo->beginTransaction();
// Elimina tutte le parti esistenti per l'iddatadb per evitare conflitti di unicità
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE iddatadb = :iddatadb");
$stmt->execute([':iddatadb' => $iddatadb]);
// Prepara l'inserimento delle nuove parti
$stmt = $pdo->prepare("
INSERT INTO identification_parts
(iddatadb, part_number, part_description, mix, created_at, updated_at)
VALUES (:iddatadb, :part_number, :part_description, :mix, NOW(), NOW())
");
$part_ids = [];
foreach ($parts as $part) {
$partNumber = $part['part_number'] ?? null;
$partDescription = $part['part_description'] ?? '';
$mix = $part['mix'] ?? 'N';
if (!$partNumber || !$partDescription) {
throw new PDOException("Numero parte o descrizione mancante per parte: " . json_encode($part));
}
$stmt->execute([
':iddatadb' => $iddatadb,
':part_number' => $partNumber,
':part_description' => $partDescription,
':mix' => $mix
]);
$part_ids[] = $pdo->lastInsertId();
}
$pdo->commit();
echo json_encode([
'success' => true,
'part_ids' => $part_ids,
'message' => 'Parti rinumerate con successo'
]);
} catch (PDOException $e) {
$pdo->rollBack();
echo json_encode([
'success' => false,
'message' => 'Errore nel salvataggio: ' . $e->getMessage()
]);
}
@@ -1,63 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$idquotations = $data['idquotations'] ?? null;
$parts = $data['parts'] ?? [];
if (!$idquotations || empty($parts)) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit;
}
try {
$pdo->beginTransaction();
// Elimina tutte le parti esistenti per idquotations
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE idquotations = :idquotations");
$stmt->execute([':idquotations' => $idquotations]);
// Prepara l'inserimento delle nuove parti
$stmt = $pdo->prepare("
INSERT INTO identification_parts
(idquotations, part_number, part_description, mix, created_at, updated_at)
VALUES (:idquotations, :part_number, :part_description, :mix, NOW(), NOW())
");
$part_ids = [];
foreach ($parts as $part) {
$partNumber = $part['part_number'] ?? null;
$partDescription = $part['part_description'] ?? '';
$mix = $part['mix'] ?? 'N';
if (!$partNumber || !$partDescription) {
throw new PDOException("Numero parte o descrizione mancante per parte: " . json_encode($part));
}
$stmt->execute([
':idquotations' => $idquotations,
':part_number' => $partNumber,
':part_description' => $partDescription,
':mix' => $mix
]);
$part_ids[] = $pdo->lastInsertId();
}
$pdo->commit();
echo json_encode([
'success' => true,
'part_ids' => $part_ids,
'message' => 'Parti rinumerate con successo'
]);
} catch (PDOException $e) {
$pdo->rollBack();
echo json_encode([
'success' => false,
'message' => 'Errore nel salvataggio: ' . $e->getMessage()
]);
}
-76
View File
@@ -1,76 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
error_reporting(E_ALL);
ini_set('display_errors', 1);
$file = $_FILES['file'] ?? null;
$filename = $_POST['filename'] ?? null;
$iddatadb = $_POST['iddatadb'] ?? null;
if (!$file || !$filename || !$iddatadb) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit;
}
if (!preg_match('/^[a-zA-Z0-9_-]+\.(png|jpg|jpeg)$/', $filename)) {
echo json_encode(['success' => false, 'message' => 'Nome file non valido']);
exit;
}
if (!is_numeric($iddatadb)) {
echo json_encode(['success' => false, 'message' => 'ID non valido']);
exit;
}
$allowedTypes = ['image/png', 'image/jpeg'];
if (!in_array($file['type'], $allowedTypes)) {
echo json_encode(['success' => false, 'message' => 'Formato file non supportato']);
exit;
}
try {
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$stmt = $pdo->prepare("SELECT iddatadb FROM datadb WHERE iddatadb = :iddatadb");
$stmt->execute([':iddatadb' => $iddatadb]);
if (!$stmt->fetch()) {
echo json_encode(['success' => false, 'message' => 'iddatadb non valido']);
exit;
}
$dirPath = '../photostrf/annotated';
if (!file_exists($dirPath)) {
mkdir($dirPath, 0755, true);
}
$filePath = $dirPath . '/' . $filename;
if (file_exists($filePath)) {
echo json_encode(['success' => false, 'message' => 'File già esistente']);
exit;
}
if (!move_uploaded_file($file['tmp_name'], $filePath)) {
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio del file']);
exit;
}
$stmt = $pdo->prepare("
INSERT INTO datadb_photos (iddatadb, file_path, file_name, uploaded_at, uploaded_by)
VALUES (:iddatadb, :file_path, :file_name, NOW(), :uploaded_by)
");
$stmt->execute([
':iddatadb' => $iddatadb,
':file_path' => $filePath,
':file_name' => $filename,
':uploaded_by' => $iduserlogin
]);
echo json_encode([
'success' => true,
'file_path' => $filePath,
'message' => 'Foto salvata con successo e registrata nel DB'
]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
}
@@ -1,76 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
error_reporting(E_ALL);
ini_set('display_errors', 1);
$file = $_FILES['file'] ?? null;
$filename = $_POST['filename'] ?? null;
$idquotations = $_POST['idquotations'] ?? null;
if (!$file || !$filename || !$idquotations || !isset($iduserlogin)) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti o utente non autenticato']);
exit;
}
if (!preg_match('/^[a-zA-Z0-9_-]+\.(png|jpg|jpeg)$/', $filename)) {
echo json_encode(['success' => false, 'message' => 'Nome file non valido']);
exit;
}
if (!is_numeric($idquotations)) {
echo json_encode(['success' => false, 'message' => 'ID non valido']);
exit;
}
$allowedTypes = ['image/png', 'image/jpeg'];
if (!in_array($file['type'], $allowedTypes)) {
echo json_encode(['success' => false, 'message' => 'Formato file non supportato']);
exit;
}
try {
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$stmt = $pdo->prepare("SELECT id FROM quotations WHERE id = :idquotations");
$stmt->execute([':idquotations' => $idquotations]);
if (!$stmt->fetch()) {
echo json_encode(['success' => false, 'message' => 'idquotations non valido']);
exit;
}
$dirPath = '../photostrf/annotated';
if (!file_exists($dirPath)) {
mkdir($dirPath, 0755, true);
}
$filePath = $dirPath . '/' . $filename;
if (file_exists($filePath)) {
echo json_encode(['success' => false, 'message' => 'File già esistente']);
exit;
}
if (!move_uploaded_file($file['tmp_name'], $filePath)) {
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio del file']);
exit;
}
$stmt = $pdo->prepare("
INSERT INTO datadb_photos (idquotations, file_path, file_name, uploaded_at, uploaded_by)
VALUES (:idquotations, :file_path, :file_name, NOW(), :uploaded_by)
");
$stmt->execute([
':idquotations' => $idquotations,
':file_path' => $filePath,
':file_name' => $filename,
':uploaded_by' => $iduserlogin
]);
echo json_encode([
'success' => true,
'file_path' => $filePath,
'message' => 'Foto salvata con successo e registrata nel DB'
]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
}
-412
View File
@@ -1,412 +0,0 @@
<?php include('include/headscript.php');
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
die("Invalid template ID");
}
$id = intval($_GET['id']);
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table, sample_xlsx, idclient, clientname, idschema, schemaname, schemajson FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
die("Template not found");
}
$clientName = $template['clientname'] ?: '';
$schemaName = $template['schemaname'] ?: '';
$schemajson = $template['schemajson'] ? json_decode($template['schemajson'], true) : [];
$isSchemajsonEmpty = empty(trim($template['schemajson']));
// Recupera i campi dalla tabella template_mapping
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label FROM template_mapping WHERE template_id = ?");
$stmt->execute([$id]);
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!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'); ?>
<title>Configure Template <?= htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'); ?></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Configure Template: <span id="templateName"><?php echo htmlspecialchars($template['name']); ?></span></h6>
<p>
Client: <span id="clientName"><?php echo htmlspecialchars($clientName); ?></span> |
Schema: <span id="schemaName"><?php echo htmlspecialchars($schemaName); ?></span> |
Header Row: <span id="headerRow"><?php echo $template['header_row']; ?></span> |
Start Column: <span id="startColumn"><?php echo htmlspecialchars($template['start_column']); ?></span>
</p>
</div>
</div>
</div>
<div class="card-body">
<div class="mb-4">
<label class="form-label">Upload XLS Example:</label>
<input type="file" id="xlsUpload" class="form-control">
<small id="uploadStatus">
<?php if (!empty($template['sample_xlsx'])): ?>
Current file: <a href="xlstemplates/<?php echo htmlspecialchars($template['sample_xlsx']); ?>" target="_blank"><?php echo htmlspecialchars($template['sample_xlsx']); ?></a>
<?php else: ?>
No file uploaded yet.
<?php endif; ?>
</small>
</div>
<div class="row">
<div class="col-12">
<div class="d-flex align-items-center mb-2">
<h5>Schema Fields Configuration</h5>
<button id="updateSchemaButton" class="btn btn-primary ms-2" data-empty="<?php echo $isSchemajsonEmpty ? 'true' : 'false'; ?>">
<?php echo $isSchemajsonEmpty ? 'Load Schema Details' : 'Update Schema Details'; ?>
</button>
</div>
<table id="schemaFieldsTable" class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>ID</th>
<th>Type</th>
<th>Mapping</th>
<th>Default Value</th>
<th>Action</th>
</tr>
</thead>
<tbody id="schemaFieldsBody">
<?php foreach ($mappings as $mapping): ?>
<tr>
<td><?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?></td>
<td><?php echo htmlspecialchars($mapping['field_id'] ?? 'N/A'); ?></td>
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
<td>
<select class="form-select mapping-select" data-id="<?php echo $mapping['id']; ?>" data-field-id="<?php echo $mapping['field_id']; ?>">
<option value="">Select Option</option>
<option value="xls" <?php echo !$mapping['is_manual'] && $mapping['excel_column'] ? 'selected' : ''; ?>>Map to XLS Column</option>
<option value="manual" <?php echo $mapping['is_manual'] ? 'selected' : ''; ?>>Manual Entry</option>
</select>
<select class="form-select xls-columns" style="display:<?php echo !$mapping['is_manual'] && $mapping['excel_column'] ? 'block' : 'none'; ?>" data-id="<?php echo $mapping['id']; ?>"></select>
</td>
<td>
<input type="text" class="form-control manual-default" placeholder="Default value" value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>" style="display:<?php echo $mapping['is_manual'] ? 'block' : 'none'; ?>" data-field-id="<?php echo $mapping['field_id']; ?>">
</td>
<td>
<?php if ($mapping['excel_column']): ?>
<button class="btn btn-danger btn-sm remove-association" data-id="<?php echo $mapping['id']; ?>">Remove</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="mt-4">
<h5>Current Configurations</h5>
<ul id="configurationsList" class="list-group border p-3"></ul>
</div>
<div class="mt-4 text-end">
<a href="templates_dashboard.php" class="btn btn-primary"> Back to Template Dashboard</a>
</div>
</div>
</div>
</div>
</div>
<div class="overlay toggle-icon"></div>
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script>
let availableXlsColumns = [];
document.getElementById('xlsUpload').addEventListener('change', function(event) {
let file = event.target.files[0];
if (!file) return;
let formData = new FormData();
formData.append("xls_file", file);
formData.append("template_id", <?php echo $id; ?>);
let statusText = document.getElementById('uploadStatus');
statusText.innerText = "Uploading...";
fetch('upload_xls_example.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
statusText.innerText = "❌ Upload failed: " + data.message;
return;
}
statusText.innerHTML = `✅ File uploaded: <a href="xlstemplates/${data.filename}" target="_blank">${data.filename}</a>`;
processXLSX(file);
})
.catch(error => {
statusText.innerText = "❌ Upload failed. Check console.";
console.error(error);
});
});
function processXLSX(file) {
let reader = new FileReader();
reader.onload = function(e) {
let data = new Uint8Array(e.target.result);
let workbook = XLSX.read(data, {
type: 'array'
});
let sheet = workbook.Sheets[workbook.SheetNames[0]];
let rowIndex = parseInt(document.getElementById('headerRow').textContent) || 1;
let startColumn = parseInt(document.getElementById('startColumn').textContent.charCodeAt(0) - 64) || 1;
let sheetData = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: "",
raw: false,
range: 0
});
if (!sheetData[rowIndex - 1]) {
document.getElementById('schemaFieldsBody').querySelectorAll('select.xls-columns').forEach(select => {
select.innerHTML = '<option value="">No headers found</option>';
});
return;
}
let headers = sheetData[rowIndex - 1].slice(startColumn - 1).filter(header => header !== undefined && header.trim() !== "");
availableXlsColumns = [...headers]; // Memorizza tutte le colonne disponibili
updateXlsDropdowns();
};
reader.readAsArrayBuffer(file);
}
function updateXlsDropdowns() {
let usedColumns = Array.from(document.querySelectorAll('select.xls-columns'))
.filter(select => select.style.display === 'block' && select.value)
.map(select => select.value);
document.querySelectorAll('select.xls-columns').forEach(select => {
let currentValue = select.value || select.dataset.currentXls || '';
let options = availableXlsColumns
.filter(col => !usedColumns.includes(col) || col === currentValue)
.map(col => `<option value="${col}" ${col === currentValue ? 'selected' : ''}>${col}</option>`)
.join('');
select.innerHTML = '<option value="">Select XLS Column</option>' + options;
select.dataset.currentXls = currentValue;
});
}
document.addEventListener("DOMContentLoaded", function() {
let templateId = <?php echo $id; ?>;
let schemaId = <?php echo json_encode($template['idschema'] ?? 0); ?>;
let isSchemajsonEmpty = <?php echo json_encode($isSchemajsonEmpty); ?>;
async function loadClientAndSchemaNames() {
if (<?php echo json_encode($template['idclient'] ?? 0); ?> > 0) {
let response = await fetch(`get_clienti.php?id=<?php echo $template['idclient']; ?>`);
let data = await response.json();
if (response.ok && data.value?.length) document.getElementById('clientName').textContent = data.value[0].Nominativo || 'N/A';
}
if (schemaId > 0) {
let response = await fetch(`get_schemi.php?id=${schemaId}`);
let data = await response.json();
if (response.ok && data.value?.length) document.getElementById('schemaName').textContent = data.value[0].Nome || 'N/A';
}
}
loadClientAndSchemaNames();
async function updateSchemaDetails() {
if (!schemaId) {
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-warning">No schema associated.</td></tr>';
return;
}
let updateSchemaButton = document.getElementById('updateSchemaButton');
updateSchemaButton.disabled = true;
updateSchemaButton.textContent = 'Loading...';
try {
let response = await fetch(`get_schema_details.php?id=${schemaId}`);
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
let data = JSON.parse(await response.text());
if (!data.SchemiCustomFieldsDettagli) throw new Error('Missing "SchemiCustomFieldsDettagli"');
await saveSchemaJson(templateId, JSON.stringify(data));
alert('Schema updated successfully. Refresh the page to see changes.');
} catch (error) {
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-danger">Error: ' + error.message + '</td></tr>';
} finally {
updateSchemaButton.disabled = false;
updateSchemaButton.textContent = 'Update Schema Details';
}
}
async function saveSchemaJson(templateId, schemaJson) {
let response = await fetch('update_schemajson.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
schemajson: schemaJson
})
});
let data = await response.json();
if (!data.success) throw new Error(data.message || 'Failed to save schema JSON');
}
document.getElementById('updateSchemaButton').addEventListener('click', updateSchemaDetails);
function loadConfigurations() {
fetch('load_mappings.php?template_id=' + templateId)
.then(response => response.json())
.then(data => {
if (!data.success) return;
let configurationsList = document.getElementById('configurationsList');
configurationsList.innerHTML = '';
data.mappings.forEach(mapping => {
let li = document.createElement('li');
li.className = 'list-group-item d-flex justify-content-between align-items-center';
li.innerHTML = `${mapping.field_label} (${mapping.field_id}) → ${mapping.excel_column || 'Manual: ' + (mapping.manual_default || 'N/A')} <button class="btn btn-danger btn-sm removeMapping" data-id="${mapping.id}">X</button>`;
configurationsList.appendChild(li);
});
})
.catch(error => console.error("❌ Error fetching mappings:", error));
}
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
if (event.target.classList.contains('mapping-select')) {
let tr = event.target.closest('tr');
let mappingId = event.target.getAttribute('data-id');
let xlsSelect = tr.querySelector('.xls-columns');
let manualInput = tr.querySelector('.manual-default');
let removeBtn = tr.querySelector('.remove-association');
if (event.target.value === 'xls') {
xlsSelect.style.display = 'block';
manualInput.style.display = 'none';
manualInput.value = '';
removeBtn.style.display = xlsSelect.value ? 'inline-block' : 'none';
} else if (event.target.value === 'manual') {
xlsSelect.style.display = 'none';
manualInput.style.display = 'block';
removeBtn.style.display = 'none';
} else {
xlsSelect.style.display = 'none';
manualInput.style.display = 'none';
manualInput.value = '';
removeBtn.style.display = 'none';
}
saveMapping(mappingId, event.target.value, manualInput.value, xlsSelect.value);
updateXlsDropdowns();
}
});
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
if (event.target.classList.contains('xls-columns')) {
let tr = event.target.closest('tr');
let mappingId = event.target.getAttribute('data-id');
let manualInput = tr.querySelector('.manual-default');
let removeBtn = tr.querySelector('.remove-association');
removeBtn.style.display = event.target.value ? 'inline-block' : 'none';
saveMapping(mappingId, 'xls', manualInput.value, event.target.value);
updateXlsDropdowns();
}
});
document.getElementById('schemaFieldsBody').addEventListener('input', function(event) {
if (event.target.classList.contains('manual-default')) {
let tr = event.target.closest('tr');
let mappingId = tr.querySelector('.mapping-select').getAttribute('data-id');
let xlsSelect = tr.querySelector('.xls-columns');
saveMapping(mappingId, 'manual', event.target.value, xlsSelect.value);
}
});
document.getElementById('schemaFieldsBody').addEventListener('click', function(event) {
if (event.target.classList.contains('remove-association')) {
let mappingId = event.target.getAttribute('data-id');
let tr = event.target.closest('tr');
let xlsSelect = tr.querySelector('.xls-columns');
xlsSelect.value = '';
xlsSelect.style.display = 'none';
tr.querySelector('.mapping-select').value = '';
event.target.style.display = 'none';
saveMapping(mappingId, '', '', null);
updateXlsDropdowns();
}
});
function saveMapping(mappingId, mappingType, defaultValue, excelColumn) {
fetch('save_mapping_json.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: mappingId,
mapping_type: mappingType,
excel_column: mappingType === 'xls' ? excelColumn : null,
manual_default: mappingType === 'manual' ? defaultValue : null,
tablename: "<?php echo $template['target_table']; ?>"
})
}).then(response => response.json())
.then(data => {
if (data.success) loadConfigurations();
else console.error("❌ Error saving mapping:", data.message);
})
.catch(error => console.error("❌ Fetch error:", error));
}
document.getElementById('configurationsList').addEventListener('click', function(event) {
if (event.target.classList.contains('removeMapping')) {
let mappingId = event.target.getAttribute('data-id');
fetch('remove_mapping.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: mappingId
})
}).then(response => response.json())
.then(data => {
if (data.success) {
loadConfigurations();
updateXlsDropdowns(); // Aggiorna la tendina dopo la rimozione
} else console.error("❌ Error removing mapping:", data.message);
})
.catch(error => console.error("❌ Fetch error:", error));
}
});
loadConfigurations();
});
</script>
</body>
</html>
-47
View File
@@ -1,47 +0,0 @@
<?php
include('include/headscript.php');
header('Content-Type: application/json');
$response = ['success' => false, 'message' => ''];
try {
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['iddatadb'])) {
throw new Exception('Richiesta non valida');
}
$iddatadb = intval($_POST['iddatadb']);
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Campi non modificabili
$excludeFields = ['importdate', 'status', 'user_id', 'filename_import', 'importreferencecode', 'limscode'];
// Prepara i dati da aggiornare
$updates = [];
$values = [];
foreach ($_POST as $key => $value) {
if ($key !== 'iddatadb' && !in_array($key, $excludeFields)) {
$updates[] = "$key = ?";
$values[] = htmlspecialchars($value);
}
}
$values[] = $iddatadb;
if (empty($updates)) {
throw new Exception('Nessun dato da aggiornare');
}
$sql = "UPDATE datadb SET " . implode(', ', $updates) . " WHERE iddatadb = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute($values);
$response['success'] = true;
$response['message'] = 'Riga aggiornata con successo';
} catch (Exception $e) {
$response['message'] = $e->getMessage();
error_log("Errore in save_edited_row.php: " . $e->getMessage());
}
echo json_encode($response);
exit;
-92
View File
@@ -1,92 +0,0 @@
<?php
include('include/headscript.php');
header('Content-Type: application/json');
$response = ['success' => false, 'message' => ''];
try {
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['iddatadb'])) {
throw new Exception('Richiesta non valida');
}
$iddatadb = intval($_POST['iddatadb']);
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$data = $_POST;
$details = [];
// 1. Estrarre i dettagli da POST
foreach ($data as $key => $value) {
if (preg_match('/^details(\d+)field_value$/', $key, $matches)) {
$id = $matches[1];
$details[$id] = $value;
}
}
// 2. Recupera i valori esistenti da import_data_details
$stmt = $pdo->prepare("SELECT mapping_id, field_value FROM import_data_details WHERE id = ?");
$stmt->execute([$iddatadb]);
$currentValues = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$currentValues[$row['mapping_id']] = $row['field_value'];
}
// 3. Confronta i valori nuovi con quelli esistenti
$changed = [];
foreach ($details as $id => $newValue) {
$oldValue = $currentValues[$id] ?? null;
if ($oldValue !== $newValue) {
$changed[$id] = [
'old' => $oldValue,
'new' => $newValue
];
}
}
// 4. Aggiorna i dettagli se ci sono modifiche
if (!empty($changed)) {
$updateStmt = $pdo->prepare("
UPDATE import_data_details
SET field_value = :newValue
WHERE id = :iddatadb AND mapping_id = :mappingId
");
foreach ($changed as $mappingId => $values) {
$updateStmt->execute([
':newValue' => $values['new'],
':iddatadb' => $iddatadb,
':mappingId' => $mappingId
]);
}
}
// 5. Aggiorna idclient in datadb
if (isset($idclient)) {
$updateStmt = $pdo->prepare("
UPDATE datadb
SET idclient = :idclient
WHERE iddatadb = :iddatadb
");
$updateStmt->execute([
':idclient' => $idclient,
':iddatadb' => $iddatadb
]);
$response['message'] = !empty($changed) ? "Updated details and idclient successfully" : "Updated idclient successfully";
} else {
$response['message'] = !empty($changed) ? "Updated details successfully" : "No changes found";
}
$response['success'] = true;
$response['changed'] = $changed; // Debug / optional
} catch (Exception $e) {
$response['success'] = false;
$response['message'] = $e->getMessage();
error_log("Errore in save_edited_row.php: " . $e->getMessage());
}
echo json_encode($response);
exit;
-39
View File
@@ -1,39 +0,0 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$data = json_decode(file_get_contents("php://input"), true);
if (!$data || !isset($data['id'])) {
echo json_encode(["success" => false, "message" => "Invalid or missing ID"]);
exit;
}
$mappingId = $data['id'];
$mappingType = $data['mapping_type'] ?? '';
$excelColumn = $data['excel_column'] ?? null;
$manualDefault = $data['manual_default'] ?? null;
$tablename = $data['tablename'] ?? '';
try {
$stmt = $pdo->prepare("UPDATE template_mapping SET is_manual = ?, excel_column = ?, manual_default = ? WHERE id = ?");
$isManual = ($mappingType === 'manual') ? 1 : 0;
$result = $stmt->execute([$isManual, $excelColumn, $manualDefault, $mappingId]);
if (!$result) {
echo json_encode(["success" => false, "message" => "Database update failed"]);
exit;
}
echo json_encode(["success" => true, "message" => "Mapping updated successfully", "data" => $data]); // Aggiunto debug
} catch (Exception $e) {
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
}
exit;

Some files were not shown because too many files have changed in this diff Show More