Dashboard: sezione Prodotti Aperti (conf parzialmente usate)

This commit is contained in:
dadaloop82
2026-03-13 15:02:27 +00:00
parent ac76d02cb6
commit 948e851b71
5 changed files with 72 additions and 2 deletions
+10
View File
@@ -753,6 +753,15 @@ function getStats(PDO $db): void {
ORDER BY i.expiry_date ASC ORDER BY i.expiry_date ASC
")->fetchAll(); ")->fetchAll();
// Opened (partially used conf items)
$opened = $db->query("
SELECT i.*, p.name, p.brand, p.category, p.unit, p.default_quantity, p.package_unit, p.image_url
FROM inventory i JOIN products p ON i.product_id = p.id
WHERE p.unit = 'conf' AND p.default_quantity > 0 AND p.package_unit IS NOT NULL
AND i.quantity > 0 AND CAST(i.quantity AS REAL) != CAST(CAST(i.quantity AS INTEGER) AS REAL)
ORDER BY i.updated_at DESC
")->fetchAll();
echo json_encode([ echo json_encode([
'total_products' => (int)$totalProducts, 'total_products' => (int)$totalProducts,
'total_items' => (float)$totalItems, 'total_items' => (float)$totalItems,
@@ -761,6 +770,7 @@ function getStats(PDO $db): void {
'recent_out' => (int)$recentOut, 'recent_out' => (int)$recentOut,
'expiring_soon' => $expiring, 'expiring_soon' => $expiring,
'expired' => $expired, 'expired' => $expired,
'opened' => $opened,
]); ]);
} }
+16
View File
@@ -2908,6 +2908,22 @@ body {
color: #92400e; color: #92400e;
} }
/* ===== OPENED PRODUCTS SECTION ===== */
.alert-opened {
background: #eff6ff;
border-color: #3b82f6;
}
.alert-opened h3 {
color: #1e40af;
}
.alert-item-badge.opened {
background: #3b82f6;
color: #fff;
font-size: 0.7rem;
}
.review-hint { .review-hint {
font-size: 0.8rem; font-size: 0.8rem;
color: #92400e; color: #92400e;
+38
View File
@@ -813,6 +813,44 @@ async function loadDashboard() {
// Review suspicious quantities // Review suspicious quantities
loadReviewItems(); loadReviewItems();
// Opened (partially used) products
const openedSection = document.getElementById('alert-opened');
const openedList = document.getElementById('opened-list');
if (statsData.opened && statsData.opened.length > 0) {
openedSection.style.display = 'block';
openedList.innerHTML = statsData.opened.map(item => {
const locInfo = LOCATIONS[item.location] || { icon: '📦', label: item.location };
const qty = parseFloat(item.quantity);
const pkgSize = parseFloat(item.default_quantity);
const pkgUnit = item.package_unit;
const wholeConf = Math.floor(qty + 0.001);
const frac = Math.round((qty - wholeConf) * 1000) / 1000;
const remainderAmt = frac * pkgSize;
const remainderText = formatSubRemainder(remainderAmt, pkgUnit);
let qtyText = '';
if (wholeConf > 0) {
const unitLabels = { 'ml': 'ml', 'l': 'L', 'g': 'g', 'kg': 'kg' };
const pkgLabel = unitLabels[pkgUnit] || pkgUnit;
qtyText = `${wholeConf} conf (da ${pkgSize}${pkgLabel}) + ${remainderText}`;
} else {
qtyText = remainderText;
}
return `
<div class="alert-item alert-item-clickable" onclick="showAlertItemDetail(${item.id}, ${item.product_id})">
<div class="alert-item-info">
<span class="alert-item-name">${escapeHtml(item.name)}</span>
${item.brand ? `<span class="alert-item-brand">${escapeHtml(item.brand)}</span>` : ''}
</div>
<div class="alert-item-badges">
<span class="alert-item-qty">${locInfo.icon} ${locInfo.label}</span>
<span class="alert-item-badge opened">${qtyText}</span>
</div>
</div>`;
}).join('');
} else {
openedSection.style.display = 'none';
}
} catch (err) { } catch (err) {
console.error('Dashboard load error:', err); console.error('Dashboard load error:', err);
BIN
View File
Binary file not shown.
+8 -2
View File
@@ -9,7 +9,7 @@
<title>Dispensa Manager</title> <title>Dispensa Manager</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏠</text></svg>"> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏠</text></svg>">
<link rel="stylesheet" href="assets/css/style.css?v=20260313g"> <link rel="stylesheet" href="assets/css/style.css?v=20260313k">
<!-- QuaggaJS for barcode scanning --> <!-- QuaggaJS for barcode scanning -->
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2@1.8.4/dist/quagga.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2@1.8.4/dist/quagga.min.js"></script>
</head> </head>
@@ -69,6 +69,12 @@
<div id="expiring-list"></div> <div id="expiring-list"></div>
</div> </div>
<!-- Opened (partially used) products -->
<div class="alert-section alert-opened" id="alert-opened" style="display:none">
<h3>📦 Prodotti Aperti</h3>
<div id="opened-list"></div>
</div>
<!-- Review suspicious quantities --> <!-- Review suspicious quantities -->
<div class="alert-section alert-review" id="alert-review" style="display:none"> <div class="alert-section alert-review" id="alert-review" style="display:none">
<h3>🔍 Da revisionare</h3> <h3>🔍 Da revisionare</h3>
@@ -878,6 +884,6 @@
</div> </div>
</div> </div>
<script src="assets/js/app.js?v=20260313j"></script> <script src="assets/js/app.js?v=20260313k"></script>
</body> </body>
</html> </html>