Confezioni: permetti uso parziale in sub-unità (ml, g) con switch unità

This commit is contained in:
dadaloop82
2026-03-13 14:42:39 +00:00
parent 9062c498de
commit 3733adf2e9
4 changed files with 155 additions and 19 deletions
+24
View File
@@ -826,6 +826,30 @@ body {
gap: 16px;
}
.use-unit-switch {
display: flex;
gap: 0;
margin-bottom: 12px;
border-radius: var(--radius-sm);
overflow: hidden;
border: 2px solid var(--primary);
}
.use-unit-btn {
flex: 1;
padding: 10px 12px;
border: none;
background: var(--bg);
color: var(--text);
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.use-unit-btn.active {
background: var(--primary);
color: #fff;
}
.use-all-btn {
min-height: 70px;
font-size: 1.15rem;
+116 -8
View File
@@ -2893,8 +2893,10 @@ async function submitAdd(e) {
// ===== USE FROM INVENTORY =====
function showUseForm() {
renderUsePreview();
_useConfMode = null; // reset
document.getElementById('use-quantity').value = 1;
document.getElementById('use-location').value = 'dispensa';
document.getElementById('use-unit-switch').style.display = 'none';
// Reset location buttons
document.querySelectorAll('#page-use .loc-btn').forEach(b => b.classList.remove('active'));
@@ -2918,17 +2920,22 @@ function renderUsePreview() {
`;
}
// Conf-mode tracking for USE form
let _useConfMode = null; // null = normal, { packageSize, packageUnit, totalSub, unit } = conf mode active
async function loadUseInventoryInfo() {
try {
const data = await api('inventory_list');
const items = (data.inventory || []).filter(i => i.product_id == currentProduct.id);
const infoEl = document.getElementById('use-inventory-info');
const unitSwitch = document.getElementById('use-unit-switch');
if (items.length > 0) {
infoEl.innerHTML = '<strong>📦 Disponibile:</strong> ' + items.map(i => {
const loc = LOCATIONS[i.location] || { icon: '📦', label: i.location };
return `${loc.icon} ${loc.label}: ${i.quantity} ${i.unit}`;
}).join(' · ');
if (items.length === 0) {
infoEl.innerHTML = '⚠️ Prodotto non presente nell\'inventario.';
unitSwitch.style.display = 'none';
_useConfMode = null;
return;
}
// Auto-select the first available location
const firstLoc = items[0].location;
@@ -2939,14 +2946,103 @@ async function loadUseInventoryInfo() {
b.classList.add('active');
}
});
const unit = items[0].unit || 'pz';
const pkgSize = parseFloat(items[0].default_quantity) || 0;
const pkgUnit = items[0].package_unit || '';
const isConf = unit === 'conf' && pkgSize > 0 && pkgUnit;
if (isConf) {
// --- CONF MODE: show sub-unit controls ---
const totalConf = items.reduce((s, i) => s + parseFloat(i.quantity), 0);
const totalSub = totalConf * pkgSize;
const unitLabels = { 'ml': 'ml', 'l': 'L', 'g': 'g', 'kg': 'kg', 'pz': 'pz' };
const subLabel = unitLabels[pkgUnit] || pkgUnit;
_useConfMode = { packageSize: pkgSize, packageUnit: pkgUnit, totalSub, totalConf, subLabel };
// Show inventory info with sub-unit total
infoEl.innerHTML = '<strong>📦 Disponibile:</strong> ' + items.map(i => {
const loc = LOCATIONS[i.location] || { icon: '📦', label: i.location };
const confQty = parseFloat(i.quantity);
const subQty = Math.round(confQty * pkgSize);
const confDisplay = confQty === Math.floor(confQty) ? Math.floor(confQty) : confQty.toFixed(1);
return `${loc.icon} ${loc.label}: ${confDisplay} conf (${subQty}${subLabel})`;
}).join(' · ');
// Show unit switch
unitSwitch.style.display = 'flex';
document.getElementById('use-unit-sub').textContent = subLabel;
// Default to sub-unit mode
switchUseUnit('sub');
} else {
infoEl.innerHTML = '⚠️ Prodotto non presente nell\'inventario.';
// --- NORMAL MODE ---
_useConfMode = null;
unitSwitch.style.display = 'none';
infoEl.innerHTML = '<strong>📦 Disponibile:</strong> ' + items.map(i => {
const loc = LOCATIONS[i.location] || { icon: '📦', label: i.location };
return `${loc.icon} ${loc.label}: ${i.quantity} ${i.unit}`;
}).join(' · ');
document.getElementById('use-quantity').value = 1;
document.getElementById('use-partial-hint').textContent = 'Oppure specifica la quantità usata:';
}
} catch(e) {
console.error(e);
}
}
function switchUseUnit(mode) {
const subBtn = document.getElementById('use-unit-sub');
const confBtn = document.getElementById('use-unit-conf');
const qtyInput = document.getElementById('use-quantity');
const hint = document.getElementById('use-partial-hint');
if (mode === 'sub') {
subBtn.classList.add('active');
confBtn.classList.remove('active');
_useConfMode._activeUnit = 'sub';
const step = getSubUnitStep(_useConfMode.packageUnit);
qtyInput.value = step;
qtyInput.step = step;
hint.textContent = `Quantità in ${_useConfMode.subLabel} (totale: ${Math.round(_useConfMode.totalSub)}${_useConfMode.subLabel})`;
} else {
confBtn.classList.add('active');
subBtn.classList.remove('active');
_useConfMode._activeUnit = 'conf';
qtyInput.value = 1;
qtyInput.step = 0.5;
hint.textContent = `Confezioni da ${_useConfMode.packageSize}${_useConfMode.subLabel} (hai ${_useConfMode.totalConf.toFixed(1)} conf)`;
}
}
function getSubUnitStep(pkgUnit) {
switch (pkgUnit) {
case 'ml': return 100;
case 'l': return 0.25;
case 'g': return 50;
case 'kg': return 0.1;
default: return 1;
}
}
function adjustUseQty(direction) {
const input = document.getElementById('use-quantity');
let val = parseFloat(input.value) || 0;
let step;
if (_useConfMode && _useConfMode._activeUnit === 'sub') {
step = getSubUnitStep(_useConfMode.packageUnit);
} else if (_useConfMode && _useConfMode._activeUnit === 'conf') {
step = 0.5;
} else {
step = 0.5;
}
val = Math.max(step, val + direction * step);
input.value = Math.round(val * 1000) / 1000;
}
function selectUseLocation(btn, loc) {
btn.parentElement.querySelectorAll('.loc-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
@@ -2981,7 +3077,18 @@ async function submitUse(e) {
e.preventDefault();
showLoading(true);
try {
const qty = parseFloat(document.getElementById('use-quantity').value) || 1;
let qty = parseFloat(document.getElementById('use-quantity').value) || 1;
let displayQty = qty;
let displayUnit = '';
// Convert sub-unit to conf if needed
if (_useConfMode && _useConfMode._activeUnit === 'sub') {
displayUnit = _useConfMode.subLabel;
qty = qty / _useConfMode.packageSize; // convert to conf
} else if (_useConfMode && _useConfMode._activeUnit === 'conf') {
displayUnit = 'conf';
}
const result = await api('inventory_use', {}, 'POST', {
product_id: currentProduct.id,
quantity: qty,
@@ -2989,7 +3096,8 @@ async function submitUse(e) {
});
showLoading(false);
if (result.success) {
showToast(`📤 Usato ${qty} di ${currentProduct.name}. Rimasti: ${result.remaining}`, 'success');
const usedText = displayUnit ? `${displayQty}${displayUnit}` : displayQty;
showToast(`📤 Usato ${usedText} di ${currentProduct.name}`, 'success');
if (result.added_to_bring) {
setTimeout(() => showToast('🛒 Prodotto finito → aggiunto a Bring!', 'info'), 1500);
}
BIN
View File
Binary file not shown.
+9 -5
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=20260313d">
<link rel="stylesheet" href="assets/css/style.css?v=20260313g">
<!-- QuaggaJS for barcode scanning -->
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2@1.8.4/dist/quagga.min.js"></script>
</head>
@@ -240,16 +240,20 @@
</div>
<div class="form-group">
<label>Quanto hai usato?</label>
<div class="use-unit-switch" id="use-unit-switch" style="display:none">
<button type="button" class="use-unit-btn active" id="use-unit-sub" onclick="switchUseUnit('sub')"></button>
<button type="button" class="use-unit-btn" id="use-unit-conf" onclick="switchUseUnit('conf')">Confezioni</button>
</div>
<div class="use-options">
<button type="button" class="btn btn-large btn-danger full-width use-all-btn" onclick="submitUseAll()">
🗑️ Usato TUTTO / Finito
</button>
<div class="use-partial">
<p>Oppure specifica la quantità usata:</p>
<p id="use-partial-hint">Oppure specifica la quantità usata:</p>
<div class="qty-control">
<button type="button" class="qty-btn" onclick="adjustQty('use-quantity', -0.5)"></button>
<button type="button" class="qty-btn" id="use-qty-minus" onclick="adjustUseQty(-1)"></button>
<input type="number" id="use-quantity" value="1" min="0.1" step="any" class="qty-input">
<button type="button" class="qty-btn" onclick="adjustQty('use-quantity', 0.5)">+</button>
<button type="button" class="qty-btn" id="use-qty-plus" onclick="adjustUseQty(1)">+</button>
</div>
<button type="submit" class="btn btn-large btn-warning full-width mt-2">📤 Usa questa quantità</button>
</div>
@@ -874,6 +878,6 @@
</div>
</div>
<script src="assets/js/app.js?v=20260313f"></script>
<script src="assets/js/app.js?v=20260313g"></script>
</body>
</html>