From 5d4a21db639814facbbc2cb6590d13e8912c20ec Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Wed, 11 Mar 2026 15:58:52 +0000 Subject: [PATCH] =?UTF-8?q?ui:=20riorganizza=20quantit=C3=A0=20in=20lista?= =?UTF-8?q?=20inventario?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Numero grande e visibile (es. '10') - Sotto: unità + dettaglio confezione (es. 'conf da 36g') - Sotto: proporzione rimasta in piccolo (es. '¼') - Stesso layout per dashboard compact items - Frazione esclusa per unit=conf (dove default_quantity è il peso) - Cache-busting v=20260311c --- assets/css/style.css | 61 ++++++++++++++++++++++++++++++++++---------- assets/js/app.js | 46 ++++++++++++++++++++++++++++----- index.html | 4 +-- 3 files changed, 90 insertions(+), 21 deletions(-) diff --git a/assets/css/style.css b/assets/css/style.css index 7f846f5..a8c2084 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -1700,7 +1700,7 @@ body { display: flex; flex-direction: column; align-items: flex-end; - gap: 2px; + gap: 1px; flex-shrink: 0; } @@ -1711,6 +1711,19 @@ body { white-space: nowrap; } +.inv-qty-value small { + font-size: 0.7rem; + font-weight: 600; + color: #4b5563; +} + +.inv-qty-pkg-detail { + font-size: 0.65rem; + color: #6b7280; + font-weight: 500; + white-space: nowrap; +} + .inv-expiry-small { font-size: 0.7rem; color: var(--text-muted); @@ -2598,27 +2611,49 @@ body { background: #ea580c; } -/* ===== LARGER QUANTITY IN INVENTORY ===== */ +/* ===== STRUCTURED QUANTITY IN INVENTORY ===== */ .inv-qty-col { display: flex; flex-direction: column; - align-items: flex-end; - gap: 2px; + align-items: center; flex-shrink: 0; + min-width: 56px; + background: #ecfdf5; + border-radius: 14px; + padding: 6px 12px; } -.inv-qty-prominent { - font-size: 1.2rem; - font-weight: 800; +.inv-qty-number { + font-size: 1.55rem; + font-weight: 900; color: var(--primary); - white-space: nowrap; - background: #d1fae5; - padding: 4px 12px; - border-radius: 20px; - flex-shrink: 0; + line-height: 1.1; + letter-spacing: -0.5px; } -/* Package fraction indicators */ +.inv-qty-unit { + font-size: 0.7rem; + font-weight: 600; + color: #4b5563; + white-space: nowrap; + line-height: 1.2; + text-align: center; +} + +.inv-qty-pkg { + font-weight: 500; + color: #6b7280; +} + +.inv-qty-frac { + font-size: 0.75rem; + font-weight: 700; + color: #9ca3af; + line-height: 1; + margin-top: 1px; +} + +/* Package fraction indicators (used elsewhere) */ .pkg-fraction { font-size: 1rem; font-weight: 700; diff --git a/assets/js/app.js b/assets/js/app.js index 8b89ec2..6aadb59 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -763,7 +763,7 @@ function renderDashItem(item) { const days = daysUntilExpiry(item.expiry_date); const isExpired = days < 0; const isExpiring = !isExpired && days <= 7; - const qtyDisplay = formatQuantity(item.quantity, item.unit, item.default_quantity, item.package_unit); + const parts = formatQuantityParts(item.quantity, item.unit, item.default_quantity, item.package_unit); let expiryLabel = ''; if (item.expiry_date) { @@ -784,7 +784,8 @@ function renderDashItem(item) { ${item.brand ? `
${escapeHtml(item.brand)}
` : ''}
- ${qtyDisplay} + ${parts.mainQty} ${parts.unitLabel} + ${parts.packageDetail ? `${parts.packageDetail}` : ''} ${expiryLabel ? `${expiryLabel}` : ''}
`; @@ -823,6 +824,39 @@ function formatQuantity(qty, unit, defaultQty, packageUnit) { return result; } +// Structured quantity display for inventory cards. +// Returns { mainQty: '10', unitLabel: 'conf', packageDetail: 'da 36g', fraction: '¼' } +function formatQuantityParts(qty, unit, defaultQty, packageUnit) { + const n = parseFloat(qty) || 0; + const unitLabels = { 'pz': 'pz', 'kg': 'kg', 'g': 'g', 'l': 'L', 'ml': 'ml', 'conf': 'conf' }; + const label = unitLabels[unit] || unit || 'pz'; + let mainQty; + if (n === Math.floor(n)) mainQty = `${Math.floor(n)}`; + else if (unit === 'pz' || unit === 'conf') mainQty = `${Math.round(n)}`; + else mainQty = `${n.toFixed(1)}`; + + let packageDetail = ''; + if (unit === 'conf' && packageUnit && defaultQty > 0) { + const pkgLabel = unitLabels[packageUnit] || packageUnit; + packageDetail = `da ${defaultQty}${pkgLabel}`; + } + + let fraction = ''; + // For conf, defaultQty is the package SIZE (e.g. 300g), not a count; fraction doesn't apply + if (unit !== 'conf' && defaultQty && defaultQty > 1) { + const d = parseFloat(defaultQty); + const ratio = n / d; + const remainder = ratio - Math.floor(ratio); + if (remainder >= 0.1 && remainder <= 0.9) { + if (remainder < 0.38) fraction = '¼'; + else if (remainder < 0.62) fraction = '½'; + else fraction = '¾'; + } + } + + return { mainQty, unitLabel: label, packageDetail, fraction }; +} + // Show package fraction: only ¼, ½, ¾ when there's a partial package. // Returns '' if quantity maps to whole packages or fraction is not meaningful. function formatPackageFraction(qty, defaultQty) { @@ -862,8 +896,7 @@ function renderInventoryItem(item) { const days = daysUntilExpiry(item.expiry_date); const isExpired = days < 0; const isExpiring = !isExpired && days <= 7; - const qtyDisplay = formatQuantity(item.quantity, item.unit, item.default_quantity, item.package_unit); - const pkgFrac = formatPackageFraction(item.quantity, item.default_quantity); + const parts = formatQuantityParts(item.quantity, item.unit, item.default_quantity, item.package_unit); let expiryBadge = ''; if (item.expiry_date) { @@ -890,8 +923,9 @@ function renderInventoryItem(item) {
- ${qtyDisplay} - ${pkgFrac ? `${pkgFrac}` : ''} + ${parts.mainQty} + ${parts.unitLabel}${parts.packageDetail ? ` ${parts.packageDetail}` : ''} + ${parts.fraction ? `${parts.fraction}` : ''}
`; } diff --git a/index.html b/index.html index 46e7d27..e9b398f 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ 🏠 Dispensa Manager - + @@ -750,6 +750,6 @@ - +