zibo-dashboard/public/userarea/produzione_programmazione_drag.php
2025-12-10 15:07:38 +01:00

1309 lines
53 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
include('include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// --- LISTE SELECT ---
$matrici = $pdo->query("SELECT id, nome FROM matrice ORDER BY nome")->fetchAll();
$mescole = $pdo->query("SELECT id, nome FROM mescole ORDER BY nome")->fetchAll();
$linee = $pdo->query("SELECT id, line_number, name FROM production_lines ORDER BY line_number")->fetchAll();
$clienti = $pdo->query("SELECT id, nome FROM clients ORDER BY nome")->fetchAll();
$status_list = $pdo->query("SELECT id, nome, badge_color, line_color FROM production_status ORDER BY ordinamento, nome")->fetchAll();
function selectOptions($arr, $sel = null, $val = 'id', $txt = 'nome')
{
$opt = '';
foreach ($arr as $a) {
$selected = ($sel !== null && $a[$val] == $sel) ? 'selected' : '';
$opt .= "<option value='{$a[$val]}' $selected>" . htmlspecialchars($a[$txt]) . "</option>";
}
return $opt;
}
// --- SALVATAGGIO INLINE ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action'])) {
$action = $_POST['action'];
try {
// ============================
// INSERT
// ============================
if ($action === 'insert') {
$dataInsert = [
'Data' => $_POST['Data'],
'data_produzione' => $_POST['data_produzione'],
'idmatrice' => $_POST['idmatrice'],
'id_linea' => $_POST['id_linea'],
'id_cliente' => $_POST['id_cliente'] ?: null,
'data_zibo' => $_POST['data_zibo'] ?: null,
'data_cliente' => $_POST['data_cliente'] ?: null,
'metri' => $_POST['metri'] ?: 0,
'kg_sp' => $_POST['kg_sp'] ?: 0,
'kg_p' => $_POST['kg_p'] ?: 0,
'ore_previste' => $_POST['ore_previste'] ?: 0,
'note_extra' => $_POST['note_extra'] ?: null,
'id_status' => $_POST['id_status'] ?? 1,
'conferma_ordine' => $_POST['conferma_ordine'] ?: null,
];
$sql = "INSERT INTO productiondata
(Data, data_produzione, conferma_ordine,
idmatrice, id_linea, id_cliente, data_zibo, data_cliente,
metri, kg_sp, kg_p, ore_previste, note_extra, id_status)
VALUES
(:Data, :data_produzione, :conferma_ordine,
:idmatrice, :id_linea, :id_cliente, :data_zibo, :data_cliente,
:metri, :kg_sp, :kg_p, :ore_previste, :note_extra, :id_status)";
$stmt = $pdo->prepare($sql);
$stmt->execute($dataInsert);
// nuovo ID
$idProd = $pdo->lastInsertId();
// reset associazioni mescole
$pdo->prepare("DELETE FROM productiondata_mescole WHERE id_productiondata=?")
->execute([$idProd]);
if (!empty($_POST['mescole'])) {
$ins = $pdo->prepare("INSERT INTO productiondata_mescole (id_productiondata, id_mescola) VALUES (?, ?)");
foreach ($_POST['mescole'] as $m) {
$ins->execute([$idProd, $m]);
}
}
echo json_encode(['success' => true]);
}
// ============================
// UPDATE
// ============================
elseif ($action === 'update' && !empty($_POST['id'])) {
$dataUpdate = [
'id' => $_POST['id'],
'idmatrice' => $_POST['idmatrice'],
'id_linea' => $_POST['id_linea'],
'id_cliente' => $_POST['id_cliente'] ?: null,
'data_zibo' => $_POST['data_zibo'] ?: null,
'data_cliente' => $_POST['data_cliente'] ?: null,
'metri' => $_POST['metri'] ?: 0,
'kg_sp' => $_POST['kg_sp'] ?: 0,
'kg_p' => $_POST['kg_p'] ?: 0,
'ore_previste' => $_POST['ore_previste'] ?: 0,
'note_extra' => $_POST['note_extra'] ?: null,
'id_status' => $_POST['id_status'] ?? 1,
'conferma_ordine' => $_POST['conferma_ordine'] ?: null,
];
$sql = "UPDATE productiondata SET
idmatrice = :idmatrice,
id_linea = :id_linea,
id_cliente = :id_cliente,
data_zibo = :data_zibo,
data_cliente = :data_cliente,
metri = :metri,
kg_sp = :kg_sp,
kg_p = :kg_p,
ore_previste = :ore_previste,
note_extra = :note_extra,
id_status = :id_status,
conferma_ordine = :conferma_ordine
WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute($dataUpdate);
// ID esistente
$idProd = $_POST['id'];
$pdo->prepare("DELETE FROM productiondata_mescole WHERE id_productiondata=?")
->execute([$idProd]);
if (!empty($_POST['mescole'])) {
$ins = $pdo->prepare("INSERT INTO productiondata_mescole (id_productiondata, id_mescola) VALUES (?, ?)");
foreach ($_POST['mescole'] as $m) {
$ins->execute([$idProd, $m]);
}
}
echo json_encode(['success' => true]);
}
exit;
} catch (Exception $e) {
echo json_encode(['success' => false, 'msg' => $e->getMessage()]);
exit;
}
}
?>
<!doctype html>
<html lang="it">
<head>
<?php include('cssinclude.php'); ?>
<title>Programmazione Produzione - <?= $titlewebsite ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet">
<link href="https://cdn.datatables.net/1.13.7/css/dataTables.bootstrap5.min.css" rel="stylesheet">
<link href="https://cdn.datatables.net/responsive/2.5.0/css/responsive.bootstrap5.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<style>
.inline-edit {
display: none;
}
.btn-xs {
padding: 0.15rem 0.35rem;
font-size: 0.7rem;
line-height: 1.2;
border-radius: .4rem;
}
.btn-xs i {
font-size: 0.85rem;
}
.table td,
.table th {
vertical-align: middle;
}
.table th {
background: #f8f9fa;
font-weight: 600;
}
.modal-xl {
max-width: 95% !important;
}
.table-responsive {
min-height: 400px;
}
#tabProgrammazione {
width: 100% !important;
}
.dataTables_wrapper .dataTables_filter input,
.dataTables_wrapper .dataTables_length select {
border-radius: 0.375rem;
border: 1px solid #ced4da;
}
.dataTables_wrapper .dataTables_filter {
float: right;
}
.dataTables_wrapper .dataTables_length {
float: left;
}
.dataTables_wrapper .dataTables_info {
float: left;
}
.dataTables_wrapper .dataTables_paginate {
float: right;
}
.btn-clear-filters {
margin-left: 0.5rem;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
.badge {
padding: .35em .65em;
font-size: .75rem;
border-radius: .4rem;
}
/* FILTRI SOPRA: TESTO PICCOLO */
.filters-row th {
padding: 0.4rem 0.3rem !important;
}
.filters-row input,
.filters-row select {
font-size: 0.75rem !important;
padding: 0.15rem 0.3rem !important;
height: auto !important;
}
.filters-row .select2-container {
font-size: 0.75rem !important;
}
.filters-row .select2-container--bootstrap-5 .select2-selection {
min-height: 28px !important;
padding: 0.1rem 0.3rem !important;
font-size: 0.75rem !important;
}
.select2-container--bootstrap-5 .select2-dropdown .select2-results__option {
font-size: 0.75rem !important;
padding: 0.2rem 0.5rem !important;
}
.select2-container--bootstrap-5 .select2-selection__placeholder {
font-size: 0.75rem !important;
}
/* ICONA NOTE */
.note-icon {
font-size: 1.1rem;
cursor: pointer;
transition: color 0.2s;
color: #6c757d;
}
.note-icon.has-note {
color: #dc3545 !important;
}
.note-icon:hover {
color: #0056b3;
}
#modalNoteView .modal-body {
white-space: pre-wrap;
max-height: 60vh;
overflow-y: auto;
font-size: 0.95rem;
}
/* Flatpickr nei filtri */
.flatpickr-input {
font-size: 0.75rem !important;
padding: 0.15rem 0.3rem !important;
}
.line-filter-btn {
font-size: 0.9rem;
padding: 0.375rem 0.75rem;
}
.line-filter-btn.active {
background-color: #0d6efd !important;
color: white !important;
}
.special-row,
.special-row td {
background-color: var(--rowcolor) !important;
}
.line-separator td {
border-top: 2px solid #343a40;
background-color: #ffffff !important;
height: 4px;
padding: 0;
}
/* FULLSCREEN BACKDROP */
#photoModal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.65);
z-index: 99999;
justify-content: center;
align-items: center;
}
/* CENTRATURA CONTENUTO */
#photoModal .modal-content {
background: white;
border-radius: 10px;
padding: 20px;
max-height: 90vh;
overflow-y: auto;
animation: fadeIn 0.25s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
/* MINIATURA FOTO */
.photo-gallery img {
width: 110px;
height: 110px;
object-fit: cover;
border-radius: 8px;
cursor: pointer;
transition: transform 0.2s;
}
.photo-gallery img:hover {
transform: scale(1.05);
}
</style>
</head>
<body>
<div class="wrapper toggled">
<?php include('include/navbar.php');
include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-3">
<h5 class="mb-0">Programmazione Produzione</h5>
<div class="d-flex align-items-center gap-2 flex-wrap">
<!-- BOTTONI FILTRO LINEA -->
<div class="btn-group me-3" role="group">
<button type="button" class="btn btn-outline-primary line-filter-btn active" data-line="">Tutte</button>
<?php foreach ($linee as $l): ?>
<button type="button" class="btn btn-outline-primary line-filter-btn" data-line="<?= $l['id'] ?>">
<?= htmlspecialchars($l['name']) ?>
</button>
<?php endforeach; ?>
</div>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalNuovo">Aggiungi</button>
<button class="btn btn-secondary" onclick="location.href='production_dashboard.php'">Torna</button>
</div>
</div>
<?php
// CARICO TUTTE LE RIGHE UNA SOLA VOLTA
$sql = "SELECT
p.*,
m.nome AS matrice,
m.photo AS matrice_photo,
GROUP_CONCAT(ms.nome SEPARATOR ', ') AS mescola,
GROUP_CONCAT(ms.id SEPARATOR ',') AS mescole_ids,
l.name AS linea,
c.nome AS cliente,
p.conferma_ordine,
s.nome AS status_nome,
COALESCE(s.badge_color, '#6c757d') AS badge_color,
COALESCE(s.line_color, '#ffffff') AS line_color
FROM productiondata p
LEFT JOIN matrice m ON p.idmatrice = m.id
LEFT JOIN productiondata_mescole pm ON p.id = pm.id_productiondata
LEFT JOIN mescole ms ON pm.id_mescola = ms.id
LEFT JOIN production_lines l ON p.id_linea = l.id
LEFT JOIN clients c ON p.id_cliente = c.id
LEFT JOIN production_status s ON p.id_status = s.id
GROUP BY p.id
ORDER BY p.priority ASC, p.data_produzione ASC, p.Data ASC
";
$rows = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
?>
<div class="card-body pt-0">
<div class="table-responsive">
<?php
$rows_special = array_filter($rows, function ($r) {
return in_array($r['id_status'], [2, 7, 8]);
});
?>
<!-- tabella speciao -->
<?php if (!empty($rows_special)): ?>
<h5 class="mt-3 mb-2">In Produzione / Pausa / Problema</h5>
<table id="tabSpecial" class="table table-hover align-middle" style="width:100%">
<thead>
<tr>
<th>Conf. Ord.</th>
<th>Matrice</th>
<th>Mescola</th>
<th>Linea</th>
<th>Cliente</th>
<th>Data Zibo</th>
<th>Data Cliente</th>
<th>Timer</th>
<th>Status</th>
<th>Foto</th>
</tr>
</thead>
<tbody>
<?php
$lastLine = null;
foreach ($rows_special as $r):
if (!empty($r['start_time'])) {
// Calcolo differenza tra adesso e lo start_time
$start = strtotime($r['start_time']);
$now = time();
$sec = max(0, $now - $start);
} else {
$sec = 0;
}
// SE CAMBIA LA LINEA → INSERISCO SEPARATORE
if ($lastLine !== null && $lastLine != $r['id_linea']) {
echo '<tr class="line-separator"><td colspan="10"></td></tr>';
}
$lastLine = $r['id_linea'];
?>
<tr class="special-row"
data-id="<?= $r['id'] ?>"
data-seconds="<?= $sec ?>"
style="--rowcolor: <?= $r['line_color'] ?>;">
<td><?= htmlspecialchars($r['conferma_ordine']) ?></td>
<td>
<?= htmlspecialchars($r['matrice']) ?>
<?php if (!empty($r['matrice_photo'])): ?>
<br>
<img src="photos/matrici/<?= htmlspecialchars($r['matrice_photo']) ?>"
data-full="photos/matrici/<?= htmlspecialchars($r['matrice_photo']) ?>"
class="photo-thumb"
style="width:42px;height:42px;object-fit:cover;
border-radius:6px;border:1px solid #ced4da;
cursor:pointer;margin-top:3px;">
<?php endif; ?>
</td>
<td><?= htmlspecialchars($r['mescola']) ?></td>
<td data-line-id="<?= $r['id_linea'] ?>"><?= htmlspecialchars($r['linea']) ?></td>
<td><?= htmlspecialchars($r['cliente']) ?></td>
<td><?= htmlspecialchars($r['data_zibo']) ?></td>
<td><?= htmlspecialchars($r['data_cliente']) ?></td>
<td class="timer" id="timer-<?= $r['id'] ?>">
<?= gmdate("H:i:s", $sec) ?>
</td>
<td>
<span class="badge" style="background: <?= $r['badge_color'] ?>;">
<?= htmlspecialchars($r['status_nome']) ?>
</span>
</td>
<td class="text-center" style="white-space: nowrap;">
<i class="bi bi-camera-fill photo-btn me-2"
data-type="lotto_mescola"
data-production="<?= $r['id'] ?>"
style="font-size:1.3rem; cursor:pointer;"></i>
<i class="bi bi-gear-fill photo-btn me-2"
data-type="parametri_macchina"
data-production="<?= $r['id'] ?>"
style="font-size:1.3rem; cursor:pointer;"></i>
<i class="bi bi-exclamation-triangle-fill photo-btn"
data-type="problema"
data-production="<?= $r['id'] ?>"
style="font-size:1.3rem; cursor:pointer; color:#b91c1c;"></i>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<hr class="my-4">
<?php endif; ?>
<!-- ========================= -->
<!-- TABELLA PROGRAMMATI -->
<!-- ========================= -->
<h5 class="mt-3 mb-2">Programmati</h5>
<table id="tabProgrammati" class="table table-hover align-middle" style="width:100%">
<thead>
<tr>
<th style="width:30px;"></th>
<th>P.</th>
<th>Conf. Ord.</th>
<th>Matrice</th>
<th>Mescola</th>
<th>Linea</th>
<th>Cliente</th>
<th>Data Zibo</th>
<th>Data Cliente</th>
<th>mt</th>
<th>kg sp</th>
<th>kg p</th>
<th>Ore</th>
<th>Note</th>
<th>Status</th>
<th style="width: 80px;">Azioni</th>
</tr>
</thead>
<tbody id="programmatiBlock">
<?php foreach ($rows as $r): ?>
<?php if ($r['id_status'] != 6) continue; ?>
<tr data-id="<?= $r['id'] ?>" data-status="6" data-priority="<?= $r['priority'] ?? '' ?>"
style="background-color: <?= $r['line_color'] ?> !important;">
<?php $is_da_programmare = true;
include 'row_production.php'; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- ========================= -->
<!-- TABELLA DA PROGRAMMARE -->
<!-- ========================= -->
<h5 class="mt-5 mb-2">Da Programmare</h5>
<table id="tabDaProgrammare" class="table table-hover align-middle" style="width:100%">
<thead>
<tr>
<th style="width:30px;"></th>
<th></th>
<th>Conf. Ord.</th>
<th>Matrice</th>
<th>Mescola</th>
<th>Linea</th>
<th>Cliente</th>
<th>Data Zibo</th>
<th>Data Cliente</th>
<th>mt</th>
<th>kg sp</th>
<th>kg p</th>
<th>Ore</th>
<th>Note</th>
<th>Status</th>
<th style="width: 80px;">Azioni</th>
</tr>
</thead>
<tbody id="daProgrammareBlock">
<?php foreach ($rows as $r): ?>
<?php if ($r['id_status'] != 1) continue; ?>
<tr data-id="<?= $r['id'] ?>" data-status="1"
style="background-color: <?= $r['line_color'] ?> !important;">
<?php $is_da_programmare = false;
include 'row_production.php'; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<!-- MODALE VISUALIZZAZIONE NOTE -->
<div class="modal fade" id="modalNoteView" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Note Extra</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="noteContent">
<!-- Contenuto caricato via JS -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Chiudi</button>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.full.min.js"></script>
<script src="https://cdn.datatables.net/1.13.7/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.7/js/dataTables.bootstrap5.min.js"></script>
<script src="https://cdn.datatables.net/responsive/2.5.0/js/dataTables.responsive.min.js"></script>
<script src="https://cdn.datatables.net/responsive/2.5.0/js/responsive.bootstrap5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<?php include('jsinclude.php'); ?>
<script>
$(function() {
const table1 = $('#tabProgrammati').DataTable({
responsive: true,
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.7/i18n/it-IT.json'
},
pageLength: 25
});
const table2 = $('#tabDaProgrammare').DataTable({
responsive: true,
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.7/i18n/it-IT.json'
},
pageLength: 25,
order: [
[8, 'desc'], // prima Data Cliente
[7, 'desc'] // poi Data Zibo
]
});
// FILTRO RAPIDO PER LINEA CON BOTTONI
$(document).on('click', '.line-filter-btn', function() {
$(".line-filter-btn").removeClass("active").addClass("btn-outline-primary");
$(this).addClass("active").removeClass("btn-outline-primary");
const lineId = $(this).data("line");
// Programmati
$("#tabProgrammati tbody tr").each(function() {
const rowLine = $(this).find("td[data-line-id]").data("line-id");
if (!lineId || lineId == rowLine) {
$(this).show();
} else {
$(this).hide();
}
});
// Da Programmare
$("#tabDaProgrammare tbody tr").each(function() {
const rowLine = $(this).find("td[data-line-id]").data("line-id");
if (!lineId || lineId == rowLine) {
$(this).show();
} else {
$(this).hide();
}
});
// (opzionale) anche la tabella special, se vuoi filtrarla
$("#tabSpecial tbody tr").each(function() {
const rowLine = $(this).find("td[data-line-id]").data("line-id");
if (!lineId || lineId == rowLine) {
$(this).show();
} else {
$(this).hide();
}
});
});
// Aggiungi al bottone "Pulisci" anche la rimozione filtro linea
$(document).on('click', '.btn-clear-filters', function() {
table.columns().search('').draw();
$('.filters-row input[type="text"]').val('');
$('.filters-row select').val('').trigger('change');
// reset dropdown status
$('.filters-row input[placeholder*="aaaa"]').each(function() {
const fp = $(this).data('flatpickr');
if (fp) fp.clear();
});
$('.dataTables_filter input').val('');
// Reset bottoni linea
$('.line-filter-btn').removeClass('active').addClass('btn-outline-primary');
$('.line-filter-btn[data-line=""]').addClass('active');
});
// --- FUNZIONE PULISCI TUTTI I FILTRO ---
$(document).on('click', '.btn-clear-filters', function() {
// Svuota tutti i filtri DataTables
table.columns().search('').draw();
// Svuota campi testo
$('.filters-row input[type="text"]').val('');
// Svuota select
$('.filters-row select').val('').trigger('change');
// Svuota Flatpickr (date inputs)
$('.filters-row input[placeholder*="aaaa"]').each(function() {
const fpInstance = $(this).data('flatpickr');
if (fpInstance) {
fpInstance.clear();
fpInstance.setDate('');
} else {
$(this).val('');
}
});
// Svuota search principale
$('.dataTables_filter input').val('');
});
// --- APERTURA MODALE NOTE ---
$(document).on('click', '.note-icon', function() {
const note = $(this).data('note') || '';
$('#noteContent').text(note || '(Nessuna nota)');
});
// --- INLINE EDIT ---
let original = {};
$('table').on('click', '.edit-row', function() {
const tr = $(this).closest('tr');
original = tr.find('.view').map((i, e) => $(e).html()).get();
tr.find('.view').hide();
tr.find('.inline-edit').show();
tr.find('.edit-row').addClass('d-none');
tr.find('.save-row, .cancel-row').removeClass('d-none');
tr.find('.inline-edit.select2').each(function() {
if (!$(this).hasClass('select2-hidden-accessible')) {
$(this).select2({
theme: 'bootstrap-5',
width: '100%'
});
}
});
});
$('table').on('click', '.cancel-row', function() {
const tr = $(this).closest('tr');
tr.find('.view').show().each((i, e) => $(e).html(original[i]));
tr.find('.inline-edit').hide();
tr.find('.edit-row').removeClass('d-none');
tr.find('.save-row, .cancel-row').addClass('d-none');
tr.find('.inline-edit.select2').select2('destroy');
});
$('table').on('click', '.save-row', function() {
const tr = $(this).closest('tr');
const data = tr.find('.inline-edit').serializeArray();
data.push({
name: 'action',
value: 'update'
}, {
name: 'id',
value: tr.data('id')
});
$.post('', data, function(r) {
if (r.success) {
location.reload();
} else {
alert(r.msg || 'Errore salvataggio');
}
}, 'json');
});
// --- ELIMINAZIONE ---
let deleteRow;
$('#tabProgrammazione').on('click', '.delete-row', function(e) {
e.preventDefault();
deleteRow = $(this).closest('tr');
$('#deleteId').val(deleteRow.data('id'));
const md = bootstrap.Modal.getOrCreateInstance(document.getElementById('modalDelete'));
md.show();
});
$('#confirmDelete').on('click', function() {
const id = $('#deleteId').val();
$.post('delete_productiondata.php', {
id
}, function(r) {
if (r.success) {
bootstrap.Modal.getInstance(document.getElementById('modalDelete')).hide();
deleteRow.fadeOut(300, () => deleteRow.remove());
} else alert(r.msg);
}, 'json');
});
// --- MODALE AGGIUNGI ---
$('#modalNuovo').on('shown.bs.modal', function() {
$(this).find('.select2').each(function() {
if (!$(this).hasClass('select2-hidden-accessible')) {
$(this).select2({
theme: 'bootstrap-5',
width: '100%'
});
}
});
});
$('#formNuovo').on('submit', function(e) {
e.preventDefault();
const data = $(this).serializeArray();
data.push({
name: 'action',
value: 'insert'
});
$.post('', data, r => r.success ? location.reload() : alert('Errore: ' + (r.msg || 'Fallito')), 'json');
});
$('#modalNuovo').on('hidden.bs.modal', function() {
$(this).find('form')[0].reset();
$(this).find('.select2').select2('destroy');
$('body').removeClass('modal-open');
$('.modal-backdrop').remove();
});
});
</script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
let sortProgrammati = Sortable.create(document.getElementById("programmatiBlock"), {
group: {
name: "prod",
pull: true,
put: true
},
handle: ".drag-icon", // 👈 drag SOLO dallicona bi-list
forceFallback: true,
fallbackOnBody: true,
fallbackTolerance: 3,
animation: 150,
onEnd: updatePriority
});
let sortDaProgrammare = Sortable.create(document.getElementById("daProgrammareBlock"), {
group: {
name: "prod",
pull: true,
put: true
},
handle: ".drag-icon", // 👈 idem
forceFallback: true,
fallbackOnBody: true,
fallbackTolerance: 3,
animation: 150,
onEnd: updatePriority
});
// disabilito sortable sulla tabella special (è sola lettura)
const specialTbody = document.querySelector("#tabSpecial tbody");
if (specialTbody) {
Sortable.create(specialTbody, {
disabled: true
});
}
function updatePriority() {
let programmati = [];
let daProgrammare = [];
// → PROGRAMMATI (status 6)
$("#programmatiBlock tr").each(function(index) {
const id = $(this).data("id");
// se il TR non ha data-id (es. riga child DataTables) lo salto
if (typeof id === "undefined" || id === null || id === "") {
return; // continue
}
programmati.push({
id: id,
priority: index + 1,
status: 6
});
});
// → DA PROGRAMMARE (status 1)
$("#daProgrammareBlock tr").each(function(index) {
const id = $(this).data("id");
if (typeof id === "undefined" || id === null || id === "") {
return; // continue
}
daProgrammare.push({
id: id,
priority: index + 1
});
});
$.post("ajax_update_priority.php", {
programmati: JSON.stringify(programmati),
daProgrammare: JSON.stringify(daProgrammare)
}, function(response) {
if (response.success) {
location.reload();
} else {
alert("Errore aggiornamento priorità: " + response.msg);
}
}, "json");
}
// ==========================
// FRECCE SU / GIÙ TRA LE DUE TABELLE
// ==========================
// Da PROGRAMMATI → DA PROGRAMMARE
$(document).on("click", ".move-to-daprogrammare", function(e) {
e.preventDefault();
const $row = $(this).closest("tr");
// sposta la riga nel tbody dei "Da programmare"
$("#daProgrammareBlock").append($row);
// aggiorna lo status nel data-attribute (utile se lo usi altrove)
$row.attr("data-status", "1");
// ricalcola priorità + invia ad ajax_update_priority.php
updatePriority();
});
// Da DA PROGRAMMARE → PROGRAMMATI
$(document).on("click", ".move-to-programmati", function(e) {
e.preventDefault();
const $row = $(this).closest("tr");
// sposta la riga nel tbody dei "Programmati"
$("#programmatiBlock").append($row);
// aggiorna lo status
$row.attr("data-status", "6");
// ricalcola priorità + invia ad ajax_update_priority.php
updatePriority();
});
});
</script>
<!-- MODALE AGGIUNGI -->
<div class="modal fade" id="modalNuovo" tabindex="-1" aria-labelledby="modalNuovoLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" style="max-width: 95vw;">
<div class="modal-content">
<form id="formNuovo">
<div class="modal-header">
<h5 class="modal-title" id="modalNuovoLabel">Aggiungi Programmazione</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Conferma d'ordine</label>
<input type="text" class="form-control" name="conferma_ordine">
</div>
<div class="col-md-6">
<label class="form-label">Data</label>
<input type="date" class="form-control" name="Data" required>
</div>
<div class="col-md-6">
<label class="form-label">Data Produzione</label>
<input type="date" class="form-control" name="data_produzione" required>
</div>
<div class="col-md-6">
<label class="form-label">Matrice</label>
<select class="form-select select2" name="idmatrice" required>
<option value="">Seleziona...</option>
<?php foreach ($matrici as $m): ?>
<option value="<?= $m['id'] ?>"><?= htmlspecialchars($m['nome']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Mescola</label>
<select class="form-select select2" name="mescole[]" multiple required>
<option value="">Seleziona...</option>
<?php foreach ($mescole as $m): ?>
<option value="<?= $m['id'] ?>"><?= htmlspecialchars($m['nome']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Linea</label>
<select class="form-select select2" name="id_linea" required>
<option value="">Seleziona...</option>
<?php foreach ($linee as $l): ?>
<option value="<?= $l['id'] ?>"><?= htmlspecialchars($l['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Cliente</label>
<select class="form-select select2" name="id_cliente">
<option value="">Nessuno</option>
<?php foreach ($clienti as $c): ?>
<option value="<?= $c['id'] ?>"><?= htmlspecialchars($c['nome']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Data Zibo</label>
<input type="date" class="form-control" name="data_zibo">
</div>
<div class="col-md-6">
<label class="form-label">Data Cliente</label>
<input type="date" class="form-control" name="data_cliente">
</div>
<div class="col-md-3">
<label class="form-label">Metri</label>
<input type="number" step="0.01" class="form-control" name="metri" value="0">
</div>
<div class="col-md-3">
<label class="form-label">Kg SP</label>
<input type="number" step="0.01" class="form-control" name="kg_sp" value="0">
</div>
<div class="col-md-3">
<label class="form-label">Kg P</label>
<input type="number" step="0.01" class="form-control" name="kg_p" value="0">
</div>
<div class="col-md-3">
<label class="form-label">Ore Previste</label>
<input type="number" step="0.01" class="form-control" name="ore_previste" value="0">
</div>
<div class="col-12">
<label class="form-label">Note Extra</label>
<textarea class="form-control" name="note_extra" rows="2"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
<button type="submit" class="btn btn-primary">Salva</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
function updateTimers() {
document.querySelectorAll(".special-row").forEach(row => {
let id = row.dataset.id;
let sec = parseInt(row.dataset.seconds) + 1;
row.dataset.seconds = sec;
let h = String(Math.floor(sec / 3600)).padStart(2, '0');
let m = String(Math.floor((sec % 3600) / 60)).padStart(2, '0');
let s = String(sec % 60).padStart(2, '0');
document.getElementById("timer-" + id).innerText = `${h}:${m}:${s}`;
});
}
setInterval(updateTimers, 1000);
});
$(document).on("click", ".photo-btn", function() {
const type = $(this).data("type");
const id = $(this).data("production");
$("#photoType").val(type);
$("#photoProductionId").val(id);
$("#photoModalTitle").text("Carica Foto " + type);
$("#photoMessageSuccess").hide();
$("#photoMessageError").hide();
// Svuota gallery
$("#photoGallery").html("<p>Caricamento foto...</p>");
// Carico foto esistenti (nuova versione JSON)
$.getJSON("get_photos.php", {
production_id: id,
photo_type: type
}, function(r) {
if (!r.success) {
$("#photoGallery").html("<p style='color:red;'>Errore nel caricamento</p>");
return;
}
if (r.photos.length === 0) {
$("#photoGallery").html("<p>Nessuna foto presente</p>");
return;
}
let html = "";
r.photos.forEach(p => {
html += `
<img src="photos/${p.filename}"
data-full="photos/${p.filename}"
class="photo-thumb"
style="width:110px; height:110px; object-fit:cover; margin:5px; cursor:pointer;">
`;
});
$("#photoGallery").html(html);
});
// MOSTRA IL MODALE
$("#photoModal").css("display", "flex");
});
// Chiudi con X o bottone "Chiudi"
$(document).on("click", "#photoModalCloseX, #photoCancel", function() {
$("#photoModal").hide();
});
// Chiudi cliccando sul backdrop scuro
$(document).on("click", "#photoModal", function(e) {
if (e.target.id === "photoModal") {
$("#photoModal").hide();
}
});
// Clic su miniatura → apri preview
$(document).on("click", ".photo-thumb", function() {
const src = $(this).data("full");
$("#previewImage").attr("src", src);
$("#imagePreviewModal").css("display", "flex");
});
// Chiudi con X (delegato)
$(document).on("click", "#previewCloseX", function() {
$("#imagePreviewModal").hide();
});
// Chiudi cliccando fuori (delegato)
$(document).on("click", "#imagePreviewModal", function(e) {
if (e.target.id === "imagePreviewModal") {
$("#imagePreviewModal").hide();
}
});
$(document).on("click", ".view-mescole-btn", function() {
let names = $(this).data("names");
let html = "";
names.split(",").forEach(n => {
html += `<div class="p-2 border-bottom">${n.trim()}</div>`;
});
$("#mescoleList").html(html);
let modal = new bootstrap.Modal(document.getElementById("modalMescoleView"));
modal.show();
});
</script>
<!-- MODALE PREVIEW FOTO -->
<div id="imagePreviewModal" class="modal"
style="display:none; position:fixed; top:0; left:0; width:100%; height:100%;
background:rgba(0,0,0,0.75); z-index:100000; justify-content:center; align-items:center;">
<div class="modal-content"
style="position:relative; max-width:90%; background:black; border-radius:10px; padding:10px;">
<!-- X CHIUSURA -->
<button id="previewCloseX"
style="position:absolute; top:10px; right:15px; font-size:2rem; color:white;
background:none; border:none; cursor:pointer;">&times;</button>
<img id="previewImage" src=""
style="max-width:100%; max-height:85vh; display:block; margin:auto;">
</div>
</div>
<div class="modal fade" id="modalMescoleView" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Mescole utilizzate</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="mescoleList"></div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Chiudi</button>
</div>
</div>
</div>
</div>
<!-- MODALE FOTO PRODUZIONE -->
<div id="photoModal"
style="display:none; position:fixed; top:0; left:0; width:100%; height:100%;
background:rgba(0,0,0,0.65); z-index:99999; justify-content:center; align-items:center;">
<div class="modal-content"
style="background:#fff; border-radius:10px; padding:20px; max-width:900px; width:95%; max-height:90vh; overflow-y:auto; position:relative;">
<!-- HEADER -->
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 id="photoModalTitle" class="mb-0">Carica Foto</h5>
<button type="button" id="photoModalCloseX" class="btn-close" aria-label="Close"></button>
</div>
<!-- MESSAGGI -->
<div id="photoMessageSuccess" class="alert alert-success d-none"></div>
<div id="photoMessageError" class="alert alert-danger d-none"></div>
<!-- GALLERY FOTO ESISTENTI -->
<div id="photoGallery" class="photo-gallery mb-3">
<!-- riempita via JS -->
</div>
<!-- FORM UPLOAD FOTO -->
<form id="photoForm" method="post" enctype="multipart/form-data">
<input type="hidden" id="photoType" name="photo_type">
<input type="hidden" id="photoProductionId" name="production_id">
<div class="mb-3">
<label class="form-label">Aggiungi nuova foto</label>
<input type="file" name="photo_file" class="form-control">
</div>
<div class="text-end">
<button type="button" class="btn btn-secondary me-2" id="photoCancel">Chiudi</button>
<button type="submit" class="btn btn-primary">Carica</button>
</div>
</form>
</div>
</div>
</body>
</html>