diff --git a/api/index.php b/api/index.php
index f948b8e..3e7a394 100644
--- a/api/index.php
+++ b/api/index.php
@@ -2395,7 +2395,7 @@ function listTransactions(PDO $db): void {
$productId = $_GET['product_id'] ?? '';
$query = "
- SELECT t.*, p.name, p.brand, p.unit
+ SELECT t.*, p.name, p.brand, p.unit, p.default_quantity, p.package_unit
FROM transactions t
JOIN products p ON t.product_id = p.id
";
@@ -7658,8 +7658,11 @@ function smartShopping(PDO $db): void {
$reasons[] = 'Esaurito';
$score += 30;
} else {
- // Rarely used or not used recently — skip
- continue;
+ // Product is depleted. Even without a proven usage pattern, always
+ // show at minimum 'low' so the user can restock it.
+ $urgency = 'low';
+ $reasons[] = 'Esaurito';
+ $score += 15;
}
}
diff --git a/assets/js/app.js b/assets/js/app.js
index 74632e0..63697cd 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -8571,9 +8571,27 @@ async function loadUseInventoryInfo() {
// Show unit switch
unitSwitch.style.display = 'flex';
document.getElementById('use-unit-sub').textContent = subLabel;
-
- // Default to sub-unit mode
- switchUseUnit('sub');
+
+ // Default to conf mode — users think in packages; scale auto-switches to sub
+ switchUseUnit('conf');
+
+ // Fraction shortcut buttons for conf mode (½, 1, 2 packages)
+ const existingConfFrac = document.getElementById('conf-fraction-btns');
+ if (existingConfFrac) existingConfFrac.remove();
+ const confFracDiv = document.createElement('div');
+ confFracDiv.id = 'conf-fraction-btns';
+ confFracDiv.className = 'pz-fraction-btns';
+ const maxConf = Math.min(4, Math.ceil(_useConfMode.totalConf));
+ const confFracs = [0.25, 0.5, 1];
+ if (maxConf >= 2) confFracs.push(2);
+ confFracDiv.innerHTML = `
${
+ confFracs.filter(f => f <= _useConfMode.totalConf + 0.01).map(f => {
+ const label = f === 0.25 ? '¼' : f === 0.5 ? '½' : f;
+ return ``;
+ }).join('')
+ }
`;
+ document.querySelector('#page-use .use-partial').appendChild(confFracDiv);
+
// Trigger a live-box refresh with the latest reading if on scale
if (_scaleLatestWeight) _scaleAutoFillUse(_scaleLatestWeight);
} else {
@@ -8625,6 +8643,10 @@ function switchUseUnit(mode) {
const qtyInput = document.getElementById('use-quantity');
const hint = document.getElementById('use-partial-hint');
+ // Show/hide fraction buttons depending on mode
+ const confFracBtns = document.getElementById('conf-fraction-btns');
+ const pzFracBtns = document.getElementById('pz-fraction-btns');
+
if (mode === 'sub') {
subBtn.classList.add('active');
confBtn.classList.remove('active');
@@ -8634,17 +8656,28 @@ function switchUseUnit(mode) {
qtyInput.step = 'any';
qtyInput.min = 1;
hint.textContent = t('recipes.quantity_in_total', { unit: _useConfMode.subLabel, total: `${Math.round(_useConfMode.totalSub)}${_useConfMode.subLabel}` });
+ if (confFracBtns) confFracBtns.style.display = 'none';
} else {
confBtn.classList.add('active');
subBtn.classList.remove('active');
_useConfMode._activeUnit = 'conf';
- qtyInput.value = 1;
+ qtyInput.value = Math.min(1, _useConfMode.totalConf); // start at 1 or max if < 1
qtyInput.step = 'any';
- qtyInput.min = 0.1;
+ qtyInput.min = 0.25;
hint.textContent = t('recipes.packs_of_have', { size: `${_useConfMode.packageSize}${_useConfMode.subLabel}`, count: _useConfMode.totalConf.toFixed(1) });
+ if (confFracBtns) confFracBtns.style.display = '';
}
}
+function setConfFraction(f) {
+ const input = document.getElementById('use-quantity');
+ if (!input) return;
+ input.value = Math.min(f, _useConfMode?.totalConf ?? f);
+ document.querySelectorAll('#conf-fraction-btns .frac-btn').forEach(b =>
+ b.classList.toggle('active', parseFloat(b.dataset.frac) === f)
+ );
+}
+
function getSubUnitStep(pkgUnit) {
switch (pkgUnit) {
case 'ml': return 50;
@@ -11927,7 +11960,10 @@ async function loadLog(more = false) {
html += `${icon}`;
html += ``;
html += `
${escapeHtml(tx.name)}${brand}${undone ? ` ${t('log.undone_badge')}` : ''}
`;
- html += `
${typeLabel} ${tx.type !== 'bring' ? (tx.quantity + ' ' + (tx.unit || '')) + ' · ' : ''}${locStr}${notes} · ${timeStr}
`;
+ const txQtyStr = tx.type !== 'bring'
+ ? formatQuantity(parseFloat(tx.quantity), tx.unit, tx.default_quantity, tx.package_unit) + ' · '
+ : '';
+ html += `
${typeLabel} ${txQtyStr}${locStr}${notes} · ${timeStr}
`;
html += recipeNote;
html += `
`;
if (canUndo) {