fixed dettaglio worksheet
This commit is contained in:
@@ -167,6 +167,7 @@ try {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mescole del foglio
|
||||||
$stmtMix = $pdo->prepare("
|
$stmtMix = $pdo->prepare("
|
||||||
SELECT
|
SELECT
|
||||||
wsm.*,
|
wsm.*,
|
||||||
@@ -180,6 +181,30 @@ try {
|
|||||||
$stmtMix->execute([$worksheetId]);
|
$stmtMix->execute([$worksheetId]);
|
||||||
$mixRows = $stmtMix->fetchAll(PDO::FETCH_ASSOC);
|
$mixRows = $stmtMix->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Imballi del foglio (come nella sezione 3 di manage-worksheet.php)
|
||||||
|
$stmtPkg = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
wsp.id,
|
||||||
|
wsp.package_position,
|
||||||
|
pi.category AS package_category,
|
||||||
|
pi.item_name AS package_name,
|
||||||
|
pi.item_code AS package_item_code
|
||||||
|
FROM work_sheet_packages wsp
|
||||||
|
LEFT JOIN packaging_items pi ON pi.id = wsp.package_code
|
||||||
|
WHERE wsp.worksheet_id = ?
|
||||||
|
ORDER BY wsp.package_position ASC, wsp.id ASC
|
||||||
|
");
|
||||||
|
$stmtPkg->execute([$worksheetId]);
|
||||||
|
$packageRows = $stmtPkg->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Etichette dei menu a tendina (marchiatura, frequenze controllo, lubrificazione...)
|
||||||
|
// Prese tutte, anche le disattivate, così i fogli vecchi mostrano comunque l'etichetta
|
||||||
|
$lookups = [];
|
||||||
|
$stmtLk = $pdo->query("SELECT category, value, label FROM ws_lookup_options");
|
||||||
|
foreach ($stmtLk->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||||
|
$lookups[$r['category']][(string)$r['value']] = $r['label'];
|
||||||
|
}
|
||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'worksheet' => [
|
'worksheet' => [
|
||||||
@@ -218,6 +243,7 @@ try {
|
|||||||
'speed_actual_m_h' => $ws['speed_actual_m_h'] ?? '',
|
'speed_actual_m_h' => $ws['speed_actual_m_h'] ?? '',
|
||||||
'approved_by' => $ws['approved_by'] ?? '',
|
'approved_by' => $ws['approved_by'] ?? '',
|
||||||
'notes' => $ws['notes'] ?? '',
|
'notes' => $ws['notes'] ?? '',
|
||||||
|
'source_xlsx_path' => $ws['source_xlsx_path'] ?? '',
|
||||||
'created_at' => $ws['created_at'] ?? '',
|
'created_at' => $ws['created_at'] ?? '',
|
||||||
'created_at_it' => formatDateTimeIT($ws['created_at'] ?? ''),
|
'created_at_it' => formatDateTimeIT($ws['created_at'] ?? ''),
|
||||||
'updated_at' => $ws['updated_at'] ?? '',
|
'updated_at' => $ws['updated_at'] ?? '',
|
||||||
@@ -235,7 +261,17 @@ try {
|
|||||||
'lubrication_type' => $r['lubrication_type'] ?? '',
|
'lubrication_type' => $r['lubrication_type'] ?? '',
|
||||||
'lubrication_notes' => $r['lubrication_notes'] ?? ''
|
'lubrication_notes' => $r['lubrication_notes'] ?? ''
|
||||||
];
|
];
|
||||||
}, $mixRows)
|
}, $mixRows),
|
||||||
|
'package_rows' => array_map(function ($r) {
|
||||||
|
return [
|
||||||
|
'id' => (int)$r['id'],
|
||||||
|
'package_position' => (int)$r['package_position'],
|
||||||
|
'package_category' => $r['package_category'] ?? '',
|
||||||
|
'package_name' => $r['package_name'] ?? '',
|
||||||
|
'package_item_code' => $r['package_item_code'] ?? ''
|
||||||
|
];
|
||||||
|
}, $packageRows),
|
||||||
|
'lookups' => (object)$lookups
|
||||||
]);
|
]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
(function () {
|
(function () {
|
||||||
function escapeHtml(str) {
|
function escapeHtml(str) {
|
||||||
return String(str || "")
|
return String(str === null || str === undefined ? "" : str)
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
.replace(/</g, "<")
|
.replace(/</g, "<")
|
||||||
.replace(/>/g, ">")
|
.replace(/>/g, ">")
|
||||||
@@ -8,16 +8,57 @@
|
|||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function valueOrDash(v) {
|
function isEmpty(v) {
|
||||||
return v === null || v === undefined || String(v).trim() === ""
|
return v === null || v === undefined || String(v).trim() === "";
|
||||||
? "—"
|
|
||||||
: escapeHtml(v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderReadonlyField(label, value) {
|
function valueOrDash(v) {
|
||||||
|
return isEmpty(v) ? "—" : escapeHtml(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traduce il valore salvato (es. "every_2h") nell'etichetta del menu a tendina,
|
||||||
|
// usando le etichette di ws_lookup_options restituite dall'endpoint.
|
||||||
|
// Se l'etichetta non c'è, mostra il valore così com'è.
|
||||||
|
function lookupLabel(lookups, category, value) {
|
||||||
|
if (isEmpty(value)) return "";
|
||||||
|
const map = lookups && lookups[category];
|
||||||
|
if (map && map[String(value)] !== undefined) return map[String(value)];
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Campo in sola lettura: etichetta piccola sopra, valore in evidenza sotto
|
||||||
|
function wsdField(label, value, extraClass) {
|
||||||
|
const empty = isEmpty(value);
|
||||||
return `
|
return `
|
||||||
<div class="readonly-label">${escapeHtml(label)}</div>
|
<div class="wsd-field ${extraClass || ""}">
|
||||||
<div class="readonly-value">${valueOrDash(value)}</div>
|
<div class="wsd-label">${escapeHtml(label)}</div>
|
||||||
|
<div class="wsd-value ${empty ? "wsd-empty" : ""}">${valueOrDash(value)}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blocco di testo lungo (note, misure di controllo)
|
||||||
|
function wsdText(label, value, emptyText) {
|
||||||
|
const empty = isEmpty(value);
|
||||||
|
return `
|
||||||
|
<div class="wsd-text-block">
|
||||||
|
${label ? `<div class="wsd-label mb-1">${escapeHtml(label)}</div>` : ""}
|
||||||
|
<div class="wsd-text ${empty ? "wsd-empty" : ""}">${empty ? escapeHtml(emptyText || "—") : escapeHtml(value)}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wsdSection(theme, title, badge, body, colClass) {
|
||||||
|
return `
|
||||||
|
<div class="${colClass || "col-12"}">
|
||||||
|
<div class="wsd-section wsd-${theme}">
|
||||||
|
<div class="wsd-head">
|
||||||
|
<span>${title}</span>
|
||||||
|
${badge ? `<span class="wsd-head-badge">${badge}</span>` : ""}
|
||||||
|
</div>
|
||||||
|
<div class="wsd-body">${body}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,203 +333,271 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.loadWorksheetDetail = function (worksheetId, endpoint) {
|
// ================================================================
|
||||||
endpoint = endpoint || "ajax/worksheet-linked-data.php";
|
// Dettaglio foglio di lavoro (sola lettura)
|
||||||
|
// Rispecchia le sezioni di manage-worksheet.php:
|
||||||
$("#worksheetDetailContainer").html(
|
// 1 Dati principali · 2 Controlli · 3 Imballo · 4 Velocità · 5 Note · 6 Mescole
|
||||||
'<div class="text-muted">Caricamento dettaglio foglio...</div>',
|
// ================================================================
|
||||||
);
|
function renderWorksheetDetail(data) {
|
||||||
|
|
||||||
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 ws = data.worksheet || {};
|
||||||
const mixRows = data.mix_rows || [];
|
const mixRows = data.mix_rows || [];
|
||||||
|
const packageRows = data.package_rows || [];
|
||||||
|
const lookups = data.lookups || {};
|
||||||
|
|
||||||
let mixHtml = `<div class="text-muted">Nessuna mescola associata</div>`;
|
const isInactive = ws.worksheet_status === "inactive";
|
||||||
|
const statusLabel =
|
||||||
|
ws.worksheet_status_label || (isInactive ? "Inattivo" : "Attivo");
|
||||||
|
const statusBadge = isInactive
|
||||||
|
? `<span class="worksheet-badge-status-inactive">${escapeHtml(statusLabel)}</span>`
|
||||||
|
: `<span class="worksheet-badge-status-active">${escapeHtml(statusLabel)}</span>`;
|
||||||
|
|
||||||
if (mixRows.length) {
|
const cliente = ws.customer_name || ws.matrice_cliente || "";
|
||||||
mixHtml = `
|
|
||||||
<div class="table-responsive">
|
// ---------- Intestazione ----------
|
||||||
<table class="table table-striped align-middle mix-readonly-table">
|
const excelLink = ws.source_xlsx_path
|
||||||
|
? `<a href="download_worksheet_file.php?id=${encodeURIComponent(ws.id || "")}"
|
||||||
|
target="_blank" class="worksheet-open-link">
|
||||||
|
<i class="fa-solid fa-file-excel"></i> Excel di origine
|
||||||
|
</a>`
|
||||||
|
: "";
|
||||||
|
|
||||||
|
const headerHtml = `
|
||||||
|
<div class="worksheet-title-box mb-3">
|
||||||
|
<div class="d-flex justify-content-between align-items-center gap-3 flex-wrap">
|
||||||
|
<div>
|
||||||
|
<h4 class="mb-1">${escapeHtml(ws.worksheet_number_label || "—")} / Rev. ${escapeHtml(ws.revision_label || "0")}</h4>
|
||||||
|
<small>${ws.matrice_nome ? "Profilo: " + escapeHtml(ws.matrice_nome) : "Nessun profilo collegato"}</small>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2 flex-wrap">
|
||||||
|
${excelLink}
|
||||||
|
<a href="manage-worksheet.php?id=${encodeURIComponent(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>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// ---------- Striscia riepilogo ----------
|
||||||
|
const summaryHtml = `
|
||||||
|
<div class="wsd-summary mb-3">
|
||||||
|
<div class="wsd-sum-item">
|
||||||
|
<div class="wsd-label">Stato</div>
|
||||||
|
<div>${statusBadge}</div>
|
||||||
|
</div>
|
||||||
|
<div class="wsd-sum-item">
|
||||||
|
<div class="wsd-label">Cliente</div>
|
||||||
|
<div class="wsd-sum-value">${valueOrDash(cliente)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="wsd-sum-item">
|
||||||
|
<div class="wsd-label">Codice profilo</div>
|
||||||
|
<div class="wsd-sum-value">${valueOrDash(ws.profile_type_code)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="wsd-sum-item">
|
||||||
|
<div class="wsd-label">Data foglio</div>
|
||||||
|
<div class="wsd-sum-value">${valueOrDash(ws.worksheet_date_it)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="wsd-sum-item">
|
||||||
|
<div class="wsd-label">Mescole / Imballi</div>
|
||||||
|
<div class="wsd-sum-value">${mixRows.length} / ${packageRows.length}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// ---------- 1. Dati principali ----------
|
||||||
|
const sec1 = wsdSection(
|
||||||
|
"blue",
|
||||||
|
"1. Dati principali",
|
||||||
|
"",
|
||||||
|
`
|
||||||
|
<div class="wsd-fields">
|
||||||
|
${wsdField("Matrice / Profilo", ws.matrice_nome)}
|
||||||
|
${wsdField("Data foglio", ws.worksheet_date_it)}
|
||||||
|
${wsdField("Codice profilo", ws.profile_type_code)}
|
||||||
|
${wsdField("Cliente (override)", ws.customer_name)}
|
||||||
|
${wsdField("Marchiatura", lookupLabel(lookups, "marking", ws.marking))}
|
||||||
|
${wsdField("Visto / Approvazione", ws.approved_by)}
|
||||||
|
</div>
|
||||||
|
<div class="wsd-meta">
|
||||||
|
Creato il ${valueOrDash(ws.created_at_it)} · Ultimo aggiornamento ${valueOrDash(ws.updated_at_it)}
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
"col-lg-6",
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---------- 2. Controlli di produzione ----------
|
||||||
|
const sec2 = wsdSection(
|
||||||
|
"green",
|
||||||
|
"2. Controlli di produzione",
|
||||||
|
"",
|
||||||
|
`
|
||||||
|
<div class="wsd-fields">
|
||||||
|
${wsdField("Taglio", lookupLabel(lookups, "control_frequency_cut", ws.control_frequency_cut))}
|
||||||
|
${wsdField("Disegno", lookupLabel(lookups, "control_frequency_drawing", ws.control_frequency_drawing))}
|
||||||
|
${wsdField("Dima", lookupLabel(lookups, "control_frequency_jig", ws.control_frequency_jig))}
|
||||||
|
${wsdField("Modalità dima", lookupLabel(lookups, "control_mode_jig", ws.control_mode_jig))}
|
||||||
|
</div>
|
||||||
|
${wsdText("Impostazione misure controllo produzione", ws.prod_control_measure_settings, "Nessuna indicazione")}
|
||||||
|
`,
|
||||||
|
"col-lg-6",
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---------- 3. Imballo e confezionamento ----------
|
||||||
|
let packagesHtml = `<div class="wsd-empty mb-2">Nessun imballo associato</div>`;
|
||||||
|
if (packageRows.length) {
|
||||||
|
packagesHtml = `
|
||||||
|
<div class="table-responsive mb-3">
|
||||||
|
<table class="table table-sm table-striped align-middle mb-0 wsd-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:80px;">Pos</th>
|
<th style="width:60px;">Pos</th>
|
||||||
<th>Mescola</th>
|
<th style="width:200px;">Categoria</th>
|
||||||
<th style="width:120px;">Peso g/m</th>
|
<th>Imballo</th>
|
||||||
<th style="width:140px;">Densità</th>
|
|
||||||
<th style="width:150px;">Durezza</th>
|
|
||||||
<th style="width:130px;">Lubr.</th>
|
|
||||||
<th>Note lubr.</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
`;
|
${packageRows
|
||||||
|
.map(
|
||||||
mixRows.forEach((r) => {
|
(p) => `
|
||||||
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>
|
<tr>
|
||||||
<td>${escapeHtml(String(r.mix_position || ""))}</td>
|
<td>${valueOrDash(p.package_position)}</td>
|
||||||
<td>${nomeMescola}</td>
|
<td>${valueOrDash(p.package_category)}</td>
|
||||||
<td>${valueOrDash(r.mix_weight_g_m)}</td>
|
<td>
|
||||||
<td>${valueOrDash(r.required_density)}</td>
|
<div class="fw-bold">${valueOrDash(p.package_name)}</div>
|
||||||
<td>${valueOrDash(r.required_hardness_shore_a)}</td>
|
${p.package_item_code ? `<div class="small text-muted">${escapeHtml(p.package_item_code)}</div>` : ""}
|
||||||
<td>${valueOrDash(r.lubrication_type)}</td>
|
</td>
|
||||||
<td>${valueOrDash(r.lubrication_notes)}</td>
|
</tr>`,
|
||||||
</tr>
|
)
|
||||||
`;
|
.join("")}
|
||||||
});
|
|
||||||
|
|
||||||
mixHtml += `
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerHtml = `
|
const sec3 = wsdSection(
|
||||||
<div class="worksheet-title-box mb-3">
|
"orange",
|
||||||
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap">
|
"3. Imballo e confezionamento",
|
||||||
<div>
|
`${packageRows.length} imball${packageRows.length === 1 ? "o" : "i"}`,
|
||||||
<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>
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
const html = `
|
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="readonly-card">
|
${packagesHtml}
|
||||||
<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>
|
|
||||||
|
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="readonly-card">
|
<div class="wsd-fields">
|
||||||
<div class="readonly-card-header">Controlli produzione</div>
|
${wsdField("Metri per confezione", ws.meters_per_package)}
|
||||||
<div class="readonly-card-body">
|
${wsdField("Tolleranza metri/conf.", ws.meters_per_package_tolerance)}
|
||||||
<div class="readonly-grid">
|
${wsdField("Conf./pezzi per scatola", ws.packages_or_pieces_per_box)}
|
||||||
${renderReadonlyField("Taglio", ws.control_frequency_cut)}
|
${wsdField("Metri per scatola", ws.meters_per_box)}
|
||||||
${renderReadonlyField("Disegno", ws.control_frequency_drawing)}
|
${wsdField("Scatole/conf. per bancale", ws.boxes_or_packages_per_pallet)}
|
||||||
${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>
|
||||||
|
${wsdText("Note metri/confezione", ws.meters_per_package_notes, "Nessuna nota")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
`,
|
||||||
|
"col-12",
|
||||||
|
);
|
||||||
|
|
||||||
<div class="col-lg-6">
|
// ---------- 4. Velocità di produzione ----------
|
||||||
<div class="readonly-card">
|
const sec4 = wsdSection(
|
||||||
<div class="readonly-card-header">Packaging / Confezionamento</div>
|
"purple",
|
||||||
<div class="readonly-card-body">
|
"4. Velocità di produzione",
|
||||||
<div class="readonly-grid">
|
"",
|
||||||
${renderReadonlyField("Codice confezione", ws.requested_package_code)}
|
`
|
||||||
${renderReadonlyField("Metri per confezione", ws.meters_per_package)}
|
<table class="wsd-speed">
|
||||||
${renderReadonlyField("Tolleranza metri/conf.", ws.meters_per_package_tolerance)}
|
<thead>
|
||||||
${renderReadonlyField("Scatola tipo", ws.box_type)}
|
<tr><th></th><th>Prevista</th><th>Effettiva</th></tr>
|
||||||
${renderReadonlyField("Conf./pezzi per scatola", ws.packages_or_pieces_per_box)}
|
</thead>
|
||||||
${renderReadonlyField("Metri per scatola", ws.meters_per_box)}
|
<tbody>
|
||||||
${renderReadonlyField("Bancale tipo", ws.pallet_type)}
|
<tr>
|
||||||
${renderReadonlyField("Scatole/conf. per bancale", ws.boxes_or_packages_per_pallet)}
|
<th>kg/h</th>
|
||||||
</div>
|
<td class="${isEmpty(ws.speed_expected_kg_h) ? "wsd-empty" : ""}">${valueOrDash(ws.speed_expected_kg_h)}</td>
|
||||||
<hr>
|
<td class="${isEmpty(ws.speed_actual_kg_h) ? "wsd-empty" : ""}">${valueOrDash(ws.speed_actual_kg_h)}</td>
|
||||||
<div class="readonly-label mb-2">Note metri / confezione</div>
|
</tr>
|
||||||
<div class="readonly-value" style="white-space:pre-wrap;">${valueOrDash(ws.meters_per_package_notes)}</div>
|
<tr>
|
||||||
</div>
|
<th>m/h</th>
|
||||||
</div>
|
<td class="${isEmpty(ws.speed_expected_m_h) ? "wsd-empty" : ""}">${valueOrDash(ws.speed_expected_m_h)}</td>
|
||||||
</div>
|
<td class="${isEmpty(ws.speed_actual_m_h) ? "wsd-empty" : ""}">${valueOrDash(ws.speed_actual_m_h)}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`,
|
||||||
|
"col-lg-6",
|
||||||
|
);
|
||||||
|
|
||||||
<div class="col-lg-6">
|
// ---------- 5. Note finali ----------
|
||||||
<div class="readonly-card">
|
const sec5 = wsdSection(
|
||||||
<div class="readonly-card-header">Velocità e note</div>
|
"gray",
|
||||||
<div class="readonly-card-body">
|
"5. Note finali",
|
||||||
<div class="readonly-grid">
|
"",
|
||||||
${renderReadonlyField("Vel. prevista kg/h", ws.speed_expected_kg_h)}
|
wsdText("", ws.notes, "Nessuna nota"),
|
||||||
${renderReadonlyField("Vel. effettiva kg/h", ws.speed_actual_kg_h)}
|
"col-lg-6",
|
||||||
${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">
|
// ---------- 6. Mescole ----------
|
||||||
<div class="readonly-card">
|
let mixHtml = `<div class="wsd-empty">Nessuna mescola associata</div>`;
|
||||||
<div class="readonly-card-header">Mescole associate al foglio</div>
|
if (mixRows.length) {
|
||||||
<div class="readonly-card-body">
|
mixHtml = `
|
||||||
${mixHtml}
|
<div class="table-responsive">
|
||||||
</div>
|
<table class="table table-striped align-middle mb-0 wsd-table">
|
||||||
</div>
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:60px;">Pos</th>
|
||||||
|
<th>Mescola</th>
|
||||||
|
<th style="width:120px;">Peso g/m</th>
|
||||||
|
<th style="width:150px;">Densità</th>
|
||||||
|
<th style="width:150px;">Durezza (Sh A)</th>
|
||||||
|
<th style="width:150px;">Lubrificazione</th>
|
||||||
|
<th>Note lubr.</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${mixRows
|
||||||
|
.map(
|
||||||
|
(r) => `
|
||||||
|
<tr>
|
||||||
|
<td>${valueOrDash(r.mix_position)}</td>
|
||||||
|
<td>
|
||||||
|
<div class="fw-bold">${valueOrDash(r.mescola_nome)}</div>
|
||||||
|
${r.mescola_uscita ? `<div class="small text-muted">${escapeHtml(r.mescola_uscita)}</div>` : ""}
|
||||||
|
</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(lookupLabel(lookups, "lubrication_type", r.lubrication_type))}</td>
|
||||||
|
<td>${valueOrDash(r.lubrication_notes)}</td>
|
||||||
|
</tr>`,
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sec6 = wsdSection(
|
||||||
|
"cyan",
|
||||||
|
"6. Mescole associate al foglio",
|
||||||
|
`${mixRows.length} rig${mixRows.length === 1 ? "a" : "he"}`,
|
||||||
|
mixHtml,
|
||||||
|
"col-12",
|
||||||
|
);
|
||||||
|
|
||||||
|
const datiHtml = `
|
||||||
|
${summaryHtml}
|
||||||
|
<div class="row g-3">
|
||||||
|
${sec1}
|
||||||
|
${sec2}
|
||||||
|
${sec3}
|
||||||
|
${sec4}
|
||||||
|
${sec5}
|
||||||
|
${sec6}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// ---------- Tab allegati ----------
|
||||||
const attTabsHtml = Object.keys(WS_ATT_KINDS)
|
const attTabsHtml = Object.keys(WS_ATT_KINDS)
|
||||||
.map((k) => {
|
.map((k) => {
|
||||||
const c = WS_ATT_KINDS[k];
|
const c = WS_ATT_KINDS[k];
|
||||||
@@ -511,7 +620,7 @@
|
|||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
const fullHtml = `
|
return `
|
||||||
${headerHtml}
|
${headerHtml}
|
||||||
|
|
||||||
<div class="ws-tabs">
|
<div class="ws-tabs">
|
||||||
@@ -522,20 +631,57 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ws-tab-pane active" data-pane="dati">
|
<div class="ws-tab-pane active" data-pane="dati">
|
||||||
${html}
|
${datiHtml}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${attPanesHtml}
|
${attPanesHtml}
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
$("#worksheetDetailContainer").html(fullHtml);
|
window.loadWorksheetDetail = function (worksheetId, endpoint) {
|
||||||
loadWorksheetAttachments(ws.idmatrice);
|
endpoint = endpoint || "ajax/worksheet-linked-data.php";
|
||||||
|
|
||||||
|
const $box = $("#worksheetDetailContainer");
|
||||||
|
$box.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 dettaglio foglio:", data);
|
||||||
|
$box.html(
|
||||||
|
'<div class="text-danger">Errore nel caricamento del foglio: ' +
|
||||||
|
escapeHtml(
|
||||||
|
data.message || "nessun dettaglio restituito",
|
||||||
|
) +
|
||||||
|
"</div>",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$box.html(renderWorksheetDetail(data));
|
||||||
|
loadWorksheetAttachments((data.worksheet || {}).idmatrice);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("Catch loadMatriceWorksheets:", err);
|
console.error("Errore dettaglio foglio:", err);
|
||||||
|
$box.html(
|
||||||
$("#worksheetsListContainer").html(
|
'<div class="text-danger">Errore nel caricamento del foglio: ' +
|
||||||
'<div class="text-danger">Errore nel caricamento dei fogli: ' +
|
|
||||||
escapeHtml(err.message || "errore JavaScript/fetch") +
|
escapeHtml(err.message || "errore JavaScript/fetch") +
|
||||||
"</div>",
|
"</div>",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -134,6 +134,188 @@
|
|||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
Dettaglio foglio in sola lettura (stesse sezioni e colori
|
||||||
|
di manage-worksheet.php)
|
||||||
|
================================================================ */
|
||||||
|
.wsd-summary {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-sum-item {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e5edf6;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.04);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-sum-value {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #111827;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-section {
|
||||||
|
border: 1px solid #e7edf5;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-head-badge {
|
||||||
|
background: rgba(255, 255, 255, .7);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
font-size: .78rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-body {
|
||||||
|
padding: 14px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-blue .wsd-head {
|
||||||
|
background: linear-gradient(135deg, #dbeafe, #bfdbfe);
|
||||||
|
color: #123b7a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-green .wsd-head {
|
||||||
|
background: linear-gradient(135deg, #dcfce7, #bbf7d0);
|
||||||
|
color: #14532d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-orange .wsd-head {
|
||||||
|
background: linear-gradient(135deg, #ffedd5, #fed7aa);
|
||||||
|
color: #9a3412;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-purple .wsd-head {
|
||||||
|
background: linear-gradient(135deg, #ede9fe, #ddd6fe);
|
||||||
|
color: #5b21b6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-gray .wsd-head {
|
||||||
|
background: linear-gradient(135deg, #f3f4f6, #e5e7eb);
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-cyan .wsd-head {
|
||||||
|
background: linear-gradient(135deg, #cffafe, #a5f3fc);
|
||||||
|
color: #155e75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-fields {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
|
||||||
|
gap: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-label {
|
||||||
|
font-size: .75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .03em;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-value {
|
||||||
|
font-size: 1.02rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #111827;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-empty {
|
||||||
|
color: #b6bec9 !important;
|
||||||
|
font-weight: 400 !important;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-text-block {
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-text {
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid #e9eef5;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-meta {
|
||||||
|
margin-top: 14px;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px dashed #e5e7eb;
|
||||||
|
font-size: .82rem;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-table thead th {
|
||||||
|
background: #eef4fb !important;
|
||||||
|
color: #163a63;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: .88rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-speed {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-speed thead th {
|
||||||
|
font-size: .75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #6b7280;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-speed tbody th {
|
||||||
|
width: 70px;
|
||||||
|
color: #5b21b6;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsd-speed td {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #111827;
|
||||||
|
background: #f8f7ff;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.wsd-summary {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- MODALE LISTA FOGLI DI LAVORO -->
|
<!-- MODALE LISTA FOGLI DI LAVORO -->
|
||||||
@@ -171,7 +353,7 @@
|
|||||||
<h5 class="modal-title mb-0">
|
<h5 class="modal-title mb-0">
|
||||||
<i class="fa-solid fa-file-lines me-2"></i>Dettaglio foglio di lavoro
|
<i class="fa-solid fa-file-lines me-2"></i>Dettaglio foglio di lavoro
|
||||||
</h5>
|
</h5>
|
||||||
<small class="text-muted">Visualizzazione completa in sola lettura</small>
|
<small class="text-muted">Sola lettura · per modificare usa "Apri foglio completo"</small>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user