diff --git a/db/migrations/20260703071659_create_mescole_files_table.php b/db/migrations/20260703071659_create_mescole_files_table.php new file mode 100644 index 0000000..e182194 --- /dev/null +++ b/db/migrations/20260703071659_create_mescole_files_table.php @@ -0,0 +1,60 @@ +table('mescole_files', [ + 'id' => 'id', + 'signed' => true, + 'engine' => 'InnoDB', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_general_ci', + ]); + + $table + ->addColumn('idmescola', 'integer', [ + 'signed' => true, + 'null' => false, + ]) + ->addColumn('categoria', 'string', [ + 'limit' => 100, + 'null' => false, + 'comment' => 'slug categoria: schede_tecniche, certificati_analisi, ...', + ]) + ->addColumn('titolo', 'string', [ + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('filename', 'string', [ + 'limit' => 255, + 'null' => false, + 'comment' => 'nome fisico del file salvato su disco (random, non l\'originale)', + ]) + ->addColumn('original_filename', 'string', [ + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('mime_type', 'string', [ + 'limit' => 150, + 'null' => true, + ]) + ->addColumn('filesize', 'integer', [ + 'null' => true, + ]) + ->addColumn('uploaded_at', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + ]) + ->addIndex(['idmescola']) + ->addIndex(['categoria']) + ->addForeignKey('idmescola', 'mescole', 'id', [ + 'delete' => 'CASCADE', + 'update' => 'CASCADE', + ]) + ->create(); + } +} diff --git a/public/userarea/config/mescole_file_categories.php b/public/userarea/config/mescole_file_categories.php new file mode 100644 index 0000000..73fff12 --- /dev/null +++ b/public/userarea/config/mescole_file_categories.php @@ -0,0 +1,14 @@ + 'Schede Tecniche', + 'certificati_analisi' => 'Certificati di Analisi', +]; diff --git a/public/userarea/delete_mescola_file.php b/public/userarea/delete_mescola_file.php new file mode 100644 index 0000000..3b141b3 --- /dev/null +++ b/public/userarea/delete_mescola_file.php @@ -0,0 +1,36 @@ + false, 'message' => 'ID non valido']); + exit; +} + +try { + $db = DBHandlerSelect::getInstance(); + $pdo = $db->getConnection(); + + $stmt = $pdo->prepare("SELECT idmescola, filename FROM mescole_files WHERE id = ?"); + $stmt->execute([$id]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$row) { + echo json_encode(['success' => false, 'message' => 'File non trovato']); + exit; + } + + $del = $pdo->prepare("DELETE FROM mescole_files WHERE id = ?"); + $del->execute([$id]); + + $path = __DIR__ . '/uploads/mescole/' . $row['idmescola'] . '/' . $row['filename']; + if (is_file($path)) { + @unlink($path); + } + + echo json_encode(['success' => true]); +} catch (Throwable $e) { + echo json_encode(['success' => false, 'message' => 'Errore database: ' . $e->getMessage()]); +} diff --git a/public/userarea/get_mescola_files.php b/public/userarea/get_mescola_files.php new file mode 100644 index 0000000..9c00d80 --- /dev/null +++ b/public/userarea/get_mescola_files.php @@ -0,0 +1,44 @@ + false, 'message' => 'ID mescola non valido']); + exit; +} + +$categorie = require __DIR__ . '/config/mescole_file_categories.php'; + +try { + $db = DBHandlerSelect::getInstance(); + $pdo = $db->getConnection(); + + $sql = "SELECT id, idmescola, categoria, titolo, filename, original_filename, mime_type, filesize, uploaded_at + FROM mescole_files + WHERE idmescola = ?"; + $params = [$idmescola]; + + if (!empty($categoria)) { + $sql .= " AND categoria = ?"; + $params[] = $categoria; + } + + $sql .= " ORDER BY uploaded_at DESC"; + + $stmt = $pdo->prepare($sql); + $stmt->execute($params); + + $rows = []; + while ($r = $stmt->fetch(PDO::FETCH_ASSOC)) { + $r['categoria_label'] = $categorie[$r['categoria']] ?? $r['categoria']; + $r['url'] = 'uploads/mescole/' . $r['idmescola'] . '/' . $r['filename']; + $rows[] = $r; + } + + echo json_encode(['success' => true, 'rows' => $rows, 'categorie' => $categorie]); +} catch (Throwable $e) { + echo json_encode(['success' => false, 'message' => 'Errore server: ' . $e->getMessage()]); +} diff --git a/public/userarea/get_mescola_general.php b/public/userarea/get_mescola_general.php new file mode 100644 index 0000000..6c8a85e --- /dev/null +++ b/public/userarea/get_mescola_general.php @@ -0,0 +1,48 @@ + false, 'message' => 'ID non valido']); + exit; +} + +try { + $db = DBHandlerSelect::getInstance(); + $pdo = $db->getConnection(); + + $sql = " + SELECT + m.id, + m.nome, + m.nomeuscita, + m.is_active, + IFNULL(q.qty_totale, 0) AS qty_totale, + GROUP_CONCAT(DISTINCT pl.name SEPARATOR ', ') AS linee + FROM mescole m + LEFT JOIN ( + SELECT idmescola, SUM(qty) AS qty_totale + FROM mescole_supplier_lots + GROUP BY idmescola + ) q ON q.idmescola = m.id + LEFT JOIN mescole_lines ml ON m.id = ml.idmescola + LEFT JOIN production_lines pl ON ml.idlinea = pl.id + WHERE m.id = ? + GROUP BY m.id + "; + + $stmt = $pdo->prepare($sql); + $stmt->execute([$id]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$row) { + echo json_encode(['success' => false, 'message' => 'Mescola non trovata']); + exit; + } + + echo json_encode(['success' => true, 'data' => $row]); +} catch (Throwable $e) { + echo json_encode(['success' => false, 'message' => 'Errore database: ' . $e->getMessage()]); +} diff --git a/public/userarea/mescole.php b/public/userarea/mescole.php index 5ff3048..3a97411 100644 --- a/public/userarea/mescole.php +++ b/public/userarea/mescole.php @@ -106,6 +106,17 @@ max-width: 360px; } + #tabellaMescole td:nth-child(2) a { + color: #1f2d3d; + text-decoration: none; + font-weight: 600; + } + + #tabellaMescole td:nth-child(2) a:hover { + text-decoration: underline; + color: #0d6efd; + } + /* Q.tà totale */ #tabellaMescole th:nth-child(3), #tabellaMescole td:nth-child(3) { @@ -130,8 +141,27 @@ /* Azioni */ #tabellaMescole th:nth-child(6), #tabellaMescole td:nth-child(6) { - width: 330px; - max-width: 330px; + width: 190px; + max-width: 190px; + white-space: normal; + } + + .azioni-cell { + display: flex; + justify-content: center; + gap: 6px; + flex-wrap: nowrap; + } + + .azioni-cell .btn { + width: 34px; + height: 34px; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 1rem; + line-height: 1; } @@ -246,33 +276,50 @@ $toggleText = $isActive ? "Disattiva" : "Attiva"; $toggleClass = $isActive ? "btn-outline-warning" : "btn-outline-success"; + $nomeUscitaSafe = htmlspecialchars($row['nomeuscita']); + echo " {$row['id']} - " . htmlspecialchars($row['nomeuscita']) . " + + + {$nomeUscitaSafe} + + {$qtyTot} {$linee} {$badge} - - - - - - - + + + + + + "; @@ -444,6 +491,56 @@ + + + diff --git a/public/userarea/save_mescola_file.php b/public/userarea/save_mescola_file.php new file mode 100644 index 0000000..687a882 --- /dev/null +++ b/public/userarea/save_mescola_file.php @@ -0,0 +1,80 @@ + false, 'message' => 'ID mescola non valido']); + exit; +} +if (!array_key_exists($categoria, $categorie)) { + echo json_encode(['success' => false, 'message' => 'Categoria non valida']); + exit; +} +if ($titolo === '') { + echo json_encode(['success' => false, 'message' => 'Il titolo del file è obbligatorio']); + exit; +} +if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) { + echo json_encode(['success' => false, 'message' => 'File mancante o upload non riuscito']); + exit; +} + +// --- Validazioni di sicurezza sul file --- +$allowedExt = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'jpg', 'jpeg', 'png']; +$maxSize = 15 * 1024 * 1024; // 15 MB + +$originalName = $_FILES['file']['name']; +$tmpPath = $_FILES['file']['tmp_name']; +$size = (int)$_FILES['file']['size']; +$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); + +if (!in_array($ext, $allowedExt, true)) { + echo json_encode(['success' => false, 'message' => 'Estensione file non consentita (' . $ext . ')']); + exit; +} +if ($size <= 0 || $size > $maxSize) { + echo json_encode(['success' => false, 'message' => 'Il file supera la dimensione massima di 15MB']); + exit; +} + +$finfo = finfo_open(FILEINFO_MIME_TYPE); +$mimeType = finfo_file($finfo, $tmpPath); +finfo_close($finfo); + +// Cartella dedicata per mescola: uploads/mescole/{idmescola}/ +$destDir = __DIR__ . '/uploads/mescole/' . $idmescola . '/'; +if (!is_dir($destDir)) { + mkdir($destDir, 0775, true); +} + +// Nome fisico casuale: evita collisioni e path traversal +$storedName = bin2hex(random_bytes(16)) . '.' . $ext; +$destPath = $destDir . $storedName; + +if (!move_uploaded_file($tmpPath, $destPath)) { + echo json_encode(['success' => false, 'message' => 'Impossibile salvare il file sul server']); + exit; +} + +try { + $db = DBHandlerSelect::getInstance(); + $pdo = $db->getConnection(); + + $stmt = $pdo->prepare(" + INSERT INTO mescole_files (idmescola, categoria, titolo, filename, original_filename, mime_type, filesize, uploaded_at) + VALUES (?, ?, ?, ?, ?, ?, ?, NOW()) + "); + $stmt->execute([$idmescola, $categoria, $titolo, $storedName, $originalName, $mimeType, $size]); + + echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]); +} catch (Throwable $e) { + // rollback file se il DB fallisce + @unlink($destPath); + echo json_encode(['success' => false, 'message' => 'Errore database: ' . $e->getMessage()]); +} diff --git a/public/userarea/scheda_mescola.php b/public/userarea/scheda_mescola.php new file mode 100644 index 0000000..435ddf8 --- /dev/null +++ b/public/userarea/scheda_mescola.php @@ -0,0 +1,397 @@ + + + + + + + + + + Scheda Mescola - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> + + + + + + + + + + + + +
+ + + +
+
+ + +
+
+
Scheda Mescola
+ +
+
+
+
+
ID
+
-
+
+
+
Nome Interno
+
-
+
+
+
Q.tà Totale
+
-
+
+
+
Stato
+
-
+
+
+
Linee Associate
+
-
+
+
+
+
+ + +
+
+ +
+
+
+ +
+
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/userarea/uploads/mescole/82/9f4d056703f05feb92601aba29ed4ca3.pdf b/public/userarea/uploads/mescole/82/9f4d056703f05feb92601aba29ed4ca3.pdf new file mode 100644 index 0000000..6e1524c Binary files /dev/null and b/public/userarea/uploads/mescole/82/9f4d056703f05feb92601aba29ed4ca3.pdf differ diff --git a/public/userarea/uploads/mescole/82/cb922ad7840a4c7a415c3ab9ab99deaa.pdf b/public/userarea/uploads/mescole/82/cb922ad7840a4c7a415c3ab9ab99deaa.pdf new file mode 100644 index 0000000..64737f5 Binary files /dev/null and b/public/userarea/uploads/mescole/82/cb922ad7840a4c7a415c3ab9ab99deaa.pdf differ