ui: riorganizza quantità in lista inventario

- 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
This commit is contained in:
dadaloop82
2026-03-11 15:58:52 +00:00
parent e6a9fb06a7
commit 5d4a21db63
3 changed files with 90 additions and 21 deletions
+48 -13
View File
@@ -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;
+40 -6
View File
@@ -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 ? `<div class="inv-brand">${escapeHtml(item.brand)}</div>` : ''}
</div>
<div class="inv-qty-right">
<span class="inv-qty-value">${qtyDisplay}</span>
<span class="inv-qty-value">${parts.mainQty} <small>${parts.unitLabel}</small></span>
${parts.packageDetail ? `<span class="inv-qty-pkg-detail">${parts.packageDetail}</span>` : ''}
${expiryLabel ? `<span class="inv-expiry-small ${isExpired ? 'expired' : isExpiring ? 'expiring' : ''}">${expiryLabel}</span>` : ''}
</div>
</div>`;
@@ -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) {
</div>
</div>
<div class="inv-qty-col">
<span class="inv-qty-prominent">${qtyDisplay}</span>
${pkgFrac ? `<span class="inv-pkg-frac">${pkgFrac}</span>` : ''}
<span class="inv-qty-number">${parts.mainQty}</span>
<span class="inv-qty-unit">${parts.unitLabel}${parts.packageDetail ? ` <span class="inv-qty-pkg">${parts.packageDetail}</span>` : ''}</span>
${parts.fraction ? `<span class="inv-qty-frac">${parts.fraction}</span>` : ''}
</div>
</div>`;
}
+2 -2
View File
@@ -9,7 +9,7 @@
<title>🏠 Dispensa Manager</title>
<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="stylesheet" href="assets/css/style.css?v=20260311b">
<link rel="stylesheet" href="assets/css/style.css?v=20260311c">
<!-- QuaggaJS for barcode scanning -->
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2@1.8.4/dist/quagga.min.js"></script>
</head>
@@ -750,6 +750,6 @@
<div class="modal-content" id="modal-content" onclick="event.stopPropagation()"></div>
</div>
<script src="assets/js/app.js?v=20260311b"></script>
<script src="assets/js/app.js?v=20260311c"></script>
</body>
</html>