Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a9827e4e81 | |||
| b51936f784 | |||
| 15b6f38e8b | |||
| 12c6cc5f95 | |||
| a0b12463c0 | |||
| 07ddcafd3f | |||
| 7843d4b1fc | |||
| 4eae855e23 | |||
| c709f64a17 | |||
| d5f0690f59 | |||
| 6bbd3fcae9 |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 3) . '/vendor/autoload.php'; // Risale a root/vendor/
|
||||||
|
require_once dirname(__FILE__) . '/../class/VisualLimsApiClient.class.php'; // In root/public/userarea/class/
|
||||||
|
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
// Debug: Log the path where we expect the .env file
|
||||||
|
$envPath = dirname(__DIR__, 3);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Endpoint per recuperare le Matrici
|
||||||
|
$endpoint = 'Matrice';
|
||||||
|
|
||||||
|
// (Opzionale) aggiungi parametri
|
||||||
|
$options = []; // es. ['$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);
|
||||||
|
|
||||||
|
// Debug: Log the API response size
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - API response received, value count: ' . (isset($data['value']) ? count($data['value']) : 0) . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Salva il JSON in locale (opzionale, per debug)
|
||||||
|
file_put_contents(__DIR__ . '/matrici_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
// Svuota la tabella (dump rapido)
|
||||||
|
$pdo->exec("TRUNCATE TABLE {$dbPrefix}matrici");
|
||||||
|
|
||||||
|
// Debug: Log after truncate
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Table truncated: ' . $dbPrefix . 'matrici' . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Prepara l'insert
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO {$dbPrefix}matrici (
|
||||||
|
IdMatrice, NomeMatriceTraduzione, DescrizioneTraduzione, MacroMatrice, NomeMatrice, Descrizione
|
||||||
|
) VALUES (
|
||||||
|
:IdMatrice, :NomeMatriceTraduzione, :DescrizioneTraduzione, :MacroMatrice, :NomeMatrice, :Descrizione
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Inserisci i dati
|
||||||
|
$insertedRows = 0;
|
||||||
|
if (isset($data['value']) && is_array($data['value'])) {
|
||||||
|
foreach ($data['value'] as $item) {
|
||||||
|
$stmt->execute([
|
||||||
|
':IdMatrice' => $item['IdMatrice'],
|
||||||
|
':NomeMatriceTraduzione' => $item['NomeMatriceTraduzione'],
|
||||||
|
':DescrizioneTraduzione' => $item['DescrizioneTraduzione'] ?? null,
|
||||||
|
':MacroMatrice' => $item['MacroMatrice'] ?? null,
|
||||||
|
':NomeMatrice' => $item['NomeMatrice'],
|
||||||
|
':Descrizione' => $item['Descrizione'] ?? null,
|
||||||
|
]);
|
||||||
|
$insertedRows++;
|
||||||
|
// Debug: Log each inserted row
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Inserted row with IdMatrice: ' . $item['IdMatrice'] . PHP_EOL, FILE_APPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log successo
|
||||||
|
file_put_contents(__DIR__ . '/success_log.txt', date('Y-m-d H:i:s') . ' - Aggiornamento completato: ' . $insertedRows . ' record inseriti.' . PHP_EOL, FILE_APPEND);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0); // Esci con successo per cron
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,19 +1,33 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Torna al livello di public per trovare vendor/
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
// Disabilita la visualizzazione degli errori PHP per evitare output HTML
|
// Disable PHP error display
|
||||||
ini_set('display_errors', '0');
|
ini_set('display_errors', '0');
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance();
|
||||||
$data = $api->get("Cliente"); // Recupera i clienti
|
|
||||||
|
|
||||||
// Salva la risposta in un file per debug
|
// Parametri OData
|
||||||
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data));
|
$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";
|
||||||
|
|
||||||
|
// Richiama API
|
||||||
|
$data = $api->get($endpoint);
|
||||||
|
|
||||||
|
// Salva risposta per debug
|
||||||
|
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
echo json_encode($data);
|
echo json_encode($data);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
@@ -80,6 +80,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
<?php include('cssinclude.php'); ?>
|
<?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://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2Lw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
.cell-changed {
|
.cell-changed {
|
||||||
background-color: #fff3b0 !important;
|
background-color: #fff3b0 !important;
|
||||||
@@ -711,7 +712,8 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<?php include 'modal_parts.php'; ?>
|
<div id="partsModalContainer"></div>
|
||||||
|
<div id="annotationsModalContainer"></div>
|
||||||
<?php include 'photos_functions.php'; ?>
|
<?php include 'photos_functions.php'; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -725,8 +727,10 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<script src="photos.js"></script>
|
<script src="photos.js"></script>
|
||||||
<script src="parts.js"></script>
|
<script src="annotationsModal.js"></script>
|
||||||
|
<script src="partsTable.js"></script>
|
||||||
<script src="tracking.js"></script>
|
<script src="tracking.js"></script>
|
||||||
<script src="export_to_lims.js"></script>
|
<script src="export_to_lims.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -1179,6 +1183,93 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
$(document).on('click', '.parts-btn', function() {
|
||||||
|
const iddatadb = $(this).data('iddatadb') || null;
|
||||||
|
const idquotations = $(this).data('idquotations') || null;
|
||||||
|
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";
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: 'modal_partsTable.php',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
iddatadb: iddatadb
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
$('#partsModalContainer').html(response);
|
||||||
|
const modalElement = document.getElementById('partsModal');
|
||||||
|
if (!modalElement) {
|
||||||
|
console.error('Elemento modale non trovato: #partsModal');
|
||||||
|
const errorMsg = $('<div class="alert alert-danger temp-alert" role="alert">Errore: Modale non trovato.</div>');
|
||||||
|
$("body").prepend(errorMsg);
|
||||||
|
setTimeout(() => errorMsg.fadeOut(500, function() {
|
||||||
|
$(this).remove();
|
||||||
|
}), 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#trfHeader").text(`${iddatadb || idquotations} - ${importRef} - ${description}`);
|
||||||
|
$("#partsModal").data("iddatadb", iddatadb).data("idquotations", idquotations);
|
||||||
|
|
||||||
|
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (!modal) {
|
||||||
|
modal = new bootstrap.Modal(modalElement, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true,
|
||||||
|
focus: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
if (typeof window.loadParts === 'function') {
|
||||||
|
window.loadParts(iddatadb, idquotations);
|
||||||
|
} else {
|
||||||
|
console.error('Funzione loadParts non definita. Verifica partsTable.js.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error('Errore nel caricamento del modale:', error);
|
||||||
|
const errorMsg = $('<div class="alert alert-danger temp-alert" role="alert">Errore nel caricamento del modale: ' + error + '</div>');
|
||||||
|
$("body").prepend(errorMsg);
|
||||||
|
setTimeout(() => errorMsg.fadeOut(500, function() {
|
||||||
|
$(this).remove();
|
||||||
|
}), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('hidden.bs.modal', '#partsModal', function() {
|
||||||
|
const modalElement = document.getElementById('partsModal');
|
||||||
|
if (modalElement) {
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (modal) {
|
||||||
|
modal.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('#partsModalContainer').empty();
|
||||||
|
$('.modal-backdrop').remove();
|
||||||
|
$('body').removeClass('modal-open').css('padding-right', '');
|
||||||
|
$('.overlay.toggle-icon').css('display', 'none');
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('hidden.bs.modal', '#annotationsModal', function() {
|
||||||
|
const modalElement = document.getElementById('annotationsModal');
|
||||||
|
if (modalElement) {
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (modal) {
|
||||||
|
modal.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('#annotationsModalContainer').empty();
|
||||||
|
$('.modal-backdrop').remove();
|
||||||
|
$('body').removeClass('modal-open').css('padding-right', '');
|
||||||
|
$('.overlay.toggle-icon').css('display', 'none');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<!-- Modale di conferma per l'esportazione -->
|
<!-- Modale di conferma per l'esportazione -->
|
||||||
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
|
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/../class/db-functions.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
|
// Inizializza la sessione
|
||||||
|
if (session_status() == PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imposta variabili di sessione di default per evitare errori
|
||||||
|
$_SESSION['iduserlogin'] = null; // Nessun utente loggato
|
||||||
|
$_SESSION['nameuser'] = 'Ospite';
|
||||||
|
$_SESSION['surnameuser'] = '';
|
||||||
|
$_SESSION['emailuser'] = '';
|
||||||
|
$_SESSION['photouser'] = '';
|
||||||
|
$photouser = $_SESSION['photouser'];
|
||||||
|
$photousername = '';
|
||||||
|
|
||||||
|
// Include file di lingua, se necessario
|
||||||
|
require_once(__DIR__ . '/../../languages/en/general.php');
|
||||||
@@ -14,9 +14,9 @@ if (!$iddatadb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("SELECT id, iddatadb, part_number, part_description FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC");
|
$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]);
|
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||||
$parts = $stmt->fetchAll();
|
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'parts' => $parts]);
|
echo json_encode(['success' => true, 'parts' => $parts]);
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
|
|||||||
@@ -0,0 +1,219 @@
|
|||||||
|
<!-- 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: 80% !important; width: 80% !important;">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="annotationsModalLabel">Annotazioni per TRF: <span id="trfHeaderAnnotations"></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;">
|
||||||
|
<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"></ul>
|
||||||
|
</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="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: 400px;">
|
||||||
|
<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;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- Modal modificato con pulsante per riconoscimento vocale e download -->
|
<!-- 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 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-dialog modal-xl" style="max-width: 80% !important; width: 80% !important;">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -9,10 +9,12 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
<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>
|
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center; min-width: 0;">
|
||||||
<input type="checkbox" id="showMixParts" name="showMixParts" style="margin-right: 5px;">
|
<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>
|
<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-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-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>
|
||||||
@@ -45,13 +47,14 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h6>Foto del Campione</h6>
|
<h6>Foto del Campione</h6>
|
||||||
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
<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>
|
<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 id="photoSelectorContainer" style="display: none;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="position: relative; width: 100%; min-height: 400px;">
|
<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;">
|
<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="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> <!-- Nuovo canvas per Fabric.js -->
|
<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 id="markerContainer"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -249,4 +252,59 @@
|
|||||||
border: 2px solid #000;
|
border: 2px solid #000;
|
||||||
margin: 2px;
|
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>
|
</style>
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
<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">
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#partsModal {
|
||||||
|
z-index: 1060 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsModal .modal-backdrop {
|
||||||
|
z-index: 1055 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsModal .modal-content {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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,
|
||||||
|
#partsTable select {
|
||||||
|
height: 24px;
|
||||||
|
padding: 0.1rem 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable button {
|
||||||
|
padding: 0.1rem 0.3rem;
|
||||||
|
margin: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable i {
|
||||||
|
font-size: 0.6rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#global-matrice,
|
||||||
|
.part-matrice,
|
||||||
|
#macro-matrice-filter {
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default #global-matrice {
|
||||||
|
width: 350px !important;
|
||||||
|
min-width: 350px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default #macro-matrice-filter {
|
||||||
|
width: 200px !important;
|
||||||
|
min-width: 200px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .part-matrice {
|
||||||
|
width: 150px !important;
|
||||||
|
min-width: 150px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single {
|
||||||
|
height: 24px !important;
|
||||||
|
padding: 0.1rem 0.3rem !important;
|
||||||
|
font-size: 0.8rem !important;
|
||||||
|
border: 1px solid #ced4da !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection__rendered {
|
||||||
|
line-height: 22px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
white-space: nowrap !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: white !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
max-height: 200px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.propagate-matrice-btn,
|
||||||
|
.propagate-all-btn {
|
||||||
|
padding: 0.1rem 0.3rem !important;
|
||||||
|
font-size: 0.8rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status,
|
||||||
|
.save-loading {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirmDeleteModal {
|
||||||
|
z-index: 1070 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirmDeleteModal .modal-backdrop {
|
||||||
|
z-index: 1065 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-btn {
|
||||||
|
padding: 0.2rem 0.4rem !important;
|
||||||
|
/* Aumentato leggermente il padding per un pulsante più grande */
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
/* Aumentato il font-size per un'icona più grande */
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-btn.has-note {
|
||||||
|
color: #dc3545 !important;
|
||||||
|
/* Rosso quando la nota è presente */
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteModal {
|
||||||
|
z-index: 1090 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteModal .modal-backdrop {
|
||||||
|
z-index: 1085 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteModal .modal-dialog {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1090 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteModal textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.propagate-date-btn {
|
||||||
|
padding: 0.2rem 0.4rem !important;
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.propagate-note-btn {
|
||||||
|
padding: 0.2rem 0.4rem !important;
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#commonNoteModal {
|
||||||
|
z-index: 1095 !important;
|
||||||
|
/* Sopra #noteModal (1090) */
|
||||||
|
}
|
||||||
|
|
||||||
|
#commonNoteModal .modal-backdrop {
|
||||||
|
z-index: 1090 !important;
|
||||||
|
/* Sopra il backdrop di #noteModal (1085) */
|
||||||
|
}
|
||||||
|
|
||||||
|
#commonNoteModal .modal-dialog {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1095 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#commonNoteModal textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+294
-18
@@ -12,11 +12,13 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
let photoAnnotations = {};
|
let photoAnnotations = {};
|
||||||
let partColors = {};
|
let partColors = {};
|
||||||
|
let partMatrice = {};
|
||||||
let selectedPartNumber = null;
|
let selectedPartNumber = null;
|
||||||
let unsavedChanges = false;
|
let unsavedChanges = false;
|
||||||
let fabricCanvas = null;
|
let fabricCanvas = null;
|
||||||
let descriptionTextbox = null;
|
let descriptionTextbox = null;
|
||||||
let markerObjects = {};
|
let markerObjects = {};
|
||||||
|
let matrici = [];
|
||||||
|
|
||||||
// ===================
|
// ===================
|
||||||
// VOICE RECOGNITION SETUP
|
// VOICE RECOGNITION SETUP
|
||||||
@@ -137,21 +139,82 @@ $(document).ready(function () {
|
|||||||
.data("iddatadb", iddatadb)
|
.data("iddatadb", iddatadb)
|
||||||
.data("idquotations", idquotations);
|
.data("idquotations", idquotations);
|
||||||
|
|
||||||
loadPhoto(iddatadb, idquotations);
|
// Precarica le matrici se iddatadb (assumendo matrici solo per iddatadb)
|
||||||
loadExistingParts(iddatadb, idquotations);
|
if (iddatadb) {
|
||||||
|
if (matrici.length === 0) {
|
||||||
|
$.ajax({
|
||||||
|
url: "get_matrici_db.php",
|
||||||
|
method: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
matrici = data.value || [];
|
||||||
|
initializeGlobalSelect2();
|
||||||
|
loadPhoto(iddatadb, idquotations);
|
||||||
|
loadExistingParts(iddatadb, idquotations);
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
matrici = [];
|
||||||
|
initializeGlobalSelect2();
|
||||||
|
loadPhoto(iddatadb, idquotations);
|
||||||
|
loadExistingParts(iddatadb, idquotations);
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Errore nel caricamento delle matrici: ' +
|
||||||
|
error +
|
||||||
|
" (" +
|
||||||
|
xhr.status +
|
||||||
|
")</div>",
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
initializeGlobalSelect2();
|
||||||
|
loadPhoto(iddatadb, idquotations);
|
||||||
|
loadExistingParts(iddatadb, idquotations);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadPhoto(iddatadb, idquotations);
|
||||||
|
loadExistingParts(iddatadb, idquotations);
|
||||||
|
}
|
||||||
|
|
||||||
const modal = new bootstrap.Modal(
|
const modalElement = document.getElementById("partsModal");
|
||||||
document.getElementById("partsModal"),
|
if (modalElement) {
|
||||||
);
|
// Verifica se il modale è già stato inizializzato
|
||||||
modal.show();
|
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (!modal) {
|
||||||
|
modal = new bootstrap.Modal(modalElement, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true,
|
||||||
|
focus: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
modal.show();
|
||||||
|
} else {
|
||||||
|
console.error("Elemento modale non trovato: #partsModal");
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Errore: Modale non trovato.</div>',
|
||||||
|
);
|
||||||
|
$("body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#partsModal .close-btn, #partsModal").on("click", function (event) {
|
$("#partsModal .close-btn, #partsModal").on("click", function (event) {
|
||||||
if (event.target === this) {
|
if (event.target === this || $(event.target).hasClass("close-btn")) {
|
||||||
const modal = bootstrap.Modal.getInstance(
|
const modalElement = document.getElementById("partsModal");
|
||||||
document.getElementById("partsModal"),
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
);
|
if (modal) {
|
||||||
modal.hide();
|
modal.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -165,7 +228,7 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("#partsModal").on("hidden.bs.modal", function () {
|
$("#partsModal").on("hidden.bs.modal", function () {
|
||||||
// Reset global state to initial values
|
// Resetta lo stato
|
||||||
photoData = {
|
photoData = {
|
||||||
naturalWidth: 0,
|
naturalWidth: 0,
|
||||||
naturalHeight: 0,
|
naturalHeight: 0,
|
||||||
@@ -175,21 +238,33 @@ $(document).ready(function () {
|
|||||||
};
|
};
|
||||||
photoAnnotations = {};
|
photoAnnotations = {};
|
||||||
partColors = {};
|
partColors = {};
|
||||||
|
partMatrice = {};
|
||||||
selectedPartNumber = null;
|
selectedPartNumber = null;
|
||||||
unsavedChanges = false;
|
unsavedChanges = false;
|
||||||
if (fabricCanvas) {
|
if (fabricCanvas) {
|
||||||
fabricCanvas.off(); // Rimuove tutti gli eventi
|
fabricCanvas.off();
|
||||||
fabricCanvas.dispose();
|
fabricCanvas.dispose();
|
||||||
fabricCanvas = null;
|
fabricCanvas = null;
|
||||||
}
|
}
|
||||||
descriptionTextbox = null;
|
descriptionTextbox = null;
|
||||||
markerObjects = {};
|
markerObjects = {};
|
||||||
// Clear UI elements
|
matrici = [];
|
||||||
$("#photoSelectorContainer").empty().hide();
|
$("#photoSelectorContainer").empty().hide();
|
||||||
$("#samplePhoto").attr("src", "");
|
$("#samplePhoto").attr("src", "");
|
||||||
$("#partsTableBody").empty();
|
$("#partsTableBody").empty();
|
||||||
// Remove any temporary messages
|
$("#global-matrice").empty();
|
||||||
$(".temp-alert").remove();
|
$(".temp-alert").remove();
|
||||||
|
|
||||||
|
// Rimuovi manualmente il backdrop e ripristina il body
|
||||||
|
const modalElement = document.getElementById("partsModal");
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (modal) {
|
||||||
|
modal.dispose(); // Distrugge l'istanza del modale
|
||||||
|
}
|
||||||
|
$(".modal-backdrop").remove();
|
||||||
|
$("body").removeClass("modal-open");
|
||||||
|
$("body").css("padding-right", "");
|
||||||
|
$(":focus").blur();
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===================
|
// ===================
|
||||||
@@ -535,6 +610,7 @@ $(document).ready(function () {
|
|||||||
if (response.success) {
|
if (response.success) {
|
||||||
$row.remove();
|
$row.remove();
|
||||||
delete partColors[partNumber];
|
delete partColors[partNumber];
|
||||||
|
delete partMatrice[partNumber];
|
||||||
if (markerObjects[partNumber]) {
|
if (markerObjects[partNumber]) {
|
||||||
fabricCanvas.remove(markerObjects[partNumber]);
|
fabricCanvas.remove(markerObjects[partNumber]);
|
||||||
delete markerObjects[partNumber];
|
delete markerObjects[partNumber];
|
||||||
@@ -576,6 +652,7 @@ $(document).ready(function () {
|
|||||||
} else {
|
} else {
|
||||||
$row.remove();
|
$row.remove();
|
||||||
delete partColors[partNumber];
|
delete partColors[partNumber];
|
||||||
|
delete partMatrice[partNumber];
|
||||||
if (markerObjects[partNumber]) {
|
if (markerObjects[partNumber]) {
|
||||||
fabricCanvas.remove(markerObjects[partNumber]);
|
fabricCanvas.remove(markerObjects[partNumber]);
|
||||||
delete markerObjects[partNumber];
|
delete markerObjects[partNumber];
|
||||||
@@ -717,6 +794,9 @@ $(document).ready(function () {
|
|||||||
</tr>`;
|
</tr>`;
|
||||||
$("#partsTableBody").append(newRow);
|
$("#partsTableBody").append(newRow);
|
||||||
partColors[part.part_number] = defaultColor;
|
partColors[part.part_number] = defaultColor;
|
||||||
|
if (part.idmatrice) {
|
||||||
|
partMatrice[part.part_number] = part.idmatrice;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
addNewRow(1);
|
addNewRow(1);
|
||||||
@@ -743,6 +823,163 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Funzione per inizializzare Select2 sulle tendine delle matrici
|
||||||
|
function initializeSelect2($select, partNumber, partId, idmatrice) {
|
||||||
|
if (typeof $.fn.select2 === "undefined") {
|
||||||
|
$select.replaceWith(
|
||||||
|
'<input type="text" class="form-control form-control-sm" placeholder="Select2 non disponibile" disabled>',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = matrici.map(function (matrice) {
|
||||||
|
return {
|
||||||
|
id: matrice.IdMatrice,
|
||||||
|
text: matrice.NomeMatrice, // Updated to use NomeMatrice
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$select.select2({
|
||||||
|
placeholder: "Seleziona matrice",
|
||||||
|
allowClear: true,
|
||||||
|
data: options,
|
||||||
|
dropdownParent: $("#partsModal"),
|
||||||
|
matcher: function (params, data) {
|
||||||
|
if (!params.term || params.term.length < 3) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const term = params.term.toUpperCase();
|
||||||
|
if (data.text.toUpperCase().indexOf(term) >= 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (partId && partId !== "new" && idmatrice) {
|
||||||
|
const matrice = matrici.find((m) => m.IdMatrice == idmatrice);
|
||||||
|
if (matrice) {
|
||||||
|
const option = new Option(
|
||||||
|
matrice.NomeMatrice, // Updated to use NomeMatrice
|
||||||
|
matrice.IdMatrice,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
$select.append(option).trigger("change");
|
||||||
|
partMatrice[partNumber] = matrice.IdMatrice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$select.on("change", function () {
|
||||||
|
const idmatrice = $(this).val();
|
||||||
|
const $listItem = $(this).closest("li");
|
||||||
|
const $saveStatus = $listItem.find(".save-status");
|
||||||
|
const $saveLoading = $listItem.find(".save-loading");
|
||||||
|
|
||||||
|
partMatrice[partNumber] = idmatrice || null;
|
||||||
|
|
||||||
|
if (partId && partId !== "new") {
|
||||||
|
$saveLoading.show();
|
||||||
|
$saveStatus.hide();
|
||||||
|
|
||||||
|
const iddatadb = $("#partsModal").data("iddatadb");
|
||||||
|
const idquotations = $("#partsModal").data("idquotations");
|
||||||
|
const endpoint = idquotations
|
||||||
|
? "save_matrice_quotation.php"
|
||||||
|
: "save_matrice.php";
|
||||||
|
const data = idquotations
|
||||||
|
? { idquotations: idquotations }
|
||||||
|
: { iddatadb: iddatadb };
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: endpoint,
|
||||||
|
method: "POST",
|
||||||
|
data: JSON.stringify({
|
||||||
|
...data,
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
id: partId,
|
||||||
|
idmatrice: idmatrice || null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
contentType: "application/json",
|
||||||
|
success: function (response) {
|
||||||
|
if (response.success) {
|
||||||
|
$saveLoading.hide();
|
||||||
|
$saveStatus.show();
|
||||||
|
setTimeout(() => $saveStatus.hide(), 2000);
|
||||||
|
} else {
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio della matrice: ' +
|
||||||
|
response.message +
|
||||||
|
"</div>",
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
$saveLoading.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio della matrice: ' +
|
||||||
|
error +
|
||||||
|
" (" +
|
||||||
|
xhr.status +
|
||||||
|
")</div>",
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
$saveLoading.hide();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funzione per inizializzare Select2 sul dropdown globale
|
||||||
|
function initializeGlobalSelect2() {
|
||||||
|
const $select = $("#global-matrice");
|
||||||
|
if (typeof $.fn.select2 === "undefined") {
|
||||||
|
$select.replaceWith(
|
||||||
|
'<input type="text" class="form-control form-control-sm" placeholder="Select2 non disponibile" disabled>',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = matrici.map(function (matrice) {
|
||||||
|
return {
|
||||||
|
id: matrice.IdMatrice,
|
||||||
|
text: matrice.NomeMatrice, // Updated to use NomeMatrice
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$select.select2({
|
||||||
|
placeholder: "Seleziona matrice globale",
|
||||||
|
allowClear: true,
|
||||||
|
data: options,
|
||||||
|
dropdownParent: $("#partsModal"),
|
||||||
|
matcher: function (params, data) {
|
||||||
|
if (!params.term || params.term.length < 3) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const term = params.term.toUpperCase();
|
||||||
|
if (data.text.toUpperCase().indexOf(term) >= 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ===================
|
// ===================
|
||||||
// PARTS LIST
|
// PARTS LIST
|
||||||
// ===================
|
// ===================
|
||||||
@@ -763,6 +1000,7 @@ $(document).ready(function () {
|
|||||||
$("#partsTableBody tr").each(function () {
|
$("#partsTableBody tr").each(function () {
|
||||||
const partNumber = $(this).find(".part-number").val();
|
const partNumber = $(this).find(".part-number").val();
|
||||||
const partDescription = $(this).find(".part-description").val();
|
const partDescription = $(this).find(".part-description").val();
|
||||||
|
const partId = $(this).data("part-id");
|
||||||
const partColor =
|
const partColor =
|
||||||
partColors[partNumber] ||
|
partColors[partNumber] ||
|
||||||
(partDescription.startsWith("Mix") ? "#0000ff" : "#ff0000");
|
(partDescription.startsWith("Mix") ? "#0000ff" : "#ff0000");
|
||||||
@@ -778,17 +1016,30 @@ $(document).ready(function () {
|
|||||||
)
|
)
|
||||||
.join("");
|
.join("");
|
||||||
const listItem = `
|
const listItem = `
|
||||||
<li class="list-group-item" data-part-number="${partNumber}">
|
<li class="list-group-item" data-part-number="${partNumber}" data-part-id="${partId}">
|
||||||
${partNumber} - ${partDescription}
|
${partNumber} - ${partDescription}
|
||||||
<div style="display: flex; align-items: center;">
|
<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" style="width: 250px !important; margin-right: 10px;"></select>
|
||||||
<button type="button" class="btn btn-success btn-sm add-to-mix-btn" 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-success btn-sm add-to-mix-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
|
||||||
<div class="color-picker-container">
|
<div class="color-picker-container">
|
||||||
<div class="color-option selected-color" style="background-color: ${partColor}; margin-left: 5px;"></div>
|
<div class="color-option selected-color" style="background-color: ${partColor}; margin-left: 5px;"></div>
|
||||||
<div class="color-picker">${colorOptions}</div>
|
<div class="color-picker">${colorOptions}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</li>`;
|
</li>`;
|
||||||
$("#partsList").append(listItem);
|
$("#partsList").append(listItem);
|
||||||
|
const $select = $("#partsList").find(
|
||||||
|
`li[data-part-number="${partNumber}"] .part-matrice`,
|
||||||
|
);
|
||||||
|
initializeSelect2(
|
||||||
|
$select,
|
||||||
|
partNumber,
|
||||||
|
partId,
|
||||||
|
partMatrice[partNumber],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -862,11 +1113,32 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on("click", ".propagate-matrice-btn", function () {
|
||||||
|
const $listItem = $(this).closest("li");
|
||||||
|
const globalVal = $("#global-matrice").val();
|
||||||
|
if (globalVal) {
|
||||||
|
$listItem.find(".part-matrice").val(globalVal).trigger("change");
|
||||||
|
} else {
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Seleziona una matrice globale prima di propagare.</div>',
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$("#partsList").on("click", "li", function (e) {
|
$("#partsList").on("click", "li", function (e) {
|
||||||
if (
|
if (
|
||||||
$(e.target).hasClass("add-to-mix-btn") ||
|
$(e.target).hasClass("add-to-mix-btn") ||
|
||||||
$(e.target).hasClass("color-option") ||
|
$(e.target).hasClass("color-option") ||
|
||||||
$(e.target).closest(".color-picker-container").length
|
$(e.target).closest(".color-picker-container").length ||
|
||||||
|
$(e.target).hasClass("part-matrice") ||
|
||||||
|
$(e.target).closest(".select2-container").length ||
|
||||||
|
$(e.target).hasClass("propagate-matrice-btn")
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
selectedPartNumber = $(this).data("part-number");
|
selectedPartNumber = $(this).data("part-number");
|
||||||
@@ -892,6 +1164,7 @@ $(document).ready(function () {
|
|||||||
? { idquotations: idquotations }
|
? { idquotations: idquotations }
|
||||||
: { iddatadb: iddatadb };
|
: { iddatadb: iddatadb };
|
||||||
let newPartColors = {};
|
let newPartColors = {};
|
||||||
|
let newPartMatrice = {};
|
||||||
let newMarkerObjects = {};
|
let newMarkerObjects = {};
|
||||||
|
|
||||||
let partsData = $rows
|
let partsData = $rows
|
||||||
@@ -908,6 +1181,7 @@ $(document).ready(function () {
|
|||||||
partsData.forEach((part, index) => {
|
partsData.forEach((part, index) => {
|
||||||
const newNumber = index + 1;
|
const newNumber = index + 1;
|
||||||
newPartColors[newNumber] = partColors[part.partNumber] || "#ff0000";
|
newPartColors[newNumber] = partColors[part.partNumber] || "#ff0000";
|
||||||
|
newPartMatrice[newNumber] = partMatrice[part.partNumber] || null;
|
||||||
if (markerObjects[part.partNumber]) {
|
if (markerObjects[part.partNumber]) {
|
||||||
newMarkerObjects[newNumber] = markerObjects[part.partNumber];
|
newMarkerObjects[newNumber] = markerObjects[part.partNumber];
|
||||||
}
|
}
|
||||||
@@ -934,6 +1208,7 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
partColors = newPartColors;
|
partColors = newPartColors;
|
||||||
|
partMatrice = newPartMatrice;
|
||||||
markerObjects = newMarkerObjects;
|
markerObjects = newMarkerObjects;
|
||||||
|
|
||||||
const partsToSave = partsData.map((part) => ({
|
const partsToSave = partsData.map((part) => ({
|
||||||
@@ -941,6 +1216,7 @@ $(document).ready(function () {
|
|||||||
part_number: part.partNumber,
|
part_number: part.partNumber,
|
||||||
part_description: part.partDescription,
|
part_description: part.partDescription,
|
||||||
mix: part.partDescription.startsWith("Mix") ? "Y" : "N",
|
mix: part.partDescription.startsWith("Mix") ? "Y" : "N",
|
||||||
|
idmatrice: partMatrice[part.partNumber] || null,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -1157,7 +1433,7 @@ $(document).ready(function () {
|
|||||||
width: annotations.descriptionSize.width,
|
width: annotations.descriptionSize.width,
|
||||||
scaleX: 1,
|
scaleX: 1,
|
||||||
scaleY: 1,
|
scaleY: 1,
|
||||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
backgroundColor: "rgba(255, 255, 255, 0)", // Changed to fully transparent
|
||||||
fontFamily: "Arial",
|
fontFamily: "Arial",
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fill: "#000000",
|
fill: "#000000",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
|||||||
|
<?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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$part = $parts[0];
|
||||||
|
$partId = $part['id'] ?? null;
|
||||||
|
$idmatrice = $part['idmatrice'] ?? null;
|
||||||
|
|
||||||
|
if (!$partId) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID parte mancante']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("UPDATE identification_parts
|
||||||
|
SET idmatrice = :idmatrice,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = :id");
|
||||||
|
$stmt->execute([
|
||||||
|
':id' => $partId,
|
||||||
|
':idmatrice' => $idmatrice // Può essere NULL
|
||||||
|
]);
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Matrice aggiornata con successo']);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio della matrice: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -15,46 +15,64 @@ if (!$iddatadb || empty($parts)) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$part = $parts[0];
|
try {
|
||||||
$partId = $part['id'] ?? null; // part_id თუ არსებობს
|
$pdo->beginTransaction();
|
||||||
$partNumber = $part['part_number'] ?? null;
|
$results = [];
|
||||||
$partDescription = $part['part_description'] ?? '';
|
|
||||||
$mix = $part['mix'] ?? 'N';
|
|
||||||
|
|
||||||
if ($partDescription) {
|
foreach ($parts as $part) {
|
||||||
try {
|
$partId = $part['id'] ?? null;
|
||||||
if ($partId) {
|
$partNumber = $part['part_number'] ?? null;
|
||||||
// UPDATE თუ უკვე არსებობს part
|
$partDescription = $part['part_description'] ?? '';
|
||||||
$stmt = $pdo->prepare("UPDATE identification_parts
|
$mix = $part['mix'] ?? 'N';
|
||||||
SET part_number = :part_number,
|
$idmatrice = $part['idmatrice'] ?? null;
|
||||||
part_description = :part_description,
|
$note = $part['note'] ?? null;
|
||||||
mix = :mix,
|
$dateexpiry = $part['dateexpiry'] ?? null;
|
||||||
updated_at = NOW()
|
|
||||||
WHERE id = :id");
|
if ($partDescription || $note || $dateexpiry) {
|
||||||
$stmt->execute([
|
if ($partId) {
|
||||||
':id' => $partId,
|
// UPDATE se la parte esiste
|
||||||
':part_number' => $partNumber,
|
$stmt = $pdo->prepare("UPDATE identification_parts
|
||||||
':part_description' => $partDescription,
|
SET part_number = :part_number,
|
||||||
':mix' => $mix
|
part_description = :part_description,
|
||||||
]);
|
mix = :mix,
|
||||||
echo json_encode(['success' => true, 'part_id' => $partId, 'part_number'=>$partNumber, 'message' => 'Parte aggiornata con successo']);
|
idmatrice = :idmatrice,
|
||||||
} else {
|
note = :note,
|
||||||
// INSERT თუ ახალია
|
dateexpiry = :dateexpiry,
|
||||||
$stmt = $pdo->prepare("INSERT INTO identification_parts
|
updated_at = NOW()
|
||||||
(iddatadb, part_number, part_description, mix, created_at, updated_at)
|
WHERE id = :id");
|
||||||
VALUES (:iddatadb, :part_number, :part_description, :mix, NOW(), NOW())");
|
$stmt->execute([
|
||||||
$stmt->execute([
|
':id' => $partId,
|
||||||
':iddatadb' => $iddatadb,
|
':part_number' => $partNumber,
|
||||||
':part_number' => $partNumber,
|
':part_description' => $partDescription,
|
||||||
':part_description' => $partDescription,
|
':mix' => $mix,
|
||||||
':mix' => $mix
|
':idmatrice' => $idmatrice,
|
||||||
]);
|
':note' => $note,
|
||||||
$newId = $pdo->lastInsertId();
|
':dateexpiry' => $dateexpiry,
|
||||||
echo json_encode(['success' => true, 'part_id' => $newId, 'part_number'=>$partNumber, 'message' => 'Parte salvata con successo']);
|
]);
|
||||||
|
$results[] = ['part_id' => $partId, 'part_number' => $partNumber, 'message' => 'Parte aggiornata con successo'];
|
||||||
|
} else {
|
||||||
|
// INSERT per nuova parte
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO identification_parts
|
||||||
|
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
|
||||||
|
VALUES (:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())");
|
||||||
|
$stmt->execute([
|
||||||
|
':iddatadb' => $iddatadb,
|
||||||
|
':part_number' => $partNumber,
|
||||||
|
':part_description' => $partDescription,
|
||||||
|
':mix' => $mix,
|
||||||
|
':idmatrice' => $idmatrice,
|
||||||
|
':note' => $note,
|
||||||
|
':dateexpiry' => $dateexpiry,
|
||||||
|
]);
|
||||||
|
$newId = $pdo->lastInsertId();
|
||||||
|
$results[] = ['part_id' => $newId, 'part_number' => $partNumber, 'message' => 'Parte salvata con successo'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (PDOException $e) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio: ' . $e->getMessage()]);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Descrizione mancante']);
|
$pdo->commit();
|
||||||
|
echo json_encode(['success' => true, 'results' => $results]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio: ' . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user