chore: auto-merge develop → main
Triggered by: bff22d4 fix(i18n): translate antiwaste title in IT/DE + use quantity validation
This commit is contained in:
@@ -384,6 +384,14 @@ body {
|
|||||||
from { opacity: 0; transform: translateY(8px); }
|
from { opacity: 0; transform: translateY(8px); }
|
||||||
to { opacity: 1; transform: translateY(0); }
|
to { opacity: 1; transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
@keyframes inputShake {
|
||||||
|
0%, 100% { transform: translateX(0); }
|
||||||
|
20% { transform: translateX(-6px); }
|
||||||
|
40% { transform: translateX(6px); }
|
||||||
|
60% { transform: translateX(-4px); }
|
||||||
|
80% { transform: translateX(4px); }
|
||||||
|
}
|
||||||
|
.input-shake { animation: inputShake 0.5s ease; border-color: var(--danger, #ef4444) !important; }
|
||||||
|
|
||||||
/* ===== PAGE HEADER ===== */
|
/* ===== PAGE HEADER ===== */
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
+34
-6
@@ -6739,7 +6739,23 @@ function adjustUseQty(direction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val = Math.max(step, val + direction * step);
|
val = Math.max(step, val + direction * step);
|
||||||
input.value = Math.round(val * 1000) / 1000;
|
val = Math.round(val * 1000) / 1000;
|
||||||
|
|
||||||
|
// Cap at max available at selected location (in current unit)
|
||||||
|
const selectedLoc = document.getElementById('use-location')?.value;
|
||||||
|
if (selectedLoc && _useCurrentItems.length > 0) {
|
||||||
|
const locItems = _useCurrentItems.filter(i => i.location === selectedLoc);
|
||||||
|
const maxQtyAtLoc = locItems.reduce((s, i) => s + parseFloat(i.quantity || 0), 0);
|
||||||
|
if (maxQtyAtLoc > 0) {
|
||||||
|
// Convert to sub-unit for comparison if needed
|
||||||
|
const maxInCurrentUnit = (_useConfMode && _useConfMode._activeUnit === 'sub')
|
||||||
|
? maxQtyAtLoc * _useConfMode.packageSize
|
||||||
|
: maxQtyAtLoc;
|
||||||
|
val = Math.min(val, Math.round(maxInCurrentUnit * 1000) / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.value = val;
|
||||||
// Sync fraction button highlight if visible
|
// Sync fraction button highlight if visible
|
||||||
const newVal = parseFloat(input.value);
|
const newVal = parseFloat(input.value);
|
||||||
document.querySelectorAll('#pz-fraction-btns .frac-btn').forEach(b => {
|
document.querySelectorAll('#pz-fraction-btns .frac-btn').forEach(b => {
|
||||||
@@ -7235,12 +7251,8 @@ async function submitUse(e) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (_useSubmitting) return; // prevent double-submit from scale auto-confirm
|
if (_useSubmitting) return; // prevent double-submit from scale auto-confirm
|
||||||
_useSubmitting = true;
|
_useSubmitting = true;
|
||||||
// Stop timers but KEEP _scaleLastConfirmedGrams: this prevents the scale from
|
|
||||||
// re-triggering another auto-submit while the product is still on the plate.
|
|
||||||
// (Calling _cancelScaleAutoConfirm(false) would reset the sentinel to null,
|
|
||||||
// allowing the same weight to start a new 10-second cycle immediately.)
|
|
||||||
_cancelScaleTimersOnly();
|
_cancelScaleTimersOnly();
|
||||||
_scaleStabilityVal = null; // reset sentinel so a new DIFFERENT weight restarts correctly
|
_scaleStabilityVal = null;
|
||||||
showLoading(true);
|
showLoading(true);
|
||||||
try {
|
try {
|
||||||
let qty = parseFloat(document.getElementById('use-quantity').value) || 1;
|
let qty = parseFloat(document.getElementById('use-quantity').value) || 1;
|
||||||
@@ -7254,6 +7266,22 @@ async function submitUse(e) {
|
|||||||
} else if (_useConfMode && _useConfMode._activeUnit === 'conf') {
|
} else if (_useConfMode && _useConfMode._activeUnit === 'conf') {
|
||||||
displayUnit = 'conf';
|
displayUnit = 'conf';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Validate: cannot use more than available at selected location ─────────
|
||||||
|
const selectedLoc = document.getElementById('use-location').value;
|
||||||
|
const locItems = _useCurrentItems.filter(i => i.location === selectedLoc);
|
||||||
|
const maxQtyAtLoc = locItems.reduce((s, i) => s + parseFloat(i.quantity || 0), 0);
|
||||||
|
if (maxQtyAtLoc > 0 && qty > maxQtyAtLoc + 0.001) {
|
||||||
|
showLoading(false);
|
||||||
|
_useSubmitting = false;
|
||||||
|
showToast(t('use.error_exceeds_stock'), 'error');
|
||||||
|
// Shake the input to make it obvious
|
||||||
|
const inp = document.getElementById('use-quantity');
|
||||||
|
inp.classList.add('input-shake');
|
||||||
|
setTimeout(() => inp.classList.remove('input-shake'), 600);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const result = await api('inventory_use', {}, 'POST', {
|
const result = await api('inventory_use', {}, 'POST', {
|
||||||
product_id: currentProduct.id,
|
product_id: currentProduct.id,
|
||||||
|
|||||||
+1
-1
@@ -1318,6 +1318,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="assets/js/app.js?v=20260504b"></script>
|
<script src="assets/js/app.js?v=20260504c"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -242,7 +242,8 @@
|
|||||||
"toast_bring": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt",
|
"toast_bring": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt",
|
||||||
"toast_opened_finished": "🔓 Geöffnete Packung von {name} aufgebraucht!",
|
"toast_opened_finished": "🔓 Geöffnete Packung von {name} aufgebraucht!",
|
||||||
"disambiguation_hint": "Was meinst du mit \"alles aufgebraucht\"?",
|
"disambiguation_hint": "Was meinst du mit \"alles aufgebraucht\"?",
|
||||||
"disambiguation_all": "🗑️ ALLES aufgebraucht ({qty})"
|
"disambiguation_all": "🗑️ ALLES verbraucht ({qty})",
|
||||||
|
"error_exceeds_stock": "⚠️ Du kannst nicht mehr verwenden als du verfügbar hast!"
|
||||||
},
|
},
|
||||||
"product": {
|
"product": {
|
||||||
"title_new": "Neues Produkt",
|
"title_new": "Neues Produkt",
|
||||||
@@ -691,7 +692,7 @@
|
|||||||
"item_added": "{name} hinzugefügt"
|
"item_added": "{name} hinzugefügt"
|
||||||
},
|
},
|
||||||
"antiwaste": {
|
"antiwaste": {
|
||||||
"title": "🌱 Anti-Waste Report",
|
"title": "🌱 Anti-Verschwendungs-Bericht",
|
||||||
"grade_label": "Note",
|
"grade_label": "Note",
|
||||||
"you": "Du",
|
"you": "Du",
|
||||||
"avg_label": "Ø",
|
"avg_label": "Ø",
|
||||||
|
|||||||
@@ -241,7 +241,8 @@
|
|||||||
"toast_bring": "🛒 Product finished → added to Bring!",
|
"toast_bring": "🛒 Product finished → added to Bring!",
|
||||||
"toast_opened_finished": "🔓 Opened package of {name} finished!",
|
"toast_opened_finished": "🔓 Opened package of {name} finished!",
|
||||||
"disambiguation_hint": "What do you mean by \"all done\"?",
|
"disambiguation_hint": "What do you mean by \"all done\"?",
|
||||||
"disambiguation_all": "🗑️ Finish EVERYTHING ({qty})"
|
"disambiguation_all": "🗑️ Finish EVERYTHING ({qty})",
|
||||||
|
"error_exceeds_stock": "⚠️ You cannot use more than you have available!"
|
||||||
},
|
},
|
||||||
"product": {
|
"product": {
|
||||||
"title_new": "New Product",
|
"title_new": "New Product",
|
||||||
|
|||||||
@@ -241,7 +241,8 @@
|
|||||||
"toast_bring": "🛒 Prodotto finito → aggiunto a Bring!",
|
"toast_bring": "🛒 Prodotto finito → aggiunto a Bring!",
|
||||||
"toast_opened_finished": "🔓 Confezione aperta di {name} finita!",
|
"toast_opened_finished": "🔓 Confezione aperta di {name} finita!",
|
||||||
"disambiguation_hint": "Cosa intendi con \"finito tutto\"?",
|
"disambiguation_hint": "Cosa intendi con \"finito tutto\"?",
|
||||||
"disambiguation_all": "🗑️ Finito TUTTO ({qty})"
|
"disambiguation_all": "🗑️ Finito TUTTO ({qty})",
|
||||||
|
"error_exceeds_stock": "⚠️ Non puoi usare più di quanto hai disponibile!"
|
||||||
},
|
},
|
||||||
"product": {
|
"product": {
|
||||||
"title_new": "Nuovo Prodotto",
|
"title_new": "Nuovo Prodotto",
|
||||||
@@ -690,7 +691,7 @@
|
|||||||
"item_added": "{name} aggiunto"
|
"item_added": "{name} aggiunto"
|
||||||
},
|
},
|
||||||
"antiwaste": {
|
"antiwaste": {
|
||||||
"title": "🌱 Anti-Waste Report",
|
"title": "🌱 Rapporto Anti-Spreco",
|
||||||
"grade_label": "Voto",
|
"grade_label": "Voto",
|
||||||
"you": "Tu",
|
"you": "Tu",
|
||||||
"avg_label": "Media",
|
"avg_label": "Media",
|
||||||
|
|||||||
Reference in New Issue
Block a user