Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa2f293835 | |||
| fc35adc7f9 | |||
| ac942dcdc8 | |||
| 5728afa788 | |||
| 1946648b1b | |||
| a1bcab3188 | |||
| 2bbeb11726 | |||
| dd5edab2f3 | |||
| 1fadc22178 | |||
| d3ee9a3790 | |||
| c387b71cae | |||
| b2cfec77df | |||
| 73b9a3d890 | |||
| 0550ffe923 | |||
| d2e5cc8b2b | |||
| d7b6a58407 | |||
| 174fa73c2c | |||
| 0bd41b8eb0 | |||
| 248ae63875 | |||
| d39e997beb | |||
| 1b23885659 | |||
| ad16d84b2e | |||
| 8cf74608b8 | |||
| 2642906a9b | |||
| d2f2a9089e | |||
| 53b990ff40 | |||
| f477f393ba | |||
| bc806f37f4 | |||
| f043b43791 | |||
| 245750f057 | |||
| 22e5b90fe4 | |||
| 31f22b4d92 | |||
| 340ebdcbce | |||
| 1edf7b7239 | |||
| 51cca3448a | |||
| 8732f21af8 | |||
| 3043522465 | |||
| 49435b8e44 | |||
| e876cb9775 | |||
| 7f78a61808 | |||
| 50c808e605 | |||
| 978b38c669 | |||
| 4683c4f40c | |||
| d9cbaf8df1 | |||
| 9649751ad8 | |||
| 824ae278d1 | |||
| 37909e8175 | |||
| 86782d26b2 | |||
| 329b3ffdeb | |||
| 9447f3cf00 | |||
| 5b7a8b57d5 | |||
| 7a2cac27cd | |||
| f9737fdf73 |
@@ -32,6 +32,8 @@ auth.json
|
|||||||
# File XLSX temporanei importati
|
# File XLSX temporanei importati
|
||||||
/public/userarea/imported_trf/*.xlsx
|
/public/userarea/imported_trf/*.xlsx
|
||||||
/public/userarea/xlstemplates/*.xlsx
|
/public/userarea/xlstemplates/*.xlsx
|
||||||
|
/public/userarea/photos/matrici/allegati/
|
||||||
|
/public/userarea/photos/matrici/allegati/*
|
||||||
|
|
||||||
# Ignora cartelle di foto generate
|
# Ignora cartelle di foto generate
|
||||||
/public/photostrf/
|
/public/photostrf/
|
||||||
|
|||||||
@@ -0,0 +1,254 @@
|
|||||||
|
<?php
|
||||||
|
ob_start();
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/../../../extra/auth.php');
|
||||||
|
require_once(__DIR__ . '/../class/db-functions.php');
|
||||||
|
|
||||||
|
while (ob_get_level()) {
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
if (!class_exists('Auth')) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Classe Auth non disponibile'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Auth::check()) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Sessione non valida o utente non autenticato'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
function formatDateIT($d)
|
||||||
|
{
|
||||||
|
if (!$d || $d === '0000-00-00') return '';
|
||||||
|
return date("d/m/Y", strtotime($d));
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTimeIT($d)
|
||||||
|
{
|
||||||
|
if (!$d || $d === '0000-00-00 00:00:00') return '';
|
||||||
|
return date("d/m/Y H:i", strtotime($d));
|
||||||
|
}
|
||||||
|
|
||||||
|
function worksheetNumberLabel($n)
|
||||||
|
{
|
||||||
|
$n = (int)$n;
|
||||||
|
return $n > 0 ? 'FL' . $n : '—';
|
||||||
|
}
|
||||||
|
|
||||||
|
function revisionLabel($rev)
|
||||||
|
{
|
||||||
|
$rev = trim((string)$rev);
|
||||||
|
return $rev !== '' ? $rev : '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($action === 'get_matrice_worksheets') {
|
||||||
|
$idmatrice = isset($_POST['idmatrice']) ? (int)$_POST['idmatrice'] : 0;
|
||||||
|
if ($idmatrice <= 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'ID matrice non valido'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
ws.id,
|
||||||
|
ws.idmatrice,
|
||||||
|
ws.worksheet_number,
|
||||||
|
ws.revision_code,
|
||||||
|
ws.worksheet_status,
|
||||||
|
ws.worksheet_date,
|
||||||
|
ws.customer_name,
|
||||||
|
ws.profile_type_code,
|
||||||
|
ws.marking,
|
||||||
|
ws.approved_by,
|
||||||
|
ws.created_at,
|
||||||
|
ws.updated_at,
|
||||||
|
(
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM work_sheet_mescole wsm
|
||||||
|
WHERE wsm.worksheet_id = ws.id
|
||||||
|
) AS mix_count
|
||||||
|
FROM work_sheets ws
|
||||||
|
WHERE ws.idmatrice = ?
|
||||||
|
ORDER BY
|
||||||
|
ws.worksheet_number DESC,
|
||||||
|
CASE
|
||||||
|
WHEN ws.revision_code IS NULL OR ws.revision_code = '' THEN 0
|
||||||
|
WHEN ws.revision_code REGEXP '^R[0-9]+$' THEN CAST(SUBSTRING(ws.revision_code, 2) AS UNSIGNED)
|
||||||
|
ELSE 0
|
||||||
|
END DESC,
|
||||||
|
ws.id DESC
|
||||||
|
");
|
||||||
|
$stmt->execute([$idmatrice]);
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
foreach ($rows as $r) {
|
||||||
|
$data[] = [
|
||||||
|
'id' => (int)$r['id'],
|
||||||
|
'idmatrice' => (int)$r['idmatrice'],
|
||||||
|
'worksheet_number' => (int)($r['worksheet_number'] ?? 0),
|
||||||
|
'worksheet_number_label' => worksheetNumberLabel($r['worksheet_number'] ?? 0),
|
||||||
|
'revision_code' => $r['revision_code'] ?? '',
|
||||||
|
'revision_label' => revisionLabel($r['revision_code'] ?? ''),
|
||||||
|
'worksheet_status' => $r['worksheet_status'] ?? 'active',
|
||||||
|
'worksheet_status_label' => (($r['worksheet_status'] ?? 'active') === 'inactive') ? 'Inattivo' : 'Attivo',
|
||||||
|
'worksheet_date' => $r['worksheet_date'],
|
||||||
|
'worksheet_date_it' => formatDateIT($r['worksheet_date']),
|
||||||
|
'customer_name' => $r['customer_name'] ?? '',
|
||||||
|
'profile_type_code' => $r['profile_type_code'] ?? '',
|
||||||
|
'marking' => $r['marking'] ?? '',
|
||||||
|
'approved_by' => $r['approved_by'] ?? '',
|
||||||
|
'created_at' => $r['created_at'] ?? '',
|
||||||
|
'created_at_it' => formatDateTimeIT($r['created_at'] ?? ''),
|
||||||
|
'updated_at' => $r['updated_at'] ?? '',
|
||||||
|
'updated_at_it' => formatDateTimeIT($r['updated_at'] ?? ''),
|
||||||
|
'mix_count' => (int)($r['mix_count'] ?? 0)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'worksheets' => $data
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'get_worksheet_detail') {
|
||||||
|
$worksheetId = isset($_POST['worksheet_id']) ? (int)$_POST['worksheet_id'] : 0;
|
||||||
|
if ($worksheetId <= 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'ID foglio non valido'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
ws.*,
|
||||||
|
m.nome AS matrice_nome,
|
||||||
|
m.cliente AS matrice_cliente
|
||||||
|
FROM work_sheets ws
|
||||||
|
LEFT JOIN matrice m ON m.id = ws.idmatrice
|
||||||
|
WHERE ws.id = ?
|
||||||
|
LIMIT 1
|
||||||
|
");
|
||||||
|
$stmt->execute([$worksheetId]);
|
||||||
|
$ws = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$ws) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Foglio di lavoro non trovato'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmtMix = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
wsm.*,
|
||||||
|
me.nome AS mescola_nome,
|
||||||
|
me.nomeuscita AS mescola_uscita
|
||||||
|
FROM work_sheet_mescole wsm
|
||||||
|
LEFT JOIN mescole me ON me.id = wsm.idmescola
|
||||||
|
WHERE wsm.worksheet_id = ?
|
||||||
|
ORDER BY wsm.mix_position ASC, wsm.id ASC
|
||||||
|
");
|
||||||
|
$stmtMix->execute([$worksheetId]);
|
||||||
|
$mixRows = $stmtMix->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'worksheet' => [
|
||||||
|
'id' => (int)$ws['id'],
|
||||||
|
'worksheet_number' => (int)($ws['worksheet_number'] ?? 0),
|
||||||
|
'worksheet_number_label' => worksheetNumberLabel($ws['worksheet_number'] ?? 0),
|
||||||
|
'revision_code' => $ws['revision_code'] ?? '',
|
||||||
|
'revision_label' => revisionLabel($ws['revision_code'] ?? ''),
|
||||||
|
'worksheet_status' => $ws['worksheet_status'] ?? 'active',
|
||||||
|
'worksheet_status_label' => (($ws['worksheet_status'] ?? 'active') === 'inactive') ? 'Inattivo' : 'Attivo',
|
||||||
|
'idmatrice' => (int)$ws['idmatrice'],
|
||||||
|
'matrice_nome' => $ws['matrice_nome'] ?? '',
|
||||||
|
'matrice_cliente' => $ws['matrice_cliente'] ?? '',
|
||||||
|
'worksheet_date' => $ws['worksheet_date'] ?? '',
|
||||||
|
'worksheet_date_it' => formatDateIT($ws['worksheet_date'] ?? ''),
|
||||||
|
'customer_name' => $ws['customer_name'] ?? '',
|
||||||
|
'profile_type_code' => $ws['profile_type_code'] ?? '',
|
||||||
|
'marking' => $ws['marking'] ?? '',
|
||||||
|
'prod_control_measure_settings' => $ws['prod_control_measure_settings'] ?? '',
|
||||||
|
'control_frequency_cut' => $ws['control_frequency_cut'] ?? '',
|
||||||
|
'control_frequency_drawing' => $ws['control_frequency_drawing'] ?? '',
|
||||||
|
'control_frequency_jig' => $ws['control_frequency_jig'] ?? '',
|
||||||
|
'control_mode_jig' => $ws['control_mode_jig'] ?? '',
|
||||||
|
'requested_package_code' => $ws['requested_package_code'] ?? '',
|
||||||
|
'meters_per_package' => $ws['meters_per_package'] ?? '',
|
||||||
|
'meters_per_package_tolerance' => $ws['meters_per_package_tolerance'] ?? '',
|
||||||
|
'meters_per_package_notes' => $ws['meters_per_package_notes'] ?? '',
|
||||||
|
'box_type' => $ws['box_type'] ?? '',
|
||||||
|
'packages_or_pieces_per_box' => $ws['packages_or_pieces_per_box'] ?? '',
|
||||||
|
'meters_per_box' => $ws['meters_per_box'] ?? '',
|
||||||
|
'pallet_type' => $ws['pallet_type'] ?? '',
|
||||||
|
'boxes_or_packages_per_pallet' => $ws['boxes_or_packages_per_pallet'] ?? '',
|
||||||
|
'speed_expected_kg_h' => $ws['speed_expected_kg_h'] ?? '',
|
||||||
|
'speed_actual_kg_h' => $ws['speed_actual_kg_h'] ?? '',
|
||||||
|
'speed_expected_m_h' => $ws['speed_expected_m_h'] ?? '',
|
||||||
|
'speed_actual_m_h' => $ws['speed_actual_m_h'] ?? '',
|
||||||
|
'approved_by' => $ws['approved_by'] ?? '',
|
||||||
|
'notes' => $ws['notes'] ?? '',
|
||||||
|
'created_at' => $ws['created_at'] ?? '',
|
||||||
|
'created_at_it' => formatDateTimeIT($ws['created_at'] ?? ''),
|
||||||
|
'updated_at' => $ws['updated_at'] ?? '',
|
||||||
|
'updated_at_it' => formatDateTimeIT($ws['updated_at'] ?? '')
|
||||||
|
],
|
||||||
|
'mix_rows' => array_map(function ($r) {
|
||||||
|
return [
|
||||||
|
'id' => (int)$r['id'],
|
||||||
|
'mix_position' => (int)$r['mix_position'],
|
||||||
|
'mescola_nome' => $r['mescola_nome'] ?? '',
|
||||||
|
'mescola_uscita' => $r['mescola_uscita'] ?? '',
|
||||||
|
'mix_weight_g_m' => $r['mix_weight_g_m'] ?? '',
|
||||||
|
'required_density' => $r['required_density'] ?? '',
|
||||||
|
'required_hardness_shore_a' => $r['required_hardness_shore_a'] ?? '',
|
||||||
|
'lubrication_type' => $r['lubrication_type'] ?? '',
|
||||||
|
'lubrication_notes' => $r['lubrication_notes'] ?? ''
|
||||||
|
];
|
||||||
|
}, $mixRows)
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Azione AJAX sconosciuta'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
@@ -0,0 +1,362 @@
|
|||||||
|
(function () {
|
||||||
|
function escapeHtml(str) {
|
||||||
|
return String(str || "")
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function valueOrDash(v) {
|
||||||
|
return v === null || v === undefined || String(v).trim() === ""
|
||||||
|
? "—"
|
||||||
|
: escapeHtml(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderReadonlyField(label, value) {
|
||||||
|
return `
|
||||||
|
<div class="readonly-label">${escapeHtml(label)}</div>
|
||||||
|
<div class="readonly-value">${valueOrDash(value)}</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.loadMatriceWorksheets = function (idmatrice, matriceNome, endpoint) {
|
||||||
|
endpoint = endpoint || "ajax/worksheet-linked-data.php";
|
||||||
|
|
||||||
|
$("#wl_idmatrice").val(idmatrice);
|
||||||
|
$("#wl_matrice_name").html(
|
||||||
|
`<span class="worksheet-chip"><i class="fa-solid fa-layer-group"></i>${escapeHtml(matriceNome || "")}</span>`,
|
||||||
|
);
|
||||||
|
$("#worksheetsListContainer").html(
|
||||||
|
'<div class="text-muted">Caricamento fogli di lavoro...</div>',
|
||||||
|
);
|
||||||
|
|
||||||
|
fetch(endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
body:
|
||||||
|
"action=get_matrice_worksheets&idmatrice=" +
|
||||||
|
encodeURIComponent(idmatrice),
|
||||||
|
})
|
||||||
|
.then(async (r) => {
|
||||||
|
const text = await r.text();
|
||||||
|
try {
|
||||||
|
return JSON.parse(text);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Risposta non JSON worksheets:", text);
|
||||||
|
throw new Error("Risposta non JSON, vedi console");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (!data.success) {
|
||||||
|
$("#worksheetsListContainer").html(
|
||||||
|
'<div class="text-danger">Errore nel caricamento dei fogli</div>',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = data.worksheets || [];
|
||||||
|
|
||||||
|
if (!rows.length) {
|
||||||
|
$("#worksheetsListContainer").html(
|
||||||
|
'<div class="text-muted">Nessun foglio di lavoro collegato a questo profilo</div>',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = `
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped align-middle worksheet-list-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:110px;">Foglio</th>
|
||||||
|
<th style="width:100px;">Rev.</th>
|
||||||
|
<th style="width:110px;">Stato</th>
|
||||||
|
<th style="width:140px;">Data foglio</th>
|
||||||
|
<th>Cliente</th>
|
||||||
|
<th>Codice profilo</th>
|
||||||
|
<th style="width:110px;">Mescole</th>
|
||||||
|
<th style="width:230px;">Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
`;
|
||||||
|
|
||||||
|
rows.forEach((r) => {
|
||||||
|
const statusBadge =
|
||||||
|
r.worksheet_status === "inactive"
|
||||||
|
? `<span class="worksheet-badge-status-inactive">Inattivo</span>`
|
||||||
|
: `<span class="worksheet-badge-status-active">Attivo</span>`;
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<tr>
|
||||||
|
<td><span class="worksheet-badge-fl">${escapeHtml(r.worksheet_number_label || "—")}</span></td>
|
||||||
|
<td><span class="worksheet-badge-rev">${escapeHtml(r.revision_label || "0")}</span></td>
|
||||||
|
<td>${statusBadge}</td>
|
||||||
|
<td>${escapeHtml(r.worksheet_date_it || "—")}</td>
|
||||||
|
<td title="${escapeHtml(r.customer_name || "")}">${escapeHtml(r.customer_name || "—")}</td>
|
||||||
|
<td>${escapeHtml(r.profile_type_code || "—")}</td>
|
||||||
|
<td>${escapeHtml(String(r.mix_count || 0))}</td>
|
||||||
|
<td class="text-nowrap">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-view-worksheet open-worksheet-detail"
|
||||||
|
data-id="${r.id}"
|
||||||
|
data-endpoint="${escapeHtml(endpoint)}">
|
||||||
|
<i class="fa-solid fa-eye me-1"></i>Apri dettaglio
|
||||||
|
</button>
|
||||||
|
<a class="worksheet-open-link ms-1"
|
||||||
|
href="manage-worksheet.php?id=${r.id}"
|
||||||
|
target="_blank">
|
||||||
|
<i class="fa-solid fa-arrow-up-right-from-square"></i>Apri pagina
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
$("#worksheetsListContainer").html(html);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
$("#worksheetsListContainer").html(
|
||||||
|
'<div class="text-danger">Errore nel caricamento dei fogli</div>',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.loadWorksheetDetail = function (worksheetId, endpoint) {
|
||||||
|
endpoint = endpoint || "ajax/worksheet-linked-data.php";
|
||||||
|
|
||||||
|
$("#worksheetDetailContainer").html(
|
||||||
|
'<div class="text-muted">Caricamento dettaglio foglio...</div>',
|
||||||
|
);
|
||||||
|
|
||||||
|
fetch(endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
body:
|
||||||
|
"action=get_worksheet_detail&worksheet_id=" +
|
||||||
|
encodeURIComponent(worksheetId),
|
||||||
|
})
|
||||||
|
.then(async (r) => {
|
||||||
|
const text = await r.text();
|
||||||
|
try {
|
||||||
|
return JSON.parse(text);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Risposta non JSON worksheet detail:", text);
|
||||||
|
throw new Error("Risposta non JSON, vedi console");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (!data.success) {
|
||||||
|
console.error("Errore worksheets:", data);
|
||||||
|
|
||||||
|
$("#worksheetsListContainer").html(
|
||||||
|
'<div class="text-danger">Errore nel caricamento dei fogli: ' +
|
||||||
|
escapeHtml(
|
||||||
|
data.message || "nessun dettaglio restituito",
|
||||||
|
) +
|
||||||
|
"</div>",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ws = data.worksheet || {};
|
||||||
|
const mixRows = data.mix_rows || [];
|
||||||
|
|
||||||
|
let mixHtml = `<div class="text-muted">Nessuna mescola associata</div>`;
|
||||||
|
|
||||||
|
if (mixRows.length) {
|
||||||
|
mixHtml = `
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped align-middle mix-readonly-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:80px;">Pos</th>
|
||||||
|
<th>Mescola</th>
|
||||||
|
<th style="width:120px;">Peso g/m</th>
|
||||||
|
<th style="width:140px;">Densità</th>
|
||||||
|
<th style="width:150px;">Durezza</th>
|
||||||
|
<th style="width:130px;">Lubr.</th>
|
||||||
|
<th>Note lubr.</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
`;
|
||||||
|
|
||||||
|
mixRows.forEach((r) => {
|
||||||
|
const nomeMescola = r.mescola_uscita
|
||||||
|
? `${escapeHtml(r.mescola_nome || "—")} <div class="small text-muted">${escapeHtml(r.mescola_uscita)}</div>`
|
||||||
|
: escapeHtml(r.mescola_nome || "—");
|
||||||
|
|
||||||
|
mixHtml += `
|
||||||
|
<tr>
|
||||||
|
<td>${escapeHtml(String(r.mix_position || ""))}</td>
|
||||||
|
<td>${nomeMescola}</td>
|
||||||
|
<td>${valueOrDash(r.mix_weight_g_m)}</td>
|
||||||
|
<td>${valueOrDash(r.required_density)}</td>
|
||||||
|
<td>${valueOrDash(r.required_hardness_shore_a)}</td>
|
||||||
|
<td>${valueOrDash(r.lubrication_type)}</td>
|
||||||
|
<td>${valueOrDash(r.lubrication_notes)}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
mixHtml += `
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = `
|
||||||
|
<div class="worksheet-title-box mb-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap">
|
||||||
|
<div>
|
||||||
|
<h4 class="mb-1">${escapeHtml(ws.worksheet_number_label || "—")} / Rev. ${escapeHtml(ws.revision_label || "0")}</h4>
|
||||||
|
<small>
|
||||||
|
Stato: ${escapeHtml(ws.worksheet_status_label || "Attivo")}
|
||||||
|
${ws.matrice_nome ? " • Profilo: " + escapeHtml(ws.matrice_nome) : ""}
|
||||||
|
${ws.matrice_cliente ? " • Cliente matrice: " + escapeHtml(ws.matrice_cliente) : ""}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="manage-worksheet.php?id=${escapeHtml(String(ws.id || ""))}"
|
||||||
|
target="_blank"
|
||||||
|
class="worksheet-open-link">
|
||||||
|
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||||
|
Apri foglio completo
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="readonly-card">
|
||||||
|
<div class="readonly-card-header">Dati principali</div>
|
||||||
|
<div class="readonly-card-body">
|
||||||
|
<div class="readonly-grid">
|
||||||
|
${renderReadonlyField("Foglio", ws.worksheet_number_label)}
|
||||||
|
${renderReadonlyField("Revisione", ws.revision_label)}
|
||||||
|
${renderReadonlyField("Stato", ws.worksheet_status_label)}
|
||||||
|
${renderReadonlyField("Data foglio", ws.worksheet_date_it)}
|
||||||
|
${renderReadonlyField("Cliente override", ws.customer_name)}
|
||||||
|
${renderReadonlyField("Codice profilo", ws.profile_type_code)}
|
||||||
|
${renderReadonlyField("Marchiatura", ws.marking)}
|
||||||
|
${renderReadonlyField("Approvato da", ws.approved_by)}
|
||||||
|
${renderReadonlyField("Creato il", ws.created_at_it)}
|
||||||
|
${renderReadonlyField("Ultimo aggiornamento", ws.updated_at_it)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="readonly-card">
|
||||||
|
<div class="readonly-card-header">Controlli produzione</div>
|
||||||
|
<div class="readonly-card-body">
|
||||||
|
<div class="readonly-grid">
|
||||||
|
${renderReadonlyField("Taglio", ws.control_frequency_cut)}
|
||||||
|
${renderReadonlyField("Disegno", ws.control_frequency_drawing)}
|
||||||
|
${renderReadonlyField("Dima", ws.control_frequency_jig)}
|
||||||
|
${renderReadonlyField("Modalità dima", ws.control_mode_jig)}
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="readonly-label mb-2">Impostazione misure controllo produzione</div>
|
||||||
|
<div class="readonly-value" style="white-space:pre-wrap;">${valueOrDash(ws.prod_control_measure_settings)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="readonly-card">
|
||||||
|
<div class="readonly-card-header">Packaging / Confezionamento</div>
|
||||||
|
<div class="readonly-card-body">
|
||||||
|
<div class="readonly-grid">
|
||||||
|
${renderReadonlyField("Codice confezione", ws.requested_package_code)}
|
||||||
|
${renderReadonlyField("Metri per confezione", ws.meters_per_package)}
|
||||||
|
${renderReadonlyField("Tolleranza metri/conf.", ws.meters_per_package_tolerance)}
|
||||||
|
${renderReadonlyField("Scatola tipo", ws.box_type)}
|
||||||
|
${renderReadonlyField("Conf./pezzi per scatola", ws.packages_or_pieces_per_box)}
|
||||||
|
${renderReadonlyField("Metri per scatola", ws.meters_per_box)}
|
||||||
|
${renderReadonlyField("Bancale tipo", ws.pallet_type)}
|
||||||
|
${renderReadonlyField("Scatole/conf. per bancale", ws.boxes_or_packages_per_pallet)}
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="readonly-label mb-2">Note metri / confezione</div>
|
||||||
|
<div class="readonly-value" style="white-space:pre-wrap;">${valueOrDash(ws.meters_per_package_notes)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="readonly-card">
|
||||||
|
<div class="readonly-card-header">Velocità e note</div>
|
||||||
|
<div class="readonly-card-body">
|
||||||
|
<div class="readonly-grid">
|
||||||
|
${renderReadonlyField("Vel. prevista kg/h", ws.speed_expected_kg_h)}
|
||||||
|
${renderReadonlyField("Vel. effettiva kg/h", ws.speed_actual_kg_h)}
|
||||||
|
${renderReadonlyField("Vel. prevista m/h", ws.speed_expected_m_h)}
|
||||||
|
${renderReadonlyField("Vel. effettiva m/h", ws.speed_actual_m_h)}
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="readonly-label mb-2">Note</div>
|
||||||
|
<div class="readonly-value" style="white-space:pre-wrap;">${valueOrDash(ws.notes)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="readonly-card">
|
||||||
|
<div class="readonly-card-header">Mescole associate al foglio</div>
|
||||||
|
<div class="readonly-card-body">
|
||||||
|
${mixHtml}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
$("#worksheetDetailContainer").html(html);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Catch loadMatriceWorksheets:", err);
|
||||||
|
|
||||||
|
$("#worksheetsListContainer").html(
|
||||||
|
'<div class="text-danger">Errore nel caricamento dei fogli: ' +
|
||||||
|
escapeHtml(err.message || "errore JavaScript/fetch") +
|
||||||
|
"</div>",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).on("click", ".worksheets, .show-worksheets", function () {
|
||||||
|
const idmatrice = $(this).data("id");
|
||||||
|
const nome = $(this).data("nome") || "";
|
||||||
|
const endpoint =
|
||||||
|
$(this).data("endpoint") || "ajax/worksheet-linked-data.php";
|
||||||
|
|
||||||
|
loadMatriceWorksheets(idmatrice, nome, endpoint);
|
||||||
|
new bootstrap.Modal(
|
||||||
|
document.getElementById("worksheetsListModal"),
|
||||||
|
).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("click", ".open-worksheet-detail", function () {
|
||||||
|
const worksheetId = $(this).data("id");
|
||||||
|
const endpoint =
|
||||||
|
$(this).data("endpoint") || "ajax/worksheet-linked-data.php";
|
||||||
|
|
||||||
|
loadWorksheetDetail(worksheetId, endpoint);
|
||||||
|
new bootstrap.Modal(
|
||||||
|
document.getElementById("worksheetDetailModal"),
|
||||||
|
).show();
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -8,14 +8,6 @@
|
|||||||
<i class="bi bi-box-seam" style="font-size:1.8rem; color:#334155;"></i>
|
<i class="bi bi-box-seam" style="font-size:1.8rem; color:#334155;"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Parametri macchina -->
|
|
||||||
<button class="photo-btn"
|
|
||||||
data-type="parametri_macchina"
|
|
||||||
data-production="<?= $r['id'] ?>"
|
|
||||||
title="Foto Parametri Macchina">
|
|
||||||
<i class="bi bi-speedometer" style="font-size:1.8rem; color:#334155;"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Problemi -->
|
<!-- Problemi -->
|
||||||
<button class="photo-btn"
|
<button class="photo-btn"
|
||||||
data-type="problema"
|
data-type="problema"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div id="photoModal" class="modal">
|
<div id="photoModal" class="custom-modal">
|
||||||
<div class="modal-content final-wide" style="max-width:800px; position:relative;">
|
<div class="modal-content final-wide" style="max-width:800px; position:relative;">
|
||||||
|
|
||||||
<!-- X per chiudere -->
|
<!-- X per chiudere -->
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'success' => false,
|
||||||
|
'message' => ''
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
throw new Exception('Metodo non consentito');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_POST['id']) || !is_numeric($_POST['id'])) {
|
||||||
|
throw new Exception('ID allegato non valido');
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int)$_POST['id'];
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT id, file_path FROM matrice_attachments WHERE id = :id LIMIT 1");
|
||||||
|
$stmt->execute([':id' => $id]);
|
||||||
|
$attachment = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$attachment) {
|
||||||
|
throw new Exception('Allegato non trovato');
|
||||||
|
}
|
||||||
|
|
||||||
|
$filePathRelative = ltrim((string)$attachment['file_path'], '/\\');
|
||||||
|
$filePathAbsolute = __DIR__ . '/' . $filePathRelative;
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
$deleteStmt = $pdo->prepare("DELETE FROM matrice_attachments WHERE id = :id");
|
||||||
|
$deleteStmt->execute([':id' => $id]);
|
||||||
|
|
||||||
|
if ($deleteStmt->rowCount() <= 0) {
|
||||||
|
throw new Exception('Impossibile eliminare il record allegato');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($filePathRelative) && file_exists($filePathAbsolute) && is_file($filePathAbsolute)) {
|
||||||
|
@unlink($filePathAbsolute);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
$response['success'] = true;
|
||||||
|
$response['message'] = 'Allegato eliminato correttamente';
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
}
|
||||||
|
$response['message'] = $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM mescole_supplier_lots WHERE id = ?");
|
||||||
|
$ok = $stmt->execute([$id]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => (bool)$ok]);
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM packaging_stock_lots WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid parameter id.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// Check existence
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM production_line_params WHERE id = :id");
|
||||||
|
$stmt->execute([':id' => $id]);
|
||||||
|
|
||||||
|
if (!$stmt->fetchColumn()) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Parameter not found.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
$del = $pdo->prepare("DELETE FROM production_line_params WHERE id = :id");
|
||||||
|
$del->execute([':id' => $id]);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Parameter deleted successfully.'
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Server error: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
require_once 'include/headscript.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$tool_type = trim($_POST['tool_type'] ?? '');
|
||||||
|
$description = trim($_POST['description'] ?? '');
|
||||||
|
$is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 1;
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($name === '') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Name is required.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "UPDATE production_tools
|
||||||
|
SET name = :name,
|
||||||
|
tool_type = :tool_type,
|
||||||
|
description = :description,
|
||||||
|
is_active = :is_active
|
||||||
|
WHERE id = :id";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([
|
||||||
|
'name' => $name,
|
||||||
|
'tool_type' => $tool_type ?: null,
|
||||||
|
'description' => $description ?: null,
|
||||||
|
'is_active' => $is_active,
|
||||||
|
'id' => $id
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,799 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
/* ==========================================
|
||||||
|
AJAX HANDLERS
|
||||||
|
========================================== */
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax']) && $_POST['ajax'] == '1') {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($action === 'add') {
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$code = trim($_POST['code'] ?? '');
|
||||||
|
$description = trim($_POST['description'] ?? '');
|
||||||
|
$color = trim($_POST['color'] ?? '#6c757d');
|
||||||
|
$sort_order = isset($_POST['sort_order']) && $_POST['sort_order'] !== '' ? (int)$_POST['sort_order'] : 999;
|
||||||
|
$is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 1;
|
||||||
|
|
||||||
|
if ($name === '') {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Department name is required.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code === '') {
|
||||||
|
$code = strtoupper(str_replace(' ', '_', $name));
|
||||||
|
$code = preg_replace('/[^A-Z0-9_]/', '', $code);
|
||||||
|
} else {
|
||||||
|
$code = strtoupper($code);
|
||||||
|
$code = preg_replace('/[^A-Z0-9_]/', '', $code);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $color)) {
|
||||||
|
$color = '#6c757d';
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_active = $is_active === 1 ? 1 : 0;
|
||||||
|
|
||||||
|
$check = $pdo->prepare("
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM departments
|
||||||
|
WHERE name = :name OR code = :code
|
||||||
|
");
|
||||||
|
$check->execute([
|
||||||
|
'name' => $name,
|
||||||
|
'code' => $code
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ((int)$check->fetchColumn() > 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'A department with the same name or code already exists.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "INSERT INTO departments
|
||||||
|
(name, code, description, color, sort_order, is_active, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
(:name, :code, :description, :color, :sort_order, :is_active, NOW(), NOW())";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([
|
||||||
|
'name' => $name,
|
||||||
|
'code' => $code !== '' ? $code : null,
|
||||||
|
'description' => $description !== '' ? $description : null,
|
||||||
|
'color' => $color,
|
||||||
|
'sort_order' => $sort_order,
|
||||||
|
'is_active' => $is_active
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'edit') {
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$code = trim($_POST['code'] ?? '');
|
||||||
|
$description = trim($_POST['description'] ?? '');
|
||||||
|
$color = trim($_POST['color'] ?? '#6c757d');
|
||||||
|
$sort_order = isset($_POST['sort_order']) && $_POST['sort_order'] !== '' ? (int)$_POST['sort_order'] : 999;
|
||||||
|
$is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 1;
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Invalid department ID.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($name === '') {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Department name is required.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code === '') {
|
||||||
|
$code = strtoupper(str_replace(' ', '_', $name));
|
||||||
|
$code = preg_replace('/[^A-Z0-9_]/', '', $code);
|
||||||
|
} else {
|
||||||
|
$code = strtoupper($code);
|
||||||
|
$code = preg_replace('/[^A-Z0-9_]/', '', $code);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $color)) {
|
||||||
|
$color = '#6c757d';
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_active = $is_active === 1 ? 1 : 0;
|
||||||
|
|
||||||
|
$check = $pdo->prepare("
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM departments
|
||||||
|
WHERE (name = :name OR code = :code)
|
||||||
|
AND id <> :id
|
||||||
|
");
|
||||||
|
$check->execute([
|
||||||
|
'name' => $name,
|
||||||
|
'code' => $code,
|
||||||
|
'id' => $id
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ((int)$check->fetchColumn() > 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Another department with the same name or code already exists.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "UPDATE departments
|
||||||
|
SET name = :name,
|
||||||
|
code = :code,
|
||||||
|
description = :description,
|
||||||
|
color = :color,
|
||||||
|
sort_order = :sort_order,
|
||||||
|
is_active = :is_active,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = :id";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([
|
||||||
|
'name' => $name,
|
||||||
|
'code' => $code !== '' ? $code : null,
|
||||||
|
'description' => $description !== '' ? $description : null,
|
||||||
|
'color' => $color,
|
||||||
|
'sort_order' => $sort_order,
|
||||||
|
'is_active' => $is_active,
|
||||||
|
'id' => $id
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'delete') {
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Invalid department ID.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Future-proof check:
|
||||||
|
* If later you add employees.department_id, this prevents deleting
|
||||||
|
* a department already used by employees.
|
||||||
|
*/
|
||||||
|
$columnCheck = $pdo->prepare("
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'employees'
|
||||||
|
AND COLUMN_NAME = 'department_id'
|
||||||
|
");
|
||||||
|
$columnCheck->execute();
|
||||||
|
$hasDepartmentId = (int)$columnCheck->fetchColumn() > 0;
|
||||||
|
|
||||||
|
if ($hasDepartmentId) {
|
||||||
|
$usageCheck = $pdo->prepare("
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM employees
|
||||||
|
WHERE department_id = :id
|
||||||
|
");
|
||||||
|
$usageCheck->execute(['id' => $id]);
|
||||||
|
|
||||||
|
if ((int)$usageCheck->fetchColumn() > 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'This department is linked to one or more employees and cannot be deleted.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM departments WHERE id = :id");
|
||||||
|
$stmt->execute(['id' => $id]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Unknown action.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================
|
||||||
|
PAGE DATA
|
||||||
|
========================================== */
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
SELECT *
|
||||||
|
FROM departments
|
||||||
|
ORDER BY sort_order ASC, name ASC
|
||||||
|
";
|
||||||
|
$stmtDepartments = $pdo->query($sql);
|
||||||
|
$departments = $stmtDepartments->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||||
|
<?php include('cssinclude.php'); ?>
|
||||||
|
<title>Gestione Departments - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
|
|
||||||
|
<!-- jQuery and Bootstrap -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
|
||||||
|
<!-- DataTables -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
||||||
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-dashboard {
|
||||||
|
background-color: #cfe3ff !important;
|
||||||
|
color: #1f2d3d !important;
|
||||||
|
border: 1px solid #bcd4f4 !important;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 10px 18px;
|
||||||
|
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-dashboard:hover {
|
||||||
|
background-color: #b9d3ff !important;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add {
|
||||||
|
background-color: #0d6efd;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add:hover {
|
||||||
|
background-color: #0b5ed7;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead {
|
||||||
|
background-color: #cfe3ff;
|
||||||
|
color: #1f2d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabellaDepartments thead th {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-status {
|
||||||
|
padding: 0.25rem 0.6rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-status.active {
|
||||||
|
background-color: #d1fae5;
|
||||||
|
color: #065f46;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-status.inactive {
|
||||||
|
background-color: #e5e7eb;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-color-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-code {
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
background: #f1f5f9;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-cell {
|
||||||
|
max-width: 320px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrapper toggled">
|
||||||
|
<?php include('include/navbar.php'); ?>
|
||||||
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="card p-3">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0">Gestione Departments</h5>
|
||||||
|
<button type="button" class="btn back-dashboard" onclick="location.href='production_dashboard.php'">
|
||||||
|
↩️ Torna alla Dashboard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h6 class="fw-semibold mb-0">Elenco Reparti / Departments</h6>
|
||||||
|
|
||||||
|
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addDepartmentModal">
|
||||||
|
➕ Aggiungi Department
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="tabellaDepartments" class="table table-striped align-middle text-center" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Color</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Code</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Order</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Created</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<?php if (!empty($departments)): ?>
|
||||||
|
<?php foreach ($departments as $row): ?>
|
||||||
|
<?php
|
||||||
|
$id = (int)$row['id'];
|
||||||
|
$name = $row['name'] ?? '';
|
||||||
|
$code = $row['code'] ?? '';
|
||||||
|
$description = $row['description'] ?? '';
|
||||||
|
$color = $row['color'] ?? '#6c757d';
|
||||||
|
$sortOrder = (int)($row['sort_order'] ?? 999);
|
||||||
|
$isActive = (int)($row['is_active'] ?? 1);
|
||||||
|
|
||||||
|
$statusClass = $isActive === 1 ? 'active' : 'inactive';
|
||||||
|
$statusLabel = $isActive === 1 ? 'Active' : 'Inactive';
|
||||||
|
|
||||||
|
$createdAt = !empty($row['created_at'])
|
||||||
|
? date('d/m/Y H:i', strtotime($row['created_at']))
|
||||||
|
: '-';
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $id ?></td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span class="department-color-dot" style="background-color: <?= htmlspecialchars($color, ENT_QUOTES) ?>;"></span>
|
||||||
|
<?= htmlspecialchars($color) ?>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="fw-semibold">
|
||||||
|
<?= htmlspecialchars($name) ?>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<?php if ($code !== ''): ?>
|
||||||
|
<span class="department-code"><?= htmlspecialchars($code) ?></span>
|
||||||
|
<?php else: ?>
|
||||||
|
-
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="description-cell" title="<?= htmlspecialchars($description, ENT_QUOTES) ?>">
|
||||||
|
<?= $description !== '' ? htmlspecialchars($description) : '-' ?>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td><?= $sortOrder ?></td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span class="badge-status <?= $statusClass ?>">
|
||||||
|
<?= htmlspecialchars($statusLabel) ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td><?= $createdAt ?></td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-outline-secondary edit-department"
|
||||||
|
data-id="<?= $id ?>"
|
||||||
|
data-name="<?= htmlspecialchars($name, ENT_QUOTES) ?>"
|
||||||
|
data-code="<?= htmlspecialchars($code, ENT_QUOTES) ?>"
|
||||||
|
data-description="<?= htmlspecialchars($description, ENT_QUOTES) ?>"
|
||||||
|
data-color="<?= htmlspecialchars($color, ENT_QUOTES) ?>"
|
||||||
|
data-sort_order="<?= $sortOrder ?>"
|
||||||
|
data-is_active="<?= $isActive ?>">
|
||||||
|
✏️ Modifica
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-outline-danger delete-department"
|
||||||
|
data-id="<?= $id ?>"
|
||||||
|
data-name="<?= htmlspecialchars($name, ENT_QUOTES) ?>">
|
||||||
|
🗑️ Cancella
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ADD DEPARTMENT MODAL -->
|
||||||
|
<div class="modal fade" id="addDepartmentModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background-color:#cfe3ff;">
|
||||||
|
<h5 class="modal-title">Aggiungi Department</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="addDepartmentForm">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Name</label>
|
||||||
|
<input type="text" class="form-control" id="addName" name="name" placeholder="e.g. Produzione" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Code</label>
|
||||||
|
<input type="text" class="form-control" id="addCode" name="code" placeholder="Optional, e.g. PRODUZIONE">
|
||||||
|
<small class="text-muted">If empty, it will be generated automatically from the name.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Description</label>
|
||||||
|
<textarea class="form-control" id="addDescription" name="description" rows="3" placeholder="Optional notes"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label fw-semibold">Color</label>
|
||||||
|
<input type="color" class="form-control form-control-color" id="addColor" name="color" value="#6c757d">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label fw-semibold">Sort Order</label>
|
||||||
|
<input type="number" class="form-control" id="addSortOrder" name="sort_order" value="999" min="0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Status</label>
|
||||||
|
<select class="form-select" id="addIsActive" name="is_active">
|
||||||
|
<option value="1" selected>Active</option>
|
||||||
|
<option value="0">Inactive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<button type="submit" class="btn btn-add">💾 Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- EDIT DEPARTMENT MODAL -->
|
||||||
|
<div class="modal fade" id="editDepartmentModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background-color:#cfe3ff;">
|
||||||
|
<h5 class="modal-title">Modifica Department</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="editDepartmentForm">
|
||||||
|
<input type="hidden" id="editDepartmentId">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Name</label>
|
||||||
|
<input type="text" class="form-control" id="editName" name="name" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Code</label>
|
||||||
|
<input type="text" class="form-control" id="editCode" name="code">
|
||||||
|
<small class="text-muted">If empty, it will be generated automatically from the name.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Description</label>
|
||||||
|
<textarea class="form-control" id="editDescription" name="description" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label fw-semibold">Color</label>
|
||||||
|
<input type="color" class="form-control form-control-color" id="editColor" name="color" value="#6c757d">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label fw-semibold">Sort Order</label>
|
||||||
|
<input type="number" class="form-control" id="editSortOrder" name="sort_order" value="999" min="0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Status</label>
|
||||||
|
<select class="form-select" id="editIsActive" name="is_active">
|
||||||
|
<option value="1">Active</option>
|
||||||
|
<option value="0">Inactive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<button type="submit" class="btn btn-add">💾 Save Changes</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('jsinclude.php'); ?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#tabellaDepartments').DataTable({
|
||||||
|
order: [
|
||||||
|
[5, 'asc'],
|
||||||
|
[2, 'asc']
|
||||||
|
],
|
||||||
|
pageLength: 25,
|
||||||
|
language: {
|
||||||
|
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json',
|
||||||
|
emptyTable: 'Nessun department presente'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------- ADD DEPARTMENT -------- */
|
||||||
|
$("#addDepartmentForm").on("submit", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const payload = new URLSearchParams();
|
||||||
|
payload.append('ajax', '1');
|
||||||
|
payload.append('action', 'add');
|
||||||
|
payload.append('name', $("#addName").val().trim());
|
||||||
|
payload.append('code', $("#addCode").val().trim());
|
||||||
|
payload.append('description', $("#addDescription").val().trim());
|
||||||
|
payload.append('color', $("#addColor").val());
|
||||||
|
payload.append('sort_order', $("#addSortOrder").val());
|
||||||
|
payload.append('is_active', $("#addIsActive").val());
|
||||||
|
|
||||||
|
fetch("", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: payload.toString()
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "success",
|
||||||
|
title: "Saved!",
|
||||||
|
confirmButtonColor: "#3085d6"
|
||||||
|
}).then(() => location.reload());
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Error",
|
||||||
|
text: data.message || "Unable to save department."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Error",
|
||||||
|
text: "Communication error."
|
||||||
|
});
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------- OPEN EDIT MODAL -------- */
|
||||||
|
$(document).on("click", ".edit-department", function() {
|
||||||
|
const btn = $(this);
|
||||||
|
|
||||||
|
$("#editDepartmentId").val(btn.data("id"));
|
||||||
|
$("#editName").val(btn.data("name"));
|
||||||
|
$("#editCode").val(btn.data("code"));
|
||||||
|
$("#editDescription").val(btn.data("description"));
|
||||||
|
$("#editColor").val(btn.data("color") || '#6c757d');
|
||||||
|
$("#editSortOrder").val(btn.data("sort_order"));
|
||||||
|
$("#editIsActive").val(String(btn.data("is_active")));
|
||||||
|
|
||||||
|
$("#editDepartmentModal").modal("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------- SAVE EDIT -------- */
|
||||||
|
$("#editDepartmentForm").on("submit", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const payload = new URLSearchParams();
|
||||||
|
payload.append('ajax', '1');
|
||||||
|
payload.append('action', 'edit');
|
||||||
|
payload.append('id', $("#editDepartmentId").val());
|
||||||
|
payload.append('name', $("#editName").val().trim());
|
||||||
|
payload.append('code', $("#editCode").val().trim());
|
||||||
|
payload.append('description', $("#editDescription").val().trim());
|
||||||
|
payload.append('color', $("#editColor").val());
|
||||||
|
payload.append('sort_order', $("#editSortOrder").val());
|
||||||
|
payload.append('is_active', $("#editIsActive").val());
|
||||||
|
|
||||||
|
fetch("", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: payload.toString()
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "success",
|
||||||
|
title: "Updated!",
|
||||||
|
confirmButtonColor: "#3085d6"
|
||||||
|
}).then(() => location.reload());
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Error",
|
||||||
|
text: data.message || "Unable to update department."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Error",
|
||||||
|
text: "Communication error."
|
||||||
|
});
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------- DELETE DEPARTMENT -------- */
|
||||||
|
$(document).on("click", ".delete-department", function() {
|
||||||
|
const id = $(this).data("id");
|
||||||
|
const name = $(this).data("name");
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: "Confermi la cancellazione?",
|
||||||
|
text: name ? ("Department: " + name) : "This department will be deleted.",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#d33",
|
||||||
|
cancelButtonColor: "#6c757d",
|
||||||
|
confirmButtonText: "Sì, cancella",
|
||||||
|
cancelButtonText: "Annulla"
|
||||||
|
}).then((result) => {
|
||||||
|
if (!result.isConfirmed) return;
|
||||||
|
|
||||||
|
const payload = new URLSearchParams();
|
||||||
|
payload.append('ajax', '1');
|
||||||
|
payload.append('action', 'delete');
|
||||||
|
payload.append('id', id);
|
||||||
|
|
||||||
|
fetch("", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: payload.toString()
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "success",
|
||||||
|
title: "Deleted!",
|
||||||
|
confirmButtonColor: "#3085d6"
|
||||||
|
}).then(() => location.reload());
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Error",
|
||||||
|
text: data.message || "Unable to delete department."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Error",
|
||||||
|
text: "Communication error."
|
||||||
|
});
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
require_once 'include/headscript.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$registrationNumber = trim($_POST['registration_number'] ?? '');
|
||||||
|
$serialNumber = trim($_POST['serial_number'] ?? '');
|
||||||
|
$toolType = trim($_POST['tool_type'] ?? '');
|
||||||
|
$manufacturer = trim($_POST['manufacturer'] ?? '');
|
||||||
|
$description = trim($_POST['description'] ?? '');
|
||||||
|
$isActive = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 1;
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($name === '') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Name is required.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "UPDATE production_tools
|
||||||
|
SET name = :name,
|
||||||
|
registration_number = :registration_number,
|
||||||
|
serial_number = :serial_number,
|
||||||
|
tool_type = :tool_type,
|
||||||
|
manufacturer = :manufacturer,
|
||||||
|
description = :description,
|
||||||
|
is_active = :is_active
|
||||||
|
WHERE id = :id";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([
|
||||||
|
'name' => $name,
|
||||||
|
'registration_number' => $registrationNumber ?: null,
|
||||||
|
'serial_number' => $serialNumber ?: null,
|
||||||
|
'tool_type' => $toolType ?: null,
|
||||||
|
'manufacturer' => $manufacturer ?: null,
|
||||||
|
'description' => $description ?: null,
|
||||||
|
'is_active' => $isActive,
|
||||||
|
'id' => $id
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'success' => false,
|
||||||
|
'attachments' => [],
|
||||||
|
'message' => ''
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
throw new Exception('ID matrice non valido');
|
||||||
|
}
|
||||||
|
|
||||||
|
$idmatrice = (int)$_GET['id'];
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$sql = "SELECT
|
||||||
|
id,
|
||||||
|
matrice_id,
|
||||||
|
file_name,
|
||||||
|
file_path,
|
||||||
|
file_type,
|
||||||
|
description,
|
||||||
|
sort_order,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM matrice_attachments
|
||||||
|
WHERE matrice_id = :matrice_id
|
||||||
|
ORDER BY sort_order ASC, id DESC";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([':matrice_id' => $idmatrice]);
|
||||||
|
|
||||||
|
$attachments = [];
|
||||||
|
|
||||||
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
$relativePath = ltrim((string)$row['file_path'], '/\\');
|
||||||
|
|
||||||
|
$attachments[] = [
|
||||||
|
'id' => (int)$row['id'],
|
||||||
|
'matrice_id' => (int)$row['matrice_id'],
|
||||||
|
'file_name' => $row['file_name'],
|
||||||
|
'file_path' => $relativePath,
|
||||||
|
'file_url' => $relativePath,
|
||||||
|
'file_type' => $row['file_type'],
|
||||||
|
'description' => $row['description'] ?? '',
|
||||||
|
'sort_order' => (int)($row['sort_order'] ?? 0),
|
||||||
|
'created_at' => !empty($row['created_at']) ? date('d/m/Y H:i', strtotime($row['created_at'])) : '',
|
||||||
|
'updated_at' => !empty($row['updated_at']) ? date('d/m/Y H:i', strtotime($row['updated_at'])) : ''
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$response['success'] = true;
|
||||||
|
$response['attachments'] = $attachments;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$response['message'] = $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$idmatrice = (int)($_GET['id'] ?? 0);
|
||||||
|
if ($idmatrice <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// tutte le linee (attive + inattive se vuoi: qui prendo tutte)
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT id, line_number, name
|
||||||
|
FROM production_lines
|
||||||
|
ORDER BY line_number ASC
|
||||||
|
");
|
||||||
|
$lines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// linee già associate
|
||||||
|
$stmt = $pdo->prepare("SELECT idlinea FROM matrici_lines WHERE idmatrice = ?");
|
||||||
|
$stmt->execute([$idmatrice]);
|
||||||
|
$selected_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'lines' => $lines,
|
||||||
|
'selected_ids' => $selected_ids
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Server error']);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$idmatrice = (int)($_GET['id'] ?? 0);
|
||||||
|
if ($idmatrice <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// tutte le mescole
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT id, nome, nomeuscita
|
||||||
|
FROM mescole
|
||||||
|
ORDER BY nome ASC
|
||||||
|
");
|
||||||
|
$mescole = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// mescole già associate
|
||||||
|
$stmt = $pdo->prepare("SELECT idmescola FROM matrici_mescole WHERE idmatrice = ?");
|
||||||
|
$stmt->execute([$idmatrice]);
|
||||||
|
$selected_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'mescole' => $mescole,
|
||||||
|
'selected_ids' => $selected_ids
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Server error']);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$sql = "SELECT
|
||||||
|
msl.id,
|
||||||
|
msl.idsupplier AS idsupplier,
|
||||||
|
s.supplier_name,
|
||||||
|
msl.supplier_mix_name,
|
||||||
|
msl.lot_code,
|
||||||
|
msl.expiry_date,
|
||||||
|
msl.qty
|
||||||
|
FROM mescole_supplier_lots msl
|
||||||
|
INNER JOIN suppliers s ON s.idsupplier = msl.idsupplier
|
||||||
|
WHERE msl.idmescola = ?
|
||||||
|
ORDER BY msl.id DESC";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'rows' => $rows]);
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$sql = "SELECT
|
||||||
|
psl.id,
|
||||||
|
psl.idsupplier,
|
||||||
|
s.supplier_name,
|
||||||
|
psl.lot_code,
|
||||||
|
psl.expiry_date,
|
||||||
|
psl.qty
|
||||||
|
FROM packaging_stock_lots psl
|
||||||
|
INNER JOIN suppliers s ON s.idsupplier = psl.idsupplier
|
||||||
|
WHERE psl.idpackaging_item = ?
|
||||||
|
ORDER BY psl.id DESC";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'rows' => $rows]);
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid parameter id.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT id, line_id, position, short_label, label, icon
|
||||||
|
FROM production_line_params
|
||||||
|
WHERE id = :id
|
||||||
|
");
|
||||||
|
$stmt->execute([':id' => $id]);
|
||||||
|
$param = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$param) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Parameter not found.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'param' => $param
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Server error: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SELECT idsupplier, supplier_name FROM suppliers ORDER BY supplier_name ASC");
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'rows' => $rows]);
|
||||||
@@ -7,7 +7,7 @@ ini_set('display_errors', 1);
|
|||||||
ini_set('display_startup_errors', 1);
|
ini_set('display_startup_errors', 1);
|
||||||
error_reporting(E_ALL | E_STRICT);
|
error_reporting(E_ALL | E_STRICT);
|
||||||
// This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php
|
// This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php
|
||||||
include('../../extra/auth.php');
|
include(__DIR__ . '/../../../extra/auth.php');
|
||||||
//require_once __DIR__ . '/extra/auth.php';
|
//require_once __DIR__ . '/extra/auth.php';
|
||||||
|
|
||||||
// Here we just check if user is not
|
// Here we just check if user is not
|
||||||
@@ -15,8 +15,13 @@ include('../../extra/auth.php');
|
|||||||
// the user to vanguard login page.
|
// the user to vanguard login page.
|
||||||
|
|
||||||
if (! Auth::check()) {
|
if (! Auth::check()) {
|
||||||
|
// Cut everything at /userarea/ and append /login.
|
||||||
redirectTo('../../public/login');
|
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
|
||||||
|
$basePath = substr($scriptName, 0, strpos($scriptName, '/userarea/'));
|
||||||
|
if ($basePath === false || $basePath === '') {
|
||||||
|
$basePath = '';
|
||||||
|
}
|
||||||
|
redirectTo($basePath . '/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|||||||
@@ -59,6 +59,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="javascript:;" class="has-arrow">
|
||||||
|
<div class="parent-icon"><i class="bx bx-calendar-check"></i>
|
||||||
|
</div>
|
||||||
|
<div class="menu-title">Scadenzario</div>
|
||||||
|
</a>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="scadenzario/index.php"><i class='bx bx-radio-circle'></i>Lista Scadenze</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="scadenzario/calendar.php"><i class='bx bx-radio-circle'></i>Calendario</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="menu-label">Others</li>
|
<li class="menu-label">Others</li>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="search-bar d-lg-block d-none" data-bs-toggle="modal" data-bs-target="#SearchModal">
|
<div class="search-bar d-lg-block d-none" data-bs-toggle="modal" data-bs-target="#SearchModal">
|
||||||
<!-- <a href="avascript:;" class="btn d-flex align-items-center"><i class='bx bx-search'></i>Search</a> -->
|
<!-- <a href="javascript:;" class="btn d-flex align-items-center"><i class='bx bx-search'></i>Search</a> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="top-menu ms-auto">
|
<div class="top-menu ms-auto">
|
||||||
<ul class="navbar-nav align-items-center gap-1">
|
<ul class="navbar-nav align-items-center gap-1">
|
||||||
<li class="nav-item mobile-search-icon d-flex d-lg-none" data-bs-toggle="modal" data-bs-target="#SearchModal">
|
<li class="nav-item mobile-search-icon d-flex d-lg-none" data-bs-toggle="modal" data-bs-target="#SearchModal">
|
||||||
<a class="nav-link" href="avascript:;"><i class='bx bx-search'></i>
|
<a class="nav-link" href="javascript:;"><i class='bx bx-search'></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item dropdown dropdown-laungauge d-none d-sm-flex">
|
<li class="nav-item dropdown dropdown-laungauge d-none d-sm-flex">
|
||||||
<a class="nav-link dropdown-toggle dropdown-toggle-nocaret" href="avascript:;" data-bs-toggle="dropdown"><img src="assets/images/county/uk.svg" width="22" alt="">
|
<a class="nav-link dropdown-toggle dropdown-toggle-nocaret" href="javascript:;" data-bs-toggle="dropdown"><img src="assets/images/county/uk.svg" width="22" alt="">
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
<ul class="dropdown-menu dropdown-menu-end">
|
||||||
<li><a class="dropdown-item d-flex align-items-center py-2" href="javascript:;"><img src="assets/images/county/uk.svg" width="20" alt=""><span class="ms-2">English</span></a>
|
<li><a class="dropdown-item d-flex align-items-center py-2" href="javascript:;"><img src="assets/images/county/uk.svg" width="20" alt=""><span class="ms-2">English</span></a>
|
||||||
@@ -60,13 +60,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<a href="javascript:;">
|
<a href="javascript:;">
|
||||||
<div class="text-center msg-footer">
|
<div class="text-center msg-footer">
|
||||||
@@ -92,16 +85,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
<ul class="dropdown-menu dropdown-menu-end">
|
||||||
<li><a class="dropdown-item d-flex align-items-center" href="user-profile.php"><i class="bx bx-user fs-5"></i><span>Profile</span></a>
|
<li>
|
||||||
</li>
|
<a class="dropdown-item d-flex align-items-center" href="../users">
|
||||||
<li><a class="dropdown-item d-flex align-items-center" href="settings.php"><i class="bx bx-cog fs-5"></i><span>Settings</span></a>
|
<i class="bx bx-user fs-5"></i><span>Utente</span>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="dropdown-divider mb-0"></div>
|
<div class="dropdown-divider mb-0"></div>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="dropdown-item d-flex align-items-center" href="../logout"><i class="bx bx-log-out-circle"></i><span>Logout</span></a>
|
<li>
|
||||||
|
<a class="dropdown-item d-flex align-items-center" href="../logout">
|
||||||
|
<i class="bx bx-log-out-circle"></i><span>Logout</span>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!-- MODALE LISTA FOGLI DI LAVORO -->
|
||||||
|
<div class="modal fade" id="worksheetsListModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered modal-worksheet-list">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background:linear-gradient(135deg,#e7f1ff,#d6e9ff);">
|
||||||
|
<div>
|
||||||
|
<h5 class="modal-title mb-0">
|
||||||
|
<i class="fa-solid fa-clipboard-list me-2"></i>Fogli di lavoro collegati
|
||||||
|
</h5>
|
||||||
|
<small class="text-muted">Elenco fogli associati al profilo selezionato</small>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="wl_idmatrice">
|
||||||
|
<div class="fw-semibold mb-3" id="wl_matrice_name" style="color:#0b3d91;"></div>
|
||||||
|
|
||||||
|
<div id="worksheetsListContainer">
|
||||||
|
<div class="text-muted">Caricamento fogli di lavoro...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MODALE DETTAGLIO FOGLIO DI LAVORO -->
|
||||||
|
<div class="modal fade" id="worksheetDetailModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-worksheet-view">
|
||||||
|
<div class="modal-content" style="min-height:92vh;">
|
||||||
|
<div class="modal-header" style="background:linear-gradient(135deg,#e7f1ff,#d6e9ff);">
|
||||||
|
<div>
|
||||||
|
<h5 class="modal-title mb-0">
|
||||||
|
<i class="fa-solid fa-file-lines me-2"></i>Dettaglio foglio di lavoro
|
||||||
|
</h5>
|
||||||
|
<small class="text-muted">Visualizzazione completa in sola lettura</small>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body" id="worksheetDetailContainer">
|
||||||
|
<div class="text-muted">Caricamento dettaglio foglio...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,381 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// Read line id from GET
|
||||||
|
$lineId = isset($_GET['line_id']) ? (int)$_GET['line_id'] : 0;
|
||||||
|
|
||||||
|
if ($lineId <= 0) {
|
||||||
|
die("Invalid line id");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load line data
|
||||||
|
$stmtLine = $pdo->prepare("SELECT * FROM production_lines WHERE id = :id");
|
||||||
|
$stmtLine->execute([':id' => $lineId]);
|
||||||
|
$line = $stmtLine->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$line) {
|
||||||
|
die("Production line not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load parameters for this line
|
||||||
|
$stmtParams = $pdo->prepare("
|
||||||
|
SELECT *
|
||||||
|
FROM production_line_params
|
||||||
|
WHERE line_id = :line_id
|
||||||
|
ORDER BY position ASC, id ASC
|
||||||
|
");
|
||||||
|
$stmtParams->execute([':line_id' => $lineId]);
|
||||||
|
$params = $stmtParams->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||||
|
<?php include('cssinclude.php'); ?>
|
||||||
|
<title>Parametri Linea <?= htmlspecialchars($line['name']) ?> - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
|
|
||||||
|
<!-- jQuery / Bootstrap / SweetAlert -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
|
||||||
|
<!-- DataTables -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
||||||
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-lines {
|
||||||
|
background-color: #cfe3ff !important;
|
||||||
|
color: #1f2d3d !important;
|
||||||
|
border: 1px solid #bcd4f4 !important;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 10px 18px;
|
||||||
|
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-lines:hover {
|
||||||
|
background-color: #b9d3ff !important;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add {
|
||||||
|
background-color: #0d6efd;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add:hover {
|
||||||
|
background-color: #0b5ed7;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action.edit {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action.delete {
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action:hover {
|
||||||
|
transform: scale(1.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead {
|
||||||
|
background-color: #cfe3ff;
|
||||||
|
color: #1f2d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-preview i {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrapper">
|
||||||
|
<?php include('include/navbar.php'); ?>
|
||||||
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="card p-3">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0 w-100 text-center">
|
||||||
|
Parametri Linea: <?= htmlspecialchars($line['name']) ?> (Linea <?= (int)$line['line_number']; ?>)
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn back-lines position-absolute end-0 me-3" onclick="location.href='linee.php'">
|
||||||
|
↩️ Torna alle Linee
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<div>
|
||||||
|
<h6 class="fw-semibold mb-0">Elenco Parametri</h6>
|
||||||
|
<small class="text-muted">
|
||||||
|
Linea ID: <?= (int)$line['id']; ?> - Colore:
|
||||||
|
<span style="display:inline-block;width:16px;height:16px;border-radius:4px;border:1px solid #999;background:<?= htmlspecialchars($line['color']); ?>;"></span>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-add" id="btnAddParam">➕ Aggiungi Parametro</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="tabellaParametri" class="table table-striped align-middle text-center" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Position</th>
|
||||||
|
<th>Short label</th>
|
||||||
|
<th>Label</th>
|
||||||
|
<th>Icon class</th>
|
||||||
|
<th>Preview</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($params)) : ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="7" class="text-muted">No parameters defined for this line.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else : ?>
|
||||||
|
<?php foreach ($params as $p) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= (int)$p['id']; ?></td>
|
||||||
|
<td><?= (int)$p['position']; ?></td>
|
||||||
|
<td><?= htmlspecialchars($p['short_label']); ?></td>
|
||||||
|
<td><?= htmlspecialchars($p['label']); ?></td>
|
||||||
|
<td><?= htmlspecialchars($p['icon']); ?></td>
|
||||||
|
<td class="icon-preview">
|
||||||
|
<?php if (!empty($p['icon'])) : ?>
|
||||||
|
<i class="bi <?= htmlspecialchars($p['icon']); ?>"></i>
|
||||||
|
<?php else : ?>
|
||||||
|
<span class="text-muted">-</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn-action edit"
|
||||||
|
data-id="<?= (int)$p['id']; ?>">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn-action delete"
|
||||||
|
data-id="<?= (int)$p['id']; ?>">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ADD/EDIT PARAM MODAL -->
|
||||||
|
<div class="modal fade" id="paramModal" tabindex="-1" aria-labelledby="paramModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background-color:#cfe3ff;">
|
||||||
|
<h5 class="modal-title" id="paramModalLabel">Add Parameter</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="paramForm">
|
||||||
|
<input type="hidden" name="param_id" id="param_id" value="">
|
||||||
|
<input type="hidden" name="line_id" id="line_id" value="<?= (int)$line['id']; ?>">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="position" class="form-label fw-semibold">Position (slot)</label>
|
||||||
|
<input type="number" class="form-control" id="position" name="position" required min="1">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="short_label" class="form-label fw-semibold">Short label</label>
|
||||||
|
<input type="text" class="form-control" id="short_label" name="short_label" maxlength="20" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="label" class="form-label fw-semibold">Label</label>
|
||||||
|
<input type="text" class="form-control" id="label" name="label" maxlength="100" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="icon" class="form-label fw-semibold">Bootstrap Icon class (optional)</label>
|
||||||
|
<input type="text" class="form-control" id="icon" name="icon" placeholder="es. bi-thermometer-half">
|
||||||
|
<small class="text-muted">Use a Bootstrap Icons class without the <code>bi</code> prefix (prefix is automatically added in preview).</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<button type="submit" class="btn btn-add" id="btnSaveParam">💾 Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('jsinclude.php'); ?>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
const table = $('#tabellaParametri').DataTable({
|
||||||
|
order: [
|
||||||
|
[1, 'asc']
|
||||||
|
],
|
||||||
|
pageLength: 50,
|
||||||
|
language: {
|
||||||
|
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const paramModal = new bootstrap.Modal(document.getElementById('paramModal'));
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
$('#param_id').val('');
|
||||||
|
$('#position').val('');
|
||||||
|
$('#short_label').val('');
|
||||||
|
$('#label').val('');
|
||||||
|
$('#icon').val('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open modal for new parameter
|
||||||
|
$('#btnAddParam').on('click', function() {
|
||||||
|
resetForm();
|
||||||
|
$('#paramModalLabel').text('Add Parameter');
|
||||||
|
$('#btnSaveParam').text('💾 Save');
|
||||||
|
paramModal.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Open modal for edit
|
||||||
|
$(document).on('click', '.edit', function() {
|
||||||
|
const id = $(this).data('id');
|
||||||
|
|
||||||
|
fetch('get_param.php?id=' + id)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
const p = data.param;
|
||||||
|
$('#param_id').val(p.id);
|
||||||
|
$('#line_id').val(p.line_id);
|
||||||
|
$('#position').val(p.position);
|
||||||
|
$('#short_label').val(p.short_label);
|
||||||
|
$('#label').val(p.label);
|
||||||
|
$('#icon').val(p.icon || '');
|
||||||
|
|
||||||
|
$('#paramModalLabel').text('Edit Parameter');
|
||||||
|
$('#btnSaveParam').text('💾 Update');
|
||||||
|
paramModal.show();
|
||||||
|
} else {
|
||||||
|
Swal.fire('Error', data.message || 'Unable to load parameter.', 'error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Swal.fire('Error', 'Server communication error.', 'error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save (add or update)
|
||||||
|
$('#paramForm').on('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const formData = new FormData(this);
|
||||||
|
|
||||||
|
fetch('save_param.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire('Saved', data.message || 'Parameter saved correctly.', 'success')
|
||||||
|
.then(() => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire('Error', data.message || 'Error while saving parameter.', 'error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Swal.fire('Error', 'Server communication error.', 'error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete parameter
|
||||||
|
$(document).on('click', '.delete', function() {
|
||||||
|
const id = $(this).data('id');
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Are you sure?',
|
||||||
|
text: 'This action will permanently delete the parameter.',
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Yes, delete',
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
confirmButtonColor: '#d33'
|
||||||
|
}).then(result => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
fetch('delete_param.php?id=' + id)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire('Deleted', data.message || 'Parameter deleted.', 'success')
|
||||||
|
.then(() => location.reload());
|
||||||
|
} else {
|
||||||
|
Swal.fire('Error', data.message || 'Error while deleting parameter.', 'error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Swal.fire('Error', 'Server communication error.', 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -84,6 +84,10 @@
|
|||||||
color: #128346;
|
color: #128346;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-action.params {
|
||||||
|
color: #6f42c1;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-action:hover {
|
.btn-action:hover {
|
||||||
transform: scale(1.15);
|
transform: scale(1.15);
|
||||||
}
|
}
|
||||||
@@ -114,7 +118,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="wrapper">
|
<div class="wrapper toggled">
|
||||||
<?php include('include/navbar.php'); ?>
|
<?php include('include/navbar.php'); ?>
|
||||||
<?php include('include/topbar.php'); ?>
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
@@ -134,7 +138,7 @@
|
|||||||
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addLineaModal">➕ Aggiungi Linea</button>
|
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addLineaModal">➕ Aggiungi Linea</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TABELLA -->
|
<!-- TABLE -->
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table id="tabellaLinee" class="table table-striped align-middle text-center" style="width:100%;">
|
<table id="tabellaLinee" class="table table-striped align-middle text-center" style="width:100%;">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -156,30 +160,42 @@
|
|||||||
$stmt = $pdo->query("SELECT * FROM production_lines ORDER BY line_number ASC");
|
$stmt = $pdo->query("SELECT * FROM production_lines ORDER BY line_number ASC");
|
||||||
|
|
||||||
if ($stmt->rowCount() === 0) {
|
if ($stmt->rowCount() === 0) {
|
||||||
echo "<tr><td colspan='7' class='text-muted'>Nessuna linea di produzione presente</td></tr>";
|
echo "<tr><td colspan='8' class='text-muted'>Nessuna linea di produzione presente</td></tr>";
|
||||||
} else {
|
} else {
|
||||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||||
$badge = $row['status'] === 'active'
|
$badge = $row['status'] === 'active'
|
||||||
? "<span class='badge-active'>Attiva</span>"
|
? "<span class='badge-active'>Attiva</span>"
|
||||||
: "<span class='badge-inactive'>Inattiva</span>";
|
: "<span class='badge-inactive'>Inattiva</span>";
|
||||||
|
|
||||||
|
$lineNameEsc = htmlspecialchars($row['name'], ENT_QUOTES, 'UTF-8');
|
||||||
|
|
||||||
echo "<tr>
|
echo "<tr>
|
||||||
<td>{$row['id']}</td>
|
<td>{$row['id']}</td>
|
||||||
<td>{$row['line_number']}</td>
|
<td>{$row['line_number']}</td>
|
||||||
<td>" . htmlspecialchars($row['name']) . "</td>
|
<td>{$lineNameEsc}</td>
|
||||||
<td>" . htmlspecialchars($row['model']) . "</td>
|
<td>" . htmlspecialchars($row['model']) . "</td>
|
||||||
<td>" . htmlspecialchars($row['brand']) . "</td>
|
<td>" . htmlspecialchars($row['brand']) . "</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<div style='width:28px; height:28px; border-radius:6px; border:1px solid #999; background: {$row['color']}; margin:auto;'></div>
|
<div style='width:28px; height:28px; border-radius:6px; border:1px solid #999; background: {$row['color']}; margin:auto;'></div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>{$badge}</td>
|
<td>{$badge}</td>
|
||||||
<td>
|
<td>
|
||||||
|
<button class='btn-action params' title='Parametri linea'
|
||||||
<button class='btn-action edit' title='Modifica' data-id='{$row['id']}'><i class='fas fa-edit'></i></button>
|
data-id='{$row['id']}'
|
||||||
<button class='btn-action delete' title='Elimina' data-id='{$row['id']}'><i class='fas fa-trash'></i></button>
|
data-name='{$lineNameEsc}'>
|
||||||
<button class='btn-action toggle' title='Cambia stato' data-id='{$row['id']}' data-status='{$row['status']}'><i class='fas fa-power-off'></i></button>
|
<i class='fas fa-sliders-h'></i>
|
||||||
|
</button>
|
||||||
|
<button class='btn-action edit' title='Modifica' data-id='{$row['id']}'>
|
||||||
|
<i class='fas fa-edit'></i>
|
||||||
|
</button>
|
||||||
|
<button class='btn-action delete' title='Elimina' data-id='{$row['id']}'>
|
||||||
|
<i class='fas fa-trash'></i>
|
||||||
|
</button>
|
||||||
|
<button class='btn-action toggle' title='Cambia stato'
|
||||||
|
data-id='{$row['id']}'
|
||||||
|
data-status='{$row['status']}'>
|
||||||
|
<i class='fas fa-power-off'></i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>";
|
</tr>";
|
||||||
}
|
}
|
||||||
@@ -196,7 +212,7 @@
|
|||||||
<?php include('include/footer.php'); ?>
|
<?php include('include/footer.php'); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- MODALE AGGIUNTA LINEA -->
|
<!-- ADD LINE MODAL -->
|
||||||
<div class="modal fade" id="addLineaModal" tabindex="-1" aria-labelledby="addLineaLabel" aria-hidden="true">
|
<div class="modal fade" id="addLineaModal" tabindex="-1" aria-labelledby="addLineaLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -250,7 +266,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ➕ Aggiungi linea
|
// ➕ Add line
|
||||||
$("#addLineaForm").on("submit", function(e) {
|
$("#addLineaForm").on("submit", function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const formData = new FormData(this);
|
const formData = new FormData(this);
|
||||||
@@ -271,13 +287,19 @@
|
|||||||
.catch(() => Swal.fire("Errore", "Impossibile contattare il server.", "error"));
|
.catch(() => Swal.fire("Errore", "Impossibile contattare il server.", "error"));
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✏️ Modifica
|
// ✏️ Edit line
|
||||||
$(document).on("click", ".edit", function() {
|
$(document).on("click", ".edit", function() {
|
||||||
const id = $(this).data("id");
|
const id = $(this).data("id");
|
||||||
location.href = "edit_linea.php?id=" + id;
|
location.href = "edit_linea.php?id=" + id;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 🗑️ Elimina
|
// 🧩 Parameters management
|
||||||
|
$(document).on("click", ".params", function() {
|
||||||
|
const id = $(this).data("id");
|
||||||
|
location.href = "line_params.php?line_id=" + id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 🗑️ Delete line
|
||||||
$(document).on("click", ".delete", function() {
|
$(document).on("click", ".delete", function() {
|
||||||
const id = $(this).data("id");
|
const id = $(this).data("id");
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
@@ -304,7 +326,7 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 🔘 Toggle stato
|
// 🔘 Toggle status
|
||||||
$(document).on("click", ".toggle", function() {
|
$(document).on("click", ".toggle", function() {
|
||||||
const id = $(this).data("id");
|
const id = $(this).data("id");
|
||||||
const currentStatus = $(this).data("status");
|
const currentStatus = $(this).data("status");
|
||||||
|
|||||||
@@ -0,0 +1,643 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ws_lookup_options manager page
|
||||||
|
* Table expected:
|
||||||
|
* - id (AI)
|
||||||
|
* - category (varchar)
|
||||||
|
* - value (varchar)
|
||||||
|
* - label (varchar)
|
||||||
|
* - sort_order (int)
|
||||||
|
* - is_default (tinyint 0/1)
|
||||||
|
* - is_active (tinyint 0/1)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// AJAX HANDLERS
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax']) && $_POST['ajax'] == '1') {
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if ($action === 'add') {
|
||||||
|
$category = trim($_POST['category'] ?? '');
|
||||||
|
$value = trim($_POST['value'] ?? '');
|
||||||
|
$label = trim($_POST['label'] ?? '');
|
||||||
|
$sort_order = (int)($_POST['sort_order'] ?? 100);
|
||||||
|
$is_default = (int)($_POST['is_default'] ?? 0) ? 1 : 0;
|
||||||
|
$is_active = (int)($_POST['is_active'] ?? 1) ? 1 : 0;
|
||||||
|
|
||||||
|
if ($category === '' || $value === '' || $label === '') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Compila category, value e label']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// If set as default, unset other defaults in same category (simple & reliable)
|
||||||
|
if ($is_default === 1) {
|
||||||
|
$stmt = $pdo->prepare("UPDATE ws_lookup_options SET is_default = 0 WHERE category = ?");
|
||||||
|
$stmt->execute([$category]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO ws_lookup_options
|
||||||
|
(category, value, label, sort_order, is_default, is_active)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
");
|
||||||
|
$stmt->execute([$category, $value, $label, $sort_order, $is_default, $is_active]);
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'edit') {
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
$category = trim($_POST['category'] ?? '');
|
||||||
|
$value = trim($_POST['value'] ?? '');
|
||||||
|
$label = trim($_POST['label'] ?? '');
|
||||||
|
$sort_order = (int)($_POST['sort_order'] ?? 100);
|
||||||
|
$is_default = (int)($_POST['is_default'] ?? 0) ? 1 : 0;
|
||||||
|
$is_active = (int)($_POST['is_active'] ?? 1) ? 1 : 0;
|
||||||
|
|
||||||
|
if ($id <= 0 || $category === '' || $value === '' || $label === '') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Dati non validi']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// If set as default, unset other defaults in same category (excluding current)
|
||||||
|
if ($is_default === 1) {
|
||||||
|
$stmt = $pdo->prepare("UPDATE ws_lookup_options SET is_default = 0 WHERE category = ? AND id <> ?");
|
||||||
|
$stmt->execute([$category, $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
UPDATE ws_lookup_options SET
|
||||||
|
category = ?,
|
||||||
|
value = ?,
|
||||||
|
label = ?,
|
||||||
|
sort_order = ?,
|
||||||
|
is_default = ?,
|
||||||
|
is_active = ?
|
||||||
|
WHERE id = ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$category, $value, $label, $sort_order, $is_default, $is_active, $id]);
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'delete') {
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID non valido']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM ws_lookup_options WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Azione sconosciuta']);
|
||||||
|
exit;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Categories list (static for now, easy to extend)
|
||||||
|
$categories = [
|
||||||
|
'marking' => 'Marchiatura',
|
||||||
|
'lubrication_type' => 'Lubrificazione',
|
||||||
|
'control_frequency_cut' => 'Frequenza controllo (taglio)',
|
||||||
|
'control_frequency_drawing' => 'Frequenza controllo disegno',
|
||||||
|
'control_frequency_jig' => 'Frequenza controllo in dima',
|
||||||
|
'control_mode_jig' => 'Modalità controllo in dima',
|
||||||
|
'box_type' => 'Tipo scatola',
|
||||||
|
'pallet_type' => 'Tipo bancale',
|
||||||
|
'requested_package_code' => 'Confezione richiesta',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Selected category filter
|
||||||
|
$categoryFilter = trim($_GET['cat'] ?? '');
|
||||||
|
if ($categoryFilter !== '' && !array_key_exists($categoryFilter, $categories)) {
|
||||||
|
$categoryFilter = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load options
|
||||||
|
$params = [];
|
||||||
|
$sql = "
|
||||||
|
SELECT id, category, value, label, sort_order, is_default, is_active, created_at, updated_at
|
||||||
|
FROM ws_lookup_options
|
||||||
|
";
|
||||||
|
if ($categoryFilter !== '') {
|
||||||
|
$sql .= " WHERE category = ? ";
|
||||||
|
$params[] = $categoryFilter;
|
||||||
|
}
|
||||||
|
$sql .= " ORDER BY category ASC, sort_order ASC, label ASC, id ASC ";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$options = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
function h($v)
|
||||||
|
{
|
||||||
|
return htmlspecialchars((string)$v, ENT_QUOTES);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<?php include('cssinclude.php'); ?>
|
||||||
|
<title>Lookup Worksheet</title>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
||||||
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||||
|
|
||||||
|
<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/@ttskch/select2-bootstrap4-theme@1.5.2/dist/select2-bootstrap4.min.css" rel="stylesheet" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-dashboard {
|
||||||
|
background: #cfe3ff !important;
|
||||||
|
color: #1f2d3d !important;
|
||||||
|
border: 1px solid #bcd4f4 !important;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 10px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add {
|
||||||
|
background: #0d6efd;
|
||||||
|
color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead {
|
||||||
|
background: #cfe3ff;
|
||||||
|
color: #1f2d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-hint {
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select2 sizing alignment */
|
||||||
|
.select2-container .select2-selection--single {
|
||||||
|
height: calc(2.375rem + 2px);
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||||
|
line-height: 1.6;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||||
|
height: calc(2.375rem + 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-soft {
|
||||||
|
background: #eef6ff;
|
||||||
|
color: #1f2d3d;
|
||||||
|
border: 1px solid #bcd4f4;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrapper toggled">
|
||||||
|
<?php include('include/navbar.php'); ?>
|
||||||
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="card p-3">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<h5 class="mb-0">Lookup Worksheet</h5>
|
||||||
|
<div class="small-hint">Gestione valori predefiniti (tendine) per i campi dei fogli di lavoro</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn back-dashboard" onclick="location.href='production_dashboard.php'">↩️ Dashboard</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<div class="d-flex align-items-center gap-2" style="min-width: 420px;">
|
||||||
|
<div class="fw-semibold">Categoria</div>
|
||||||
|
<select id="categoryFilter" class="form-select">
|
||||||
|
<option value="">-- Tutte --</option>
|
||||||
|
<?php foreach ($categories as $key => $label): ?>
|
||||||
|
<option value="<?= h($key) ?>" <?= $categoryFilter === $key ? 'selected' : '' ?>>
|
||||||
|
<?= h($label) ?> (<?= h($key) ?>)
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addModal">
|
||||||
|
➕ Aggiungi valore
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="tabellaLookup" class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Category</th>
|
||||||
|
<th>Value</th>
|
||||||
|
<th>Label</th>
|
||||||
|
<th>Ordine</th>
|
||||||
|
<th>Default</th>
|
||||||
|
<th>Attivo</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($options as $o): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= (int)$o['id'] ?></td>
|
||||||
|
<td>
|
||||||
|
<span class="badge badge-soft"><?= h($o['category']) ?></span>
|
||||||
|
<div class="small-hint">
|
||||||
|
<?= h($categories[$o['category']] ?? '-') ?>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="fw-semibold"><?= h($o['value']) ?></td>
|
||||||
|
<td><?= h($o['label']) ?></td>
|
||||||
|
<td><?= (int)$o['sort_order'] ?></td>
|
||||||
|
<td><?= (int)$o['is_default'] === 1 ? '✅' : '-' ?></td>
|
||||||
|
<td><?= (int)$o['is_active'] === 1 ? '✅' : '⛔' ?></td>
|
||||||
|
<td class="text-nowrap">
|
||||||
|
<button class="btn btn-sm btn-outline-primary edit-row"
|
||||||
|
data-id="<?= (int)$o['id'] ?>"
|
||||||
|
data-category="<?= h($o['category']) ?>"
|
||||||
|
data-value="<?= h($o['value']) ?>"
|
||||||
|
data-label="<?= h($o['label']) ?>"
|
||||||
|
data-sort_order="<?= (int)$o['sort_order'] ?>"
|
||||||
|
data-is_default="<?= (int)$o['is_default'] ?>"
|
||||||
|
data-is_active="<?= (int)$o['is_active'] ?>">
|
||||||
|
✏️ Modifica
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-outline-danger delete-row"
|
||||||
|
data-id="<?= (int)$o['id'] ?>"
|
||||||
|
data-name="<?= h($o['category'] . ' / ' . $o['value']) ?>">
|
||||||
|
🗑️ Elimina
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small-hint mt-2">
|
||||||
|
Suggerimento: imposta <b>Default</b> solo per 1 valore per categoria (questa pagina lo gestisce automaticamente).
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MODAL ADD -->
|
||||||
|
<div class="modal fade" id="addModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background:#cfe3ff;">
|
||||||
|
<h5 class="modal-title">Aggiungi valore</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="addForm">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">Categoria *</label>
|
||||||
|
<select class="form-select" name="category" id="add_category" required>
|
||||||
|
<option value="">-- Seleziona --</option>
|
||||||
|
<?php foreach ($categories as $key => $label): ?>
|
||||||
|
<option value="<?= h($key) ?>"><?= h($label) ?> (<?= h($key) ?>)</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<div class="small-hint">Categoria = campo di destinazione nel worksheet</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">Value *</label>
|
||||||
|
<input type="text" class="form-control" name="value" placeholder="es. S, N, EUR, EACH_COIL" required>
|
||||||
|
<div class="small-hint">Valore tecnico stabile (quello che salvi nel DB)</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label class="form-label fw-semibold">Label *</label>
|
||||||
|
<input type="text" class="form-control" name="label" placeholder="Testo visibile in tendina" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label fw-semibold">Ordine</label>
|
||||||
|
<input type="number" class="form-control" name="sort_order" value="100" min="1">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">Default</label>
|
||||||
|
<select class="form-select" name="is_default">
|
||||||
|
<option value="0" selected>No</option>
|
||||||
|
<option value="1">Sì</option>
|
||||||
|
</select>
|
||||||
|
<div class="small-hint">Se “Sì”, gli altri default della stessa categoria verranno disattivati</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">Attivo</label>
|
||||||
|
<select class="form-select" name="is_active">
|
||||||
|
<option value="1" selected>Sì</option>
|
||||||
|
<option value="0">No</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 text-center mt-2">
|
||||||
|
<button type="submit" class="btn btn-add">💾 Salva</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MODAL EDIT -->
|
||||||
|
<div class="modal fade" id="editModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background:#cfe3ff;">
|
||||||
|
<h5 class="modal-title">Modifica valore</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="editForm">
|
||||||
|
<input type="hidden" name="id" id="edit_id">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">Categoria *</label>
|
||||||
|
<select class="form-select" name="category" id="edit_category" required>
|
||||||
|
<option value="">-- Seleziona --</option>
|
||||||
|
<?php foreach ($categories as $key => $label): ?>
|
||||||
|
<option value="<?= h($key) ?>"><?= h($label) ?> (<?= h($key) ?>)</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">Value *</label>
|
||||||
|
<input type="text" class="form-control" name="value" id="edit_value" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label class="form-label fw-semibold">Label *</label>
|
||||||
|
<input type="text" class="form-control" name="label" id="edit_label" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label fw-semibold">Ordine</label>
|
||||||
|
<input type="number" class="form-control" name="sort_order" id="edit_sort_order" min="1">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">Default</label>
|
||||||
|
<select class="form-select" name="is_default" id="edit_is_default">
|
||||||
|
<option value="0">No</option>
|
||||||
|
<option value="1">Sì</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">Attivo</label>
|
||||||
|
<select class="form-select" name="is_active" id="edit_is_active">
|
||||||
|
<option value="1">Sì</option>
|
||||||
|
<option value="0">No</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 text-center mt-2">
|
||||||
|
<button type="submit" class="btn btn-add">💾 Salva modifiche</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('jsinclude.php'); ?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
// DataTable
|
||||||
|
$('#tabellaLookup').DataTable({
|
||||||
|
pageLength: 50,
|
||||||
|
lengthMenu: [10, 25, 50, 100],
|
||||||
|
order: [
|
||||||
|
[1, 'asc'],
|
||||||
|
[4, 'asc'],
|
||||||
|
[3, 'asc']
|
||||||
|
],
|
||||||
|
language: {
|
||||||
|
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select2 for category filter + modal selects
|
||||||
|
$('#categoryFilter, #add_category, #edit_category').select2({
|
||||||
|
theme: 'bootstrap4',
|
||||||
|
width: '100%',
|
||||||
|
placeholder: '-- Seleziona --',
|
||||||
|
allowClear: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter redirect
|
||||||
|
$('#categoryFilter').on('change', function() {
|
||||||
|
const v = $(this).val() || '';
|
||||||
|
const base = window.location.pathname.split('/').pop();
|
||||||
|
if (v === '') {
|
||||||
|
window.location.href = base;
|
||||||
|
} else {
|
||||||
|
window.location.href = base + '?cat=' + encodeURIComponent(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add
|
||||||
|
$('#addForm').on('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(this);
|
||||||
|
formData.append('ajax', '1');
|
||||||
|
formData.append('action', 'add');
|
||||||
|
|
||||||
|
fetch('', {
|
||||||
|
method: 'POST',
|
||||||
|
body: new URLSearchParams(formData)
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Salvato!',
|
||||||
|
timer: 900
|
||||||
|
})
|
||||||
|
.then(() => location.reload());
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Errore',
|
||||||
|
text: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Open edit modal
|
||||||
|
$(document).on('click', '.edit-row', function() {
|
||||||
|
const btn = $(this);
|
||||||
|
|
||||||
|
$('#edit_id').val(btn.data('id'));
|
||||||
|
$('#edit_category').val(btn.data('category')).trigger('change');
|
||||||
|
$('#edit_value').val(btn.data('value'));
|
||||||
|
$('#edit_label').val(btn.data('label'));
|
||||||
|
$('#edit_sort_order').val(btn.data('sort_order'));
|
||||||
|
$('#edit_is_default').val(String(btn.data('is_default')));
|
||||||
|
$('#edit_is_active').val(String(btn.data('is_active')));
|
||||||
|
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('editModal'));
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save edit
|
||||||
|
$('#editForm').on('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(this);
|
||||||
|
formData.append('ajax', '1');
|
||||||
|
formData.append('action', 'edit');
|
||||||
|
|
||||||
|
fetch('', {
|
||||||
|
method: 'POST',
|
||||||
|
body: new URLSearchParams(formData)
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Aggiornato!',
|
||||||
|
timer: 900
|
||||||
|
})
|
||||||
|
.then(() => location.reload());
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Errore',
|
||||||
|
text: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
$(document).on('click', '.delete-row', function() {
|
||||||
|
const id = $(this).data('id');
|
||||||
|
const name = $(this).data('name');
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Confermi eliminazione?',
|
||||||
|
text: name,
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#d33',
|
||||||
|
cancelButtonColor: '#6c757d',
|
||||||
|
confirmButtonText: 'Sì, elimina',
|
||||||
|
cancelButtonText: 'Annulla'
|
||||||
|
}).then(result => {
|
||||||
|
if (!result.isConfirmed) return;
|
||||||
|
|
||||||
|
fetch('', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: `ajax=1&action=delete&id=${id}`
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Eliminato!',
|
||||||
|
timer: 900
|
||||||
|
})
|
||||||
|
.then(() => location.reload());
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Errore',
|
||||||
|
text: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -12,6 +12,7 @@ $sql = "
|
|||||||
SELECT
|
SELECT
|
||||||
p.*,
|
p.*,
|
||||||
m.nome AS matrice,
|
m.nome AS matrice,
|
||||||
|
m.photo AS matrice_photo,
|
||||||
l.name AS linea,
|
l.name AS linea,
|
||||||
c.nome AS cliente,
|
c.nome AS cliente,
|
||||||
s.nome AS status_nome,
|
s.nome AS status_nome,
|
||||||
@@ -222,7 +223,20 @@ $rows_special = array_filter($rows, function ($r) {
|
|||||||
data-seconds="<?= $sec ?>"
|
data-seconds="<?= $sec ?>"
|
||||||
style="--rowcolor: <?= htmlspecialchars($r['line_color']) ?>;">
|
style="--rowcolor: <?= htmlspecialchars($r['line_color']) ?>;">
|
||||||
<td><?= htmlspecialchars($r['conferma_ordine'] ?? '') ?></td>
|
<td><?= htmlspecialchars($r['conferma_ordine'] ?? '') ?></td>
|
||||||
<td><?= htmlspecialchars($r['matrice']) ?></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>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$mescRaw = $r['mescole_list'] ?? '';
|
$mescRaw = $r['mescole_list'] ?? '';
|
||||||
$mescArray = $mescRaw !== '' ? explode(' | ', $mescRaw) : [];
|
$mescArray = $mescRaw !== '' ? explode(' | ', $mescRaw) : [];
|
||||||
@@ -338,7 +352,20 @@ $rows_special = array_filter($rows, function ($r) {
|
|||||||
data-status="<?= (int)$r['id_status'] ?>"
|
data-status="<?= (int)$r['id_status'] ?>"
|
||||||
style="background-color: <?= htmlspecialchars($r['line_color']) ?>;">
|
style="background-color: <?= htmlspecialchars($r['line_color']) ?>;">
|
||||||
<td><?= htmlspecialchars($r['conferma_ordine'] ?? '') ?></td>
|
<td><?= htmlspecialchars($r['conferma_ordine'] ?? '') ?></td>
|
||||||
<td><?= htmlspecialchars($r['matrice']) ?></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>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$mescRaw = $r['mescole_list'] ?? '';
|
$mescRaw = $r['mescole_list'] ?? '';
|
||||||
$mescArray = $mescRaw !== '' ? explode(' | ', $mescRaw) : [];
|
$mescArray = $mescRaw !== '' ? explode(' | ', $mescRaw) : [];
|
||||||
@@ -565,6 +592,19 @@ $rows_special = array_filter($rows, function ($r) {
|
|||||||
$("#photoModal").css("display", "flex");
|
$("#photoModal").css("display", "flex");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ❌ Chiudi modale foto (delegato)
|
||||||
|
$(document).on("click", "#photoModalCloseX, #photoCancel", function() {
|
||||||
|
$("#photoModal").hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ❌ Chiudi cliccando sul backdrop (delegato)
|
||||||
|
$(document).on("click", "#photoModal", function(e) {
|
||||||
|
if (e.target.id === "photoModal") {
|
||||||
|
$("#photoModal").hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
$("#photoModalCloseX, #photoCancel").on("click", function() {
|
$("#photoModalCloseX, #photoCancel").on("click", function() {
|
||||||
$("#photoModal").hide();
|
$("#photoModal").hide();
|
||||||
});
|
});
|
||||||
@@ -610,16 +650,19 @@ $rows_special = array_filter($rows, function ($r) {
|
|||||||
$("#imagePreviewModal").css("display", "flex");
|
$("#imagePreviewModal").css("display", "flex");
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#previewCloseX").on("click", function() {
|
// ❌ Chiudi preview con la X (delegato)
|
||||||
|
$(document).on("click", "#previewCloseX", function() {
|
||||||
$("#imagePreviewModal").hide();
|
$("#imagePreviewModal").hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#imagePreviewModal").on("click", function(e) {
|
// ❌ Chiudi cliccando fuori (delegato)
|
||||||
|
$(document).on("click", "#imagePreviewModal", function(e) {
|
||||||
if (e.target.id === "imagePreviewModal") {
|
if (e.target.id === "imagePreviewModal") {
|
||||||
$("#imagePreviewModal").hide();
|
$("#imagePreviewModal").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// ===== MODALE MESCOLE (Multi) =====
|
// ===== MODALE MESCOLE (Multi) =====
|
||||||
$(document).on("click", ".showMescole", function(e) {
|
$(document).on("click", ".showMescole", function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -79,11 +79,66 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- FIX colonne lunghe: tronca con ellissi --- */
|
||||||
|
#tabellaMescole {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabellaMescole th,
|
||||||
|
#tabellaMescole td {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ID */
|
||||||
|
#tabellaMescole th:nth-child(1),
|
||||||
|
#tabellaMescole td:nth-child(1) {
|
||||||
|
width: 70px;
|
||||||
|
max-width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nome Uscita */
|
||||||
|
#tabellaMescole th:nth-child(2),
|
||||||
|
#tabellaMescole td:nth-child(2) {
|
||||||
|
width: 360px;
|
||||||
|
max-width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Q.tà totale */
|
||||||
|
#tabellaMescole th:nth-child(3),
|
||||||
|
#tabellaMescole td:nth-child(3) {
|
||||||
|
width: 140px;
|
||||||
|
max-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Linee */
|
||||||
|
#tabellaMescole th:nth-child(4),
|
||||||
|
#tabellaMescole td:nth-child(4) {
|
||||||
|
width: 260px;
|
||||||
|
max-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stato */
|
||||||
|
#tabellaMescole th:nth-child(5),
|
||||||
|
#tabellaMescole td:nth-child(5) {
|
||||||
|
width: 140px;
|
||||||
|
max-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Azioni */
|
||||||
|
#tabellaMescole th:nth-child(6),
|
||||||
|
#tabellaMescole td:nth-child(6) {
|
||||||
|
width: 330px;
|
||||||
|
max-width: 330px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="wrapper">
|
<div class="wrapper toggled">
|
||||||
<?php include('include/navbar.php'); ?>
|
<?php include('include/navbar.php'); ?>
|
||||||
<?php include('include/topbar.php'); ?>
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
@@ -99,7 +154,16 @@
|
|||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
<h6 class="fw-semibold mb-0">Elenco Completo</h6>
|
<h6 class="fw-semibold mb-0">Elenco Completo</h6>
|
||||||
|
|
||||||
|
<select id="filterActive" class="form-select form-select-sm" style="width:220px;">
|
||||||
|
<option value="all">Tutte</option>
|
||||||
|
<option value="1" selected>Solo Attive</option>
|
||||||
|
<option value="0">Solo Inattive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addMescolaModal">➕ Aggiungi Mescola</button>
|
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addMescolaModal">➕ Aggiungi Mescola</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -109,47 +173,105 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>Nome Mescola</th>
|
|
||||||
<th>Nome Uscita</th>
|
<th>Nome Uscita</th>
|
||||||
|
<th>Q.tà Totale</th>
|
||||||
<th>Linee Associate</th>
|
<th>Linee Associate</th>
|
||||||
|
<th>Stato</th>
|
||||||
<th>Azioni</th>
|
<th>Azioni</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// filtro: all / 1 / 0
|
||||||
|
$activeFilter = $_GET['active'] ?? '1';
|
||||||
|
if (!in_array($activeFilter, ['all', '0', '1'], true)) {
|
||||||
|
$activeFilter = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
$where = "";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if ($activeFilter !== 'all') {
|
||||||
|
$where = "WHERE m.is_active = ?";
|
||||||
|
$params[] = (int)$activeFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// QTY IN SUBQUERY per evitare raddoppi dovuti alle linee
|
||||||
$sql = "
|
$sql = "
|
||||||
SELECT m.id, m.nome, m.nomeuscita,
|
SELECT
|
||||||
GROUP_CONCAT(pl.name SEPARATOR ', ') AS linee
|
m.id,
|
||||||
|
m.nomeuscita,
|
||||||
|
m.is_active,
|
||||||
|
IFNULL(q.qty_totale, 0) AS qty_totale,
|
||||||
|
GROUP_CONCAT(DISTINCT pl.name SEPARATOR ', ') AS linee
|
||||||
FROM mescole m
|
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 mescole_lines ml ON m.id = ml.idmescola
|
||||||
LEFT JOIN production_lines pl ON ml.idlinea = pl.id
|
LEFT JOIN production_lines pl ON ml.idlinea = pl.id
|
||||||
|
$where
|
||||||
GROUP BY m.id
|
GROUP BY m.id
|
||||||
ORDER BY m.id DESC";
|
ORDER BY m.id DESC
|
||||||
|
";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
|
||||||
$stmt = $pdo->query($sql);
|
|
||||||
if ($stmt->rowCount() === 0) {
|
if ($stmt->rowCount() === 0) {
|
||||||
echo "<tr><td colspan='5' class='text-muted'>Nessuna mescola presente</td></tr>";
|
// DataTables-friendly: 6 TD reali
|
||||||
|
echo "<tr>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>Nessuna mescola presente</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
</tr>";
|
||||||
} else {
|
} else {
|
||||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||||
$linee = $row['linee'] ? htmlspecialchars($row['linee']) : '<span class="text-muted">Nessuna</span>';
|
$linee = $row['linee'] ? htmlspecialchars($row['linee']) : '<span class="text-muted">Nessuna</span>';
|
||||||
|
$qtyTot = number_format((float)$row['qty_totale'], 3, ',', '.');
|
||||||
|
|
||||||
echo "<tr>
|
$isActive = (int)$row['is_active'] === 1;
|
||||||
|
$badge = $isActive
|
||||||
|
? "<span class='badge bg-success'>Attiva</span>"
|
||||||
|
: "<span class='badge bg-secondary'>Inattiva</span>";
|
||||||
|
|
||||||
|
$toggleText = $isActive ? "Disattiva" : "Attiva";
|
||||||
|
$toggleClass = $isActive ? "btn-outline-warning" : "btn-outline-success";
|
||||||
|
|
||||||
|
echo "<tr data-mescola-id='{$row['id']}'>
|
||||||
<td>{$row['id']}</td>
|
<td>{$row['id']}</td>
|
||||||
<td>" . htmlspecialchars($row['nome']) . "</td>
|
|
||||||
<td>" . htmlspecialchars($row['nomeuscita']) . "</td>
|
<td>" . htmlspecialchars($row['nomeuscita']) . "</td>
|
||||||
|
<td><span class='fw-semibold'>{$qtyTot}</span></td>
|
||||||
<td>{$linee}</td>
|
<td>{$linee}</td>
|
||||||
|
<td>{$badge}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class='btn btn-sm btn-outline-primary associa-linee'
|
<button class='btn btn-sm btn-outline-dark associa-fornitori'
|
||||||
data-id='{$row['id']}'
|
data-id='{$row['id']}'
|
||||||
data-nome='" . htmlspecialchars($row['nome'], ENT_QUOTES) . "'>
|
data-nomeuscita='" . htmlspecialchars($row['nomeuscita'], ENT_QUOTES) . "'>
|
||||||
⚙️ Associa Linee
|
🧾 Fornitori
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class='btn btn-sm btn-outline-primary associa-linee'
|
||||||
|
data-id='{$row['id']}'>
|
||||||
|
⚙️ Linee
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class='btn btn-sm {$toggleClass} toggle-active'
|
||||||
|
data-id='{$row['id']}'>
|
||||||
|
🔁 {$toggleText}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class='btn btn-sm btn-outline-secondary edit-mescola'
|
<button class='btn btn-sm btn-outline-secondary edit-mescola'
|
||||||
data-id='{$row['id']}'
|
data-id='{$row['id']}'
|
||||||
data-nome='" . htmlspecialchars($row['nome'], ENT_QUOTES) . "'
|
|
||||||
data-nomeuscita='" . htmlspecialchars($row['nomeuscita'], ENT_QUOTES) . "'>
|
data-nomeuscita='" . htmlspecialchars($row['nomeuscita'], ENT_QUOTES) . "'>
|
||||||
✏️ Modifica
|
✏️ Modifica
|
||||||
</button>
|
</button>
|
||||||
@@ -182,8 +304,8 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form id="addMescolaForm">
|
<form id="addMescolaForm">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label fw-semibold">Nome Mescola</label>
|
<label class="form-label fw-semibold">Nome Mescola (interno)</label>
|
||||||
<input type="text" class="form-control" id="nomeMescola" required>
|
<input type="text" class="form-control" id="nomeMescola" placeholder="opzionale / interno">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -215,8 +337,8 @@
|
|||||||
<input type="hidden" id="editIdMescola">
|
<input type="hidden" id="editIdMescola">
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label fw-semibold">Nome Mescola</label>
|
<label class="form-label fw-semibold">Nome Mescola (interno)</label>
|
||||||
<input type="text" class="form-control" id="editNomeMescola" required>
|
<input type="text" class="form-control" id="editNomeMescola" placeholder="opzionale / interno">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -261,11 +383,79 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- MODALE ASSOCIA FORNITORI / LOTTI -->
|
||||||
|
<div class="modal fade" id="associaFornitoriModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background-color:#cfe3ff;">
|
||||||
|
<h5 class="modal-title">Fornitori / Lotti - <span id="afNomeUscita"></span></h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="afIdMescola">
|
||||||
|
<input type="hidden" id="afEditId">
|
||||||
|
|
||||||
|
<div class="row g-2 align-items-end mb-3">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label fw-semibold">Fornitore</label>
|
||||||
|
<select class="form-select" id="afIdSupplier" style="width:100%;"></select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label fw-semibold">Nome mescola fornitore</label>
|
||||||
|
<input type="text" class="form-control" id="afSupplierMixName" placeholder="Nome specifico fornitore">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label fw-semibold">Lotto</label>
|
||||||
|
<input type="text" class="form-control" id="afLotCode" placeholder="LOT-...">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label fw-semibold">Scadenza</label>
|
||||||
|
<input type="date" class="form-control" id="afExpiryDate">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label fw-semibold">Q.tà</label>
|
||||||
|
<input type="number" step="0.001" class="form-control" id="afQty" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 text-end">
|
||||||
|
<button class="btn btn-add" id="afSaveBtn">➕ Aggiungi Riga</button>
|
||||||
|
<button class="btn btn-outline-secondary ms-2" id="afCancelEdit" type="button" style="display:none;">Annulla modifica</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped align-middle text-center" id="afTable">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Fornitore</th>
|
||||||
|
<th>Nome mescola fornitore</th>
|
||||||
|
<th>Lotto</th>
|
||||||
|
<th>Scadenza</th>
|
||||||
|
<th>Q.tà</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php include('jsinclude.php'); ?>
|
<?php include('jsinclude.php'); ?>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* ----------------- DATATABLE ----------------- */
|
/* ----------------- DATATABLE ----------------- */
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
// init dropdown from URL
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const activeParam = urlParams.get('active') || '1';
|
||||||
|
$('#filterActive').val(activeParam);
|
||||||
|
|
||||||
|
// datatable
|
||||||
$('#tabellaMescole').DataTable({
|
$('#tabellaMescole').DataTable({
|
||||||
order: [
|
order: [
|
||||||
[0, 'desc']
|
[0, 'desc']
|
||||||
@@ -275,6 +465,39 @@
|
|||||||
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
|
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// filter reload
|
||||||
|
$('#filterActive').on('change', function() {
|
||||||
|
const v = $(this).val();
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.searchParams.set('active', v);
|
||||||
|
window.location.href = url.toString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------- TOGGLE ACTIVE -------- */
|
||||||
|
$(document).on('click', '.toggle-active', function() {
|
||||||
|
const id = $(this).data('id');
|
||||||
|
|
||||||
|
fetch('toggle_mescola_active.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: `id=${encodeURIComponent(id)}`
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Errore",
|
||||||
|
text: data.message || "Operazione non riuscita"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -------- AGGIUNTA MESCOLA -------- */
|
/* -------- AGGIUNTA MESCOLA -------- */
|
||||||
@@ -304,7 +527,7 @@
|
|||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: "error",
|
icon: "error",
|
||||||
title: "Errore",
|
title: "Errore",
|
||||||
text: data.message
|
text: data.message || "Errore salvataggio"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -313,8 +536,8 @@
|
|||||||
/* -------- APERTURA MODALE EDIT -------- */
|
/* -------- APERTURA MODALE EDIT -------- */
|
||||||
$(document).on("click", ".edit-mescola", function() {
|
$(document).on("click", ".edit-mescola", function() {
|
||||||
$("#editIdMescola").val($(this).data("id"));
|
$("#editIdMescola").val($(this).data("id"));
|
||||||
$("#editNomeMescola").val($(this).data("nome"));
|
|
||||||
$("#editNomeUscita").val($(this).data("nomeuscita"));
|
$("#editNomeUscita").val($(this).data("nomeuscita"));
|
||||||
|
$("#editNomeMescola").val("");
|
||||||
$("#editMescolaModal").modal("show");
|
$("#editMescolaModal").modal("show");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -331,7 +554,7 @@
|
|||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded"
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
},
|
},
|
||||||
body: `id=${id}&nome=${encodeURIComponent(nome)}&nomeuscita=${encodeURIComponent(nomeuscita)}`
|
body: `id=${encodeURIComponent(id)}&nome=${encodeURIComponent(nome)}&nomeuscita=${encodeURIComponent(nomeuscita)}`
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
@@ -346,7 +569,7 @@
|
|||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: "error",
|
icon: "error",
|
||||||
title: "Errore",
|
title: "Errore",
|
||||||
text: data.message
|
text: data.message || "Errore aggiornamento"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -354,11 +577,10 @@
|
|||||||
|
|
||||||
/* -------- MODALE ASSOCIA LINEE -------- */
|
/* -------- MODALE ASSOCIA LINEE -------- */
|
||||||
$(document).on("click", ".associa-linee", function() {
|
$(document).on("click", ".associa-linee", function() {
|
||||||
|
|
||||||
const idMescola = $(this).data("id");
|
const idMescola = $(this).data("id");
|
||||||
$("#idMescolaLinee").val(idMescola);
|
$("#idMescolaLinee").val(idMescola);
|
||||||
|
|
||||||
fetch("get_linee_mescola.php?id=" + idMescola)
|
fetch("get_linee_mescola.php?id=" + encodeURIComponent(idMescola))
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const select = $("#lineeSelect");
|
const select = $("#lineeSelect");
|
||||||
@@ -407,11 +629,260 @@
|
|||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: "error",
|
icon: "error",
|
||||||
title: "Errore",
|
title: "Errore",
|
||||||
text: data.message
|
text: data.message || "Errore salvataggio"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// FORNITORI / LOTTI
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
function afResetForm() {
|
||||||
|
$("#afEditId").val("");
|
||||||
|
$("#afIdSupplier").val("").trigger("change");
|
||||||
|
$("#afSupplierMixName").val("");
|
||||||
|
$("#afLotCode").val("");
|
||||||
|
$("#afExpiryDate").val("");
|
||||||
|
$("#afQty").val("0");
|
||||||
|
$("#afSaveBtn").text("➕ Aggiungi Riga");
|
||||||
|
$("#afCancelEdit").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
function afSetSupplierValue(supplierId) {
|
||||||
|
const sel = $("#afIdSupplier");
|
||||||
|
const normalizedId = String(supplierId || "").trim();
|
||||||
|
|
||||||
|
if (normalizedId === "") {
|
||||||
|
sel.val("").trigger("change");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sel.find('option[value="' + normalizedId + '"]').length > 0) {
|
||||||
|
sel.val(normalizedId).trigger("change");
|
||||||
|
} else {
|
||||||
|
sel.val("").trigger("change");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function afLoadSuppliers(selectedId = "") {
|
||||||
|
return fetch("get_suppliers.php")
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
const sel = $("#afIdSupplier");
|
||||||
|
|
||||||
|
if (sel.hasClass("select2-hidden-accessible")) {
|
||||||
|
sel.select2("destroy");
|
||||||
|
}
|
||||||
|
|
||||||
|
sel.empty();
|
||||||
|
sel.append(`<option value="">Seleziona...</option>`);
|
||||||
|
|
||||||
|
if (data.success && Array.isArray(data.rows)) {
|
||||||
|
data.rows.forEach(s => {
|
||||||
|
const value = String(s.idsupplier);
|
||||||
|
sel.append(`<option value="${value}">${s.supplier_name}</option>`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sel.select2({
|
||||||
|
theme: "bootstrap-5",
|
||||||
|
width: "100%",
|
||||||
|
dropdownParent: $("#associaFornitoriModal")
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selectedId !== "") {
|
||||||
|
sel.val(String(selectedId)).trigger("change");
|
||||||
|
} else {
|
||||||
|
sel.val("").trigger("change");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function afLoadRows(idMescola) {
|
||||||
|
fetch("get_mescola_supplier_lots.php?id=" + encodeURIComponent(idMescola))
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log("ROWS LOTTI:", data);
|
||||||
|
|
||||||
|
const tbody = $("#afTable tbody");
|
||||||
|
tbody.empty();
|
||||||
|
|
||||||
|
if (!data.success || !Array.isArray(data.rows) || data.rows.length === 0) {
|
||||||
|
tbody.append(`<tr>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">Nessuna associazione presente</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
</tr>`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.rows.forEach(row => {
|
||||||
|
const exp = row.expiry_date ? row.expiry_date : "";
|
||||||
|
const supplierId = String(row.idsupplier ?? row.id_supplier ?? row.supplier_id ?? row.supplierId ?? "");
|
||||||
|
tbody.append(`
|
||||||
|
<tr>
|
||||||
|
<td>${row.id}</td>
|
||||||
|
<td>${row.supplier_name}</td>
|
||||||
|
<td>${row.supplier_mix_name}</td>
|
||||||
|
<td>${row.lot_code ?? ""}</td>
|
||||||
|
<td>${exp}</td>
|
||||||
|
<td>${row.qty}</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary af-edit"
|
||||||
|
data-id="${row.id}"
|
||||||
|
data-idsupplier="${supplierId}"
|
||||||
|
data-mix="${String(row.supplier_mix_name).replace(/"/g, '"')}"
|
||||||
|
data-lot="${String(row.lot_code ?? '').replace(/"/g, '"')}"
|
||||||
|
data-exp="${exp}"
|
||||||
|
data-qty="${row.qty}">
|
||||||
|
✏️
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger af-del" data-id="${row.id}">🗑️</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open modal
|
||||||
|
$(document).on("click", ".associa-fornitori", function() {
|
||||||
|
const idMescola = $(this).data("id");
|
||||||
|
const nomeUscita = $(this).data("nomeuscita");
|
||||||
|
|
||||||
|
$("#afIdMescola").val(idMescola);
|
||||||
|
$("#afNomeUscita").text(nomeUscita);
|
||||||
|
|
||||||
|
afResetForm();
|
||||||
|
|
||||||
|
afLoadSuppliers("")
|
||||||
|
.then(() => {
|
||||||
|
$("#associaFornitoriModal").modal("show");
|
||||||
|
afLoadRows(idMescola);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Edit row in modal
|
||||||
|
$(document).on("click", ".af-edit", function() {
|
||||||
|
const editId = $(this).attr("data-id");
|
||||||
|
const supplierId = $(this).attr("data-idsupplier");
|
||||||
|
const mix = $(this).attr("data-mix");
|
||||||
|
const lot = $(this).attr("data-lot");
|
||||||
|
const exp = $(this).attr("data-exp");
|
||||||
|
const qty = $(this).attr("data-qty");
|
||||||
|
|
||||||
|
$("#afEditId").val(editId);
|
||||||
|
afSetSupplierValue(supplierId);
|
||||||
|
$("#afSupplierMixName").val(mix);
|
||||||
|
$("#afLotCode").val(lot);
|
||||||
|
$("#afExpiryDate").val(exp);
|
||||||
|
$("#afQty").val(qty);
|
||||||
|
|
||||||
|
$("#afSaveBtn").text("💾 Salva Modifica");
|
||||||
|
$("#afCancelEdit").show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#afCancelEdit").on("click", function() {
|
||||||
|
afResetForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save (insert/update)
|
||||||
|
$("#afSaveBtn").on("click", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const idmescola = $("#afIdMescola").val();
|
||||||
|
const editId = $("#afEditId").val();
|
||||||
|
|
||||||
|
const idsupplier = String($("#afIdSupplier").val() || "").trim();
|
||||||
|
const supplier_mix_name = $("#afSupplierMixName").val().trim();
|
||||||
|
const lot_code = $("#afLotCode").val().trim();
|
||||||
|
const expiry_date = $("#afExpiryDate").val();
|
||||||
|
const qty = $("#afQty").val();
|
||||||
|
|
||||||
|
if (!idsupplier || !supplier_mix_name) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "warning",
|
||||||
|
title: "Attenzione",
|
||||||
|
text: "Fornitore e Nome mescola fornitore sono obbligatori"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionUrl = editId ? "update_mescola_supplier_lot.php" : "save_mescola_supplier_lot.php";
|
||||||
|
|
||||||
|
const body =
|
||||||
|
(editId ? `id=${encodeURIComponent(editId)}&` : ``) +
|
||||||
|
`idmescola=${encodeURIComponent(idmescola)}` +
|
||||||
|
`&idsupplier=${encodeURIComponent(idsupplier)}` +
|
||||||
|
`&supplier_mix_name=${encodeURIComponent(supplier_mix_name)}` +
|
||||||
|
`&lot_code=${encodeURIComponent(lot_code)}` +
|
||||||
|
`&expiry_date=${encodeURIComponent(expiry_date)}` +
|
||||||
|
`&qty=${encodeURIComponent(qty)}`;
|
||||||
|
|
||||||
|
fetch(actionUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
afResetForm();
|
||||||
|
afLoadRows(idmescola);
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Errore",
|
||||||
|
text: data.message || "Operazione non riuscita"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
$(document).on("click", ".af-del", function() {
|
||||||
|
const id = $(this).data("id");
|
||||||
|
const idmescola = $("#afIdMescola").val();
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: "Eliminare la riga?",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: "Sì, elimina",
|
||||||
|
cancelButtonText: "Annulla",
|
||||||
|
confirmButtonColor: "#d33"
|
||||||
|
}).then((res) => {
|
||||||
|
if (!res.isConfirmed) return;
|
||||||
|
|
||||||
|
fetch("delete_mescola_supplier_lot.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: `id=${encodeURIComponent(id)}`
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
afLoadRows(idmescola);
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Errore",
|
||||||
|
text: data.message || "Cancellazione non riuscita"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -0,0 +1,390 @@
|
|||||||
|
<?php include('include/headscript.php'); ?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||||
|
<?php include('cssinclude.php'); ?>
|
||||||
|
<title>Dashboard Produzione - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
|
|
||||||
|
<!-- Bootstrap + jQuery -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: linear-gradient(135deg, #f3f6f8, #e8eef3);
|
||||||
|
font-family: 'Segoe UI', sans-serif;
|
||||||
|
color: #2b3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3.dashboard-title {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2b3e50;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== STATISTICHE ===== */
|
||||||
|
.stats-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
flex: 1 1 250px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 20px 25px;
|
||||||
|
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2b3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #6b7a89;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-prod {
|
||||||
|
background: linear-gradient(135deg, #cde5ff, #dff0ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-mese {
|
||||||
|
background: linear-gradient(135deg, #d2f7d9, #e7ffea);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-scarti {
|
||||||
|
background: linear-gradient(135deg, #ffe7cc, #fff3df);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== BOTTONI ===== */
|
||||||
|
.dashboard-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(200px, 1fr));
|
||||||
|
gap: 25px 35px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 900px;
|
||||||
|
justify-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-grid-bottom {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(200px, 1fr));
|
||||||
|
gap: 15px 35px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 900px;
|
||||||
|
justify-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tools {
|
||||||
|
background: linear-gradient(135deg, #9f7aea, #b794f4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-btn {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 280px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 24px 10px;
|
||||||
|
color: #2b3e50;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #fff;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-btn:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.15);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-icon {
|
||||||
|
font-size: 3.2rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colori pastello */
|
||||||
|
.btn-inserisci {
|
||||||
|
background: linear-gradient(135deg, #a4c2f3ff, #c1d8ffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-visualizza {
|
||||||
|
background: linear-gradient(135deg, #82f09eff, #aaecaaff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-statistiche {
|
||||||
|
background: linear-gradient(135deg, #ffe0a3ff, #fff1c1ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-mescole {
|
||||||
|
background: linear-gradient(135deg, #ffc853ff, #fdd98bff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-matrici {
|
||||||
|
background: linear-gradient(135deg, #ff8585ff, #ff9d9dff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-linee {
|
||||||
|
background: linear-gradient(135deg, #b9e3ffff, #d7f1ffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-programmazione {
|
||||||
|
background: linear-gradient(135deg, #7c7afaff, #7c7afaff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-status {
|
||||||
|
background: linear-gradient(135deg, #61ce5dff, #61ce5dff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-problem {
|
||||||
|
background-color: #ef4444 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-problem:hover {
|
||||||
|
background-color: #dc2626 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tools {
|
||||||
|
background: linear-gradient(135deg, #9f7aea, #b794f4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 🔹 Nuovo bottone Employees */
|
||||||
|
.btn-employees {
|
||||||
|
background: linear-gradient(135deg, #a5b4fc, #c7d2fe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Pulsanti grandi (default) --- */
|
||||||
|
.dash-btn-large {
|
||||||
|
padding: 18px 10px;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Pulsanti di servizio: più bassi --- */
|
||||||
|
.dash-btn-small {
|
||||||
|
padding: 9px 10px !important;
|
||||||
|
font-size: 1.05rem !important;
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
|
||||||
|
.stats-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-grid,
|
||||||
|
.dashboard-grid-bottom {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-btn {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 30px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-icon {
|
||||||
|
font-size: 2.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrapper toggled">
|
||||||
|
<?php include('include/navbar.php'); ?>
|
||||||
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
|
||||||
|
<h3 class="dashboard-title">Dashboard Produzione</h3>
|
||||||
|
|
||||||
|
<!-- ===== STATISTICHE PRINCIPALI ===== -->
|
||||||
|
<div class="stats-row">
|
||||||
|
<?php
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// Totale odierno
|
||||||
|
$stmt = $pdo->query("SELECT SUM(kgprod) AS totale_oggi FROM productiondata WHERE DATE(Data) = CURDATE()");
|
||||||
|
$totOggi = number_format($stmt->fetchColumn() ?? 0, 2, ',', '.');
|
||||||
|
|
||||||
|
// Totale mese
|
||||||
|
$stmt = $pdo->query("SELECT SUM(kgprod) AS totale_mese FROM productiondata WHERE MONTH(Data) = MONTH(CURDATE()) AND YEAR(Data) = YEAR(CURDATE())");
|
||||||
|
$totMese = number_format($stmt->fetchColumn() ?? 0, 2, ',', '.');
|
||||||
|
|
||||||
|
// Scarti medi %
|
||||||
|
$stmt = $pdo->query("SELECT (SUM(scarto)/NULLIF(SUM(kgprod),0))*100 AS perc_scarto FROM productiondata WHERE MONTH(Data) = MONTH(CURDATE()) AND YEAR(Data) = YEAR(CURDATE())");
|
||||||
|
$percScarto = number_format($stmt->fetchColumn() ?? 0, 2, ',', '.');
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="stat-card stat-prod">
|
||||||
|
<div class="stat-value"><?= $totOggi ?> kg</div>
|
||||||
|
<div class="stat-label">Produzione odierna</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card stat-mese">
|
||||||
|
<div class="stat-value"><?= $totMese ?> kg</div>
|
||||||
|
<div class="stat-label">Produzione mese corrente</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card stat-scarti">
|
||||||
|
<div class="stat-value"><?= $percScarto ?>%</div>
|
||||||
|
<div class="stat-label">Scarto medio mensile</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ===== PRIMA RIGA ===== -->
|
||||||
|
<div class="dashboard-grid">
|
||||||
|
<button class="dash-btn dash-btn-large btn-programmazione" onclick="location.href='produzione_programmazione_drag.php'">
|
||||||
|
<div class="dash-icon">🗓️</div>
|
||||||
|
<div>Programmazione</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-large btn-status" onclick="location.href='production_line_view2.php'">
|
||||||
|
<div class="dash-icon">⚙️</div>
|
||||||
|
<div>Line View</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-large btn-statistiche" onclick="location.href='production_stats.php'">
|
||||||
|
<div class="dash-icon">📈</div>
|
||||||
|
<div>Statistiche</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-large btn-manager" onclick="location.href='manager_produzione.php'">
|
||||||
|
<div class="dash-icon">👔</div>
|
||||||
|
<div>Manager</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-large btn-manager-stats" onclick="location.href='manager_stats.php'">
|
||||||
|
<div class="dash-icon">📊</div>
|
||||||
|
<div>Manager Stats</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-large btn-linee" onclick="location.href='warehouse_dashboard.php'">
|
||||||
|
<div class="dash-icon">📦</div>
|
||||||
|
<div>Magazzino</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ===== SECONDA RIGA ===== -->
|
||||||
|
<div class="dashboard-grid">
|
||||||
|
<button class="dash-btn dash-btn-small btn-mescole" onclick="location.href='mescole.php'">
|
||||||
|
<div class="dash-icon">⚗️</div>
|
||||||
|
<div>Mescole</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-small btn-matrici" onclick="location.href='matrici.php'">
|
||||||
|
<div class="dash-icon">🧩</div>
|
||||||
|
<div>Elenco Profili</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-small btn-linee" onclick="location.href='linee.php'">
|
||||||
|
<div class="dash-icon">🏭</div>
|
||||||
|
<div>Linee di Produzione</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ===== TERZA RIGA ===== -->
|
||||||
|
<div class="dashboard-grid-bottom">
|
||||||
|
<button class="dash-btn dash-btn-small btn-inserisci btn-inserisci-status" onclick="location.href='production_status.php'">
|
||||||
|
<div class="dash-icon">📋</div>
|
||||||
|
<div>Status</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-small btn-problem" onclick="location.href='production_pause_reasons.php'">
|
||||||
|
<div class="dash-icon">🛑</div>
|
||||||
|
<div>Cause di Pausa</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="dash-btn dash-btn-small btn-tools" onclick="location.href='production_tools.php'">
|
||||||
|
<div class="dash-icon">🛠️</div>
|
||||||
|
<div>Attrezzature</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 🔹 QUARTA RIGA: EMPLOYEES -->
|
||||||
|
<div class="dashboard-grid-bottom" style="margin-top: 20px;">
|
||||||
|
<button class="dash-btn dash-btn-small btn-skills" onclick="location.href='worksheets.php'">
|
||||||
|
<div class="dash-icon">🛠️</div>
|
||||||
|
<div>Fogli di lavoro</div>
|
||||||
|
</button>
|
||||||
|
<button class="dash-btn dash-btn-small btn-employees" onclick="location.href='employees.php'">
|
||||||
|
<div class="dash-icon">👥</div>
|
||||||
|
<div>Employees</div>
|
||||||
|
</button>
|
||||||
|
<button class="dash-btn dash-btn-small btn-skills" onclick="location.href='skills.php'">
|
||||||
|
<div class="dash-icon">🛠️</div>
|
||||||
|
<div>Skills</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 🔹 Quinta RIGA: EMPLOYEES -->
|
||||||
|
<div class="dashboard-grid-bottom" style="margin-top: 20px;">
|
||||||
|
<button class="dash-btn dash-btn-small btn-skills" onclick="location.href='packaging_items.php'">
|
||||||
|
<div class="dash-icon">🛠️</div>
|
||||||
|
<div>Imballaggi</div>
|
||||||
|
</button>
|
||||||
|
<button class="dash-btn dash-btn-small btn-skills" onclick="location.href='suppliers.php'">
|
||||||
|
<div class="dash-icon">🛠️</div>
|
||||||
|
<div>Suppliers</div>
|
||||||
|
</button>
|
||||||
|
<button class="dash-btn dash-btn-small btn-skills" onclick="location.href='lookup_values.php'">
|
||||||
|
<div class="dash-icon">🛠️</div>
|
||||||
|
<div>Setup</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('jsinclude.php'); ?>
|
||||||
|
<?php include('include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,689 @@
|
|||||||
|
<?php include('include/headscript.php'); ?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||||
|
<?php include('cssinclude.php'); ?>
|
||||||
|
<title>Imballaggi - Anagrafica & Stock - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
|
|
||||||
|
<!-- jQuery + Bootstrap -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
|
||||||
|
<!-- DataTables -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
||||||
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Select2 -->
|
||||||
|
<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" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.full.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, .08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead {
|
||||||
|
background: #cfe3ff;
|
||||||
|
color: #1f2d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add {
|
||||||
|
background-color: #0d6efd;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add:hover {
|
||||||
|
background-color: #0b5ed7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabPackaging {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabPackaging th,
|
||||||
|
#tabPackaging td {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Column widths */
|
||||||
|
#tabPackaging th:nth-child(1),
|
||||||
|
#tabPackaging td:nth-child(1) {
|
||||||
|
width: 60px;
|
||||||
|
max-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabPackaging th:nth-child(2),
|
||||||
|
#tabPackaging td:nth-child(2) {
|
||||||
|
width: 170px;
|
||||||
|
max-width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabPackaging th:nth-child(4),
|
||||||
|
#tabPackaging td:nth-child(4) {
|
||||||
|
width: 140px;
|
||||||
|
max-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabPackaging th:nth-child(5),
|
||||||
|
#tabPackaging td:nth-child(5) {
|
||||||
|
width: 110px;
|
||||||
|
max-width: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabPackaging th:nth-child(6),
|
||||||
|
#tabPackaging td:nth-child(6) {
|
||||||
|
width: 120px;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabPackaging th:nth-child(7),
|
||||||
|
#tabPackaging td:nth-child(7) {
|
||||||
|
width: 420px;
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-dashboard {
|
||||||
|
background-color: #cfe3ff !important;
|
||||||
|
color: #1f2d3d !important;
|
||||||
|
border: 1px solid #bcd4f4 !important;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 10px 18px;
|
||||||
|
box-shadow: 0 3px 8px rgba(0, 0, 0, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-dashboard:hover {
|
||||||
|
background-color: #b9d3ff !important;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qty-badge {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.inactive-item {
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrapper toggled">
|
||||||
|
<?php include('include/navbar.php'); ?>
|
||||||
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="card p-3">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0">Imballaggi - Anagrafica & Stock</h5>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button class="btn back-dashboard" onclick="location.href='warehouse_dashboard.php'">↩️ Torna a Magazzino</button>
|
||||||
|
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addItemModal">➕ Nuovo Imballo</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<h6 class="fw-semibold mb-0">Elenco</h6>
|
||||||
|
|
||||||
|
<select id="filterActive" class="form-select form-select-sm" style="width:220px;">
|
||||||
|
<option value="all">Tutti</option>
|
||||||
|
<option value="1" selected>Solo Attivi</option>
|
||||||
|
<option value="0">Solo Inattivi</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="filterCategory" class="form-select form-select-sm" style="width:240px;">
|
||||||
|
<option value="all">Tutte le categorie</option>
|
||||||
|
<option value="PACKAGING_TYPE">Tipo Confezione</option>
|
||||||
|
<option value="BOX">Scatole</option>
|
||||||
|
<option value="PALLET">Pallet</option>
|
||||||
|
<option value="OTHER">Altro</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$active = $_GET['active'] ?? '1';
|
||||||
|
if (!in_array($active, ['all', '0', '1'], true)) $active = '1';
|
||||||
|
|
||||||
|
$cat = $_GET['cat'] ?? 'all';
|
||||||
|
$allowedCats = ['all', 'PACKAGING_TYPE', 'BOX', 'PALLET', 'OTHER'];
|
||||||
|
if (!in_array($cat, $allowedCats, true)) $cat = 'all';
|
||||||
|
|
||||||
|
$where = [];
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if ($active !== 'all') {
|
||||||
|
$where[] = "pi.is_active = ?";
|
||||||
|
$params[] = (int)$active;
|
||||||
|
}
|
||||||
|
if ($cat !== 'all') {
|
||||||
|
$where[] = "pi.category = ?";
|
||||||
|
$params[] = $cat;
|
||||||
|
}
|
||||||
|
|
||||||
|
$whereSql = $where ? ("WHERE " . implode(" AND ", $where)) : "";
|
||||||
|
|
||||||
|
// Sum qty in subquery to avoid duplicates
|
||||||
|
$sql = "
|
||||||
|
SELECT
|
||||||
|
pi.id,
|
||||||
|
pi.category,
|
||||||
|
pi.item_name,
|
||||||
|
pi.item_code,
|
||||||
|
pi.is_active,
|
||||||
|
IFNULL(q.qty_totale, 0) AS qty_totale
|
||||||
|
FROM packaging_items pi
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT idpackaging_item, SUM(qty) AS qty_totale
|
||||||
|
FROM packaging_stock_lots
|
||||||
|
GROUP BY idpackaging_item
|
||||||
|
) q ON q.idpackaging_item = pi.id
|
||||||
|
$whereSql
|
||||||
|
ORDER BY pi.category ASC, pi.item_name ASC, pi.id DESC
|
||||||
|
";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
function catLabel($c)
|
||||||
|
{
|
||||||
|
return match ($c) {
|
||||||
|
'PACKAGING_TYPE' => 'Tipo Confezione',
|
||||||
|
'BOX' => 'Scatola',
|
||||||
|
'PALLET' => 'Pallet',
|
||||||
|
default => $c
|
||||||
|
};
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="tabPackaging" class="table table-striped align-middle text-center" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Categoria</th>
|
||||||
|
<th>Nome</th>
|
||||||
|
<th>Codice</th>
|
||||||
|
<th>Q.tà Tot</th>
|
||||||
|
<th>Stato</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php
|
||||||
|
if (!$rows) {
|
||||||
|
echo "<tr>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>Nessun elemento</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
<td class='text-muted'>-</td>
|
||||||
|
</tr>";
|
||||||
|
} else {
|
||||||
|
foreach ($rows as $r) {
|
||||||
|
$isActive = ((int)$r['is_active'] === 1);
|
||||||
|
$badge = $isActive
|
||||||
|
? "<span class='badge bg-success'>Attivo</span>"
|
||||||
|
: "<span class='badge bg-secondary'>Inattivo</span>";
|
||||||
|
$toggleText = $isActive ? "Disattiva" : "Attiva";
|
||||||
|
$toggleClass = $isActive ? "btn-outline-warning" : "btn-outline-success";
|
||||||
|
|
||||||
|
$qtyTot = number_format((float)$r['qty_totale'], 3, ',', '.');
|
||||||
|
$trClass = $isActive ? "" : " class='inactive-item'";
|
||||||
|
|
||||||
|
echo "<tr{$trClass} data-item-id='{$r['id']}'>
|
||||||
|
<td>{$r['id']}</td>
|
||||||
|
<td>" . htmlspecialchars(catLabel($r['category'])) . "</td>
|
||||||
|
<td>" . htmlspecialchars($r['item_name']) . "</td>
|
||||||
|
<td><span class='fw-semibold'>" . htmlspecialchars($r['item_code']) . "</span></td>
|
||||||
|
<td><span class='qty-badge'>{$qtyTot}</span></td>
|
||||||
|
<td>{$badge}</td>
|
||||||
|
<td>
|
||||||
|
<button class='btn btn-sm btn-outline-dark manage-stock'
|
||||||
|
data-id='{$r['id']}'
|
||||||
|
data-name='" . htmlspecialchars($r['item_name'], ENT_QUOTES) . "'>
|
||||||
|
📦 Stock
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class='btn btn-sm {$toggleClass} toggle-item'
|
||||||
|
data-id='{$r['id']}'>
|
||||||
|
🔁 {$toggleText}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-muted small mt-2">
|
||||||
|
Q.tà Tot = somma di tutti i lotti/fornitori collegati all’imballo.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MODALE: NUOVO IMBALLO -->
|
||||||
|
<div class="modal fade" id="addItemModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background-color:#cfe3ff;">
|
||||||
|
<h5 class="modal-title">Nuovo Imballo</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="addItemForm">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Categoria</label>
|
||||||
|
<select class="form-select" id="newCategory" required>
|
||||||
|
<option value="PACKAGING_TYPE">Tipo Confezione</option>
|
||||||
|
<option value="BOX">Scatola</option>
|
||||||
|
<option value="PALLET">Pallet</option>
|
||||||
|
<option value="OTHER">Altro</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Nome</label>
|
||||||
|
<input type="text" class="form-control" id="newItemName" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Codice</label>
|
||||||
|
<input type="text" class="form-control" id="newItemCode" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<button type="submit" class="btn btn-add">💾 Salva</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MODALE: STOCK FORNITORI/LOTTI -->
|
||||||
|
<div class="modal fade" id="stockModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header" style="background-color:#cfe3ff;">
|
||||||
|
<h5 class="modal-title">Stock - <span id="stockItemName"></span></h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="stockItemId">
|
||||||
|
<input type="hidden" id="stockEditId">
|
||||||
|
|
||||||
|
<div class="row g-2 align-items-end mb-3">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label fw-semibold">Fornitore</label>
|
||||||
|
<select class="form-select" id="stockSupplier" style="width:100%;"></select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label fw-semibold">Lotto</label>
|
||||||
|
<input type="text" class="form-control" id="stockLot" placeholder="LOT-...">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label fw-semibold">Scadenza</label>
|
||||||
|
<input type="date" class="form-control" id="stockExpiry">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label fw-semibold">Q.tà</label>
|
||||||
|
<input type="number" step="0.001" class="form-control" id="stockQty" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 text-end">
|
||||||
|
<button class="btn btn-add w-100" id="stockSaveBtn">➕ Aggiungi</button>
|
||||||
|
<button class="btn btn-outline-secondary w-100 mt-2" id="stockCancelEdit" type="button" style="display:none;">Annulla</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped align-middle text-center" id="stockTable">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Fornitore</th>
|
||||||
|
<th>Lotto</th>
|
||||||
|
<th>Scadenza</th>
|
||||||
|
<th>Q.tà</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-muted small">
|
||||||
|
Suggerimento: per cambiare quantità clicca ✏️, modifica Q.tà e salva.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('jsinclude.php'); ?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let dt;
|
||||||
|
|
||||||
|
function refreshUrlParams() {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.searchParams.set('active', $('#filterActive').val());
|
||||||
|
url.searchParams.set('cat', $('#filterCategory').val());
|
||||||
|
window.location.href = url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSuppliersSelect($select, dropdownParent) {
|
||||||
|
return fetch("get_suppliers.php")
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
$select.empty();
|
||||||
|
$select.append(`<option value="">Seleziona...</option>`);
|
||||||
|
if (data.success && Array.isArray(data.rows)) {
|
||||||
|
data.rows.forEach(s => $select.append(`<option value="${s.idsupplier}">${s.supplier_name}</option>`));
|
||||||
|
}
|
||||||
|
$select.select2({
|
||||||
|
theme: "bootstrap-5",
|
||||||
|
width: "100%",
|
||||||
|
dropdownParent: dropdownParent
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function stockResetForm() {
|
||||||
|
$("#stockEditId").val("");
|
||||||
|
$("#stockSupplier").val("").trigger("change");
|
||||||
|
$("#stockLot").val("");
|
||||||
|
$("#stockExpiry").val("");
|
||||||
|
$("#stockQty").val("0");
|
||||||
|
$("#stockSaveBtn").text("➕ Aggiungi");
|
||||||
|
$("#stockCancelEdit").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stockLoadRows(itemId) {
|
||||||
|
fetch("get_packaging_stock_lots.php?id=" + encodeURIComponent(itemId))
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
const tbody = $("#stockTable tbody");
|
||||||
|
tbody.empty();
|
||||||
|
|
||||||
|
if (!data.success || !Array.isArray(data.rows) || data.rows.length === 0) {
|
||||||
|
tbody.append(`<tr>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">Nessuna riga</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
</tr>`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.rows.forEach(row => {
|
||||||
|
const exp = row.expiry_date ? row.expiry_date : "";
|
||||||
|
tbody.append(`
|
||||||
|
<tr data-row-id="${row.id}">
|
||||||
|
<td>${row.id}</td>
|
||||||
|
<td>${row.supplier_name}</td>
|
||||||
|
<td>${row.lot_code ?? ""}</td>
|
||||||
|
<td>${exp || "-"}</td>
|
||||||
|
<td>${row.qty}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-sm btn-outline-secondary stock-edit"
|
||||||
|
data-id="${row.id}"
|
||||||
|
data-idsupplier="${row.idsupplier}"
|
||||||
|
data-lot="${(row.lot_code ?? "").replace(/"/g,'"')}"
|
||||||
|
data-exp="${exp}"
|
||||||
|
data-qty="${row.qty}">
|
||||||
|
✏️
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-outline-danger stock-del" data-id="${row.id}">🗑️</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Init filters from URL
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
$('#filterActive').val(urlParams.get('active') || '1');
|
||||||
|
$('#filterCategory').val(urlParams.get('cat') || 'all');
|
||||||
|
|
||||||
|
dt = $('#tabPackaging').DataTable({
|
||||||
|
order: [
|
||||||
|
[2, 'asc']
|
||||||
|
],
|
||||||
|
pageLength: 50,
|
||||||
|
language: {
|
||||||
|
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#filterActive, #filterCategory').on('change', refreshUrlParams);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create new item
|
||||||
|
$("#addItemForm").on("submit", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const category = $("#newCategory").val();
|
||||||
|
const item_name = $("#newItemName").val().trim();
|
||||||
|
const item_code = $("#newItemCode").val().trim();
|
||||||
|
|
||||||
|
fetch("save_packaging_item.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: `category=${encodeURIComponent(category)}&item_name=${encodeURIComponent(item_name)}&item_code=${encodeURIComponent(item_code)}`
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "success",
|
||||||
|
title: "Creato!"
|
||||||
|
}).then(() => location.reload());
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Errore",
|
||||||
|
text: data.message || "Non salvato"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle item active
|
||||||
|
$(document).on("click", ".toggle-item", function() {
|
||||||
|
const id = $(this).data("id");
|
||||||
|
fetch("toggle_packaging_item_active.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: `id=${encodeURIComponent(id)}`
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) location.reload();
|
||||||
|
else Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Errore",
|
||||||
|
text: data.message || "Operazione non riuscita"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Open stock modal
|
||||||
|
$(document).on("click", ".manage-stock", function() {
|
||||||
|
const id = $(this).data("id");
|
||||||
|
const name = $(this).data("name");
|
||||||
|
|
||||||
|
$("#stockItemId").val(id);
|
||||||
|
$("#stockItemName").text(name);
|
||||||
|
|
||||||
|
$("#stockModal").modal("show");
|
||||||
|
stockResetForm();
|
||||||
|
loadSuppliersSelect($("#stockSupplier"), $("#stockModal")).then(() => stockLoadRows(id));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click edit stock row
|
||||||
|
$(document).on("click", ".stock-edit", function() {
|
||||||
|
$("#stockEditId").val($(this).data("id"));
|
||||||
|
$("#stockSupplier").val(String($(this).data("idsupplier"))).trigger("change");
|
||||||
|
$("#stockLot").val($(this).data("lot"));
|
||||||
|
$("#stockExpiry").val($(this).data("exp"));
|
||||||
|
$("#stockQty").val($(this).data("qty"));
|
||||||
|
|
||||||
|
$("#stockSaveBtn").text("💾 Salva");
|
||||||
|
$("#stockCancelEdit").show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel edit
|
||||||
|
$("#stockCancelEdit").on("click", function() {
|
||||||
|
stockResetForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save stock (insert/update)
|
||||||
|
$("#stockSaveBtn").on("click", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const itemId = $("#stockItemId").val();
|
||||||
|
const editId = $("#stockEditId").val();
|
||||||
|
|
||||||
|
const idsupplier = $("#stockSupplier").val();
|
||||||
|
const lot = $("#stockLot").val().trim();
|
||||||
|
const expiry = $("#stockExpiry").val();
|
||||||
|
const qty = $("#stockQty").val();
|
||||||
|
|
||||||
|
if (!idsupplier) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "warning",
|
||||||
|
title: "Attenzione",
|
||||||
|
text: "Seleziona un fornitore"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = editId ? "update_packaging_stock_lot.php" : "save_packaging_stock_lot.php";
|
||||||
|
|
||||||
|
const body =
|
||||||
|
(editId ? `id=${encodeURIComponent(editId)}&` : ``) +
|
||||||
|
`idpackaging_item=${encodeURIComponent(itemId)}` +
|
||||||
|
`&idsupplier=${encodeURIComponent(idsupplier)}` +
|
||||||
|
`&lot_code=${encodeURIComponent(lot)}` +
|
||||||
|
`&expiry_date=${encodeURIComponent(expiry)}` +
|
||||||
|
`&qty=${encodeURIComponent(qty)}`;
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
stockResetForm();
|
||||||
|
stockLoadRows(itemId);
|
||||||
|
// Refresh totals by reloading page (simple and safe)
|
||||||
|
// If you want live update without reload, we can do it.
|
||||||
|
// location.reload();
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Errore",
|
||||||
|
text: data.message || "Operazione non riuscita"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete stock row
|
||||||
|
$(document).on("click", ".stock-del", function() {
|
||||||
|
const id = $(this).data("id");
|
||||||
|
const itemId = $("#stockItemId").val();
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: "Eliminare la riga?",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: "Sì, elimina",
|
||||||
|
cancelButtonText: "Annulla",
|
||||||
|
confirmButtonColor: "#d33"
|
||||||
|
}).then((res) => {
|
||||||
|
if (!res.isConfirmed) return;
|
||||||
|
|
||||||
|
fetch("delete_packaging_stock_lot.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: `id=${encodeURIComponent(id)}`
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
stockLoadRows(itemId);
|
||||||
|
// location.reload();
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Errore",
|
||||||
|
text: data.message || "Cancellazione non riuscita"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 898 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 463 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 435 B |
|
After Width: | Height: | Size: 409 B |
|
After Width: | Height: | Size: 392 B |
|
After Width: | Height: | Size: 861 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 450 B |
|
After Width: | Height: | Size: 806 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 838 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 953 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 895 B |
|
After Width: | Height: | Size: 867 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 680 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 921 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |