diff --git a/assets/css/style.css b/assets/css/style.css
index a17f239..d33f731 100644
--- a/assets/css/style.css
+++ b/assets/css/style.css
@@ -4248,19 +4248,19 @@ body.cooking-mode-active .app-header {
.btn-log-undo {
flex-shrink: 0;
- background: none;
- border: 1px solid var(--border);
- color: var(--text-muted);
+ background: rgba(248, 113, 113, 0.12);
+ border: 1px solid rgba(248, 113, 113, 0.4);
+ color: #f87171;
border-radius: 6px;
- padding: 3px 8px;
- font-size: 0.85rem;
+ padding: 5px 10px;
+ font-size: 1rem;
cursor: pointer;
line-height: 1.4;
transition: background 0.15s, color 0.15s;
}
.btn-log-undo:hover {
- background: rgba(255,255,255,0.08);
- color: var(--text);
+ background: rgba(248, 113, 113, 0.25);
+ color: #fca5a5;
}
diff --git a/assets/js/app.js b/assets/js/app.js
index 826536f..97d7e3f 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -5743,27 +5743,105 @@ function selectThrowLocation(btn, loc) {
document.getElementById('throw-location').value = loc;
}
+/**
+ * Show a destructive-action confirmation modal with a 5-second auto-confirm countdown.
+ * The user can tap "Annulla" to cancel or "Conferma" (or wait) to proceed.
+ * @param {string} title — Modal title
+ * @param {string} msg — Explanatory text
+ * @param {Function} onConfirm — Called when confirmed (by user or countdown)
+ * @param {string} [confirmLabel] — Override confirm button label
+ */
+function _showDestructiveConfirm(title, msg, onConfirm, confirmLabel) {
+ const DURATION = 5000;
+ const btnLabel = confirmLabel || t('confirm.proceed') || 'Conferma';
+ const cancelLabel = t('confirm.cancel') || 'Annulla';
+ let rafHandle = null;
+ let timerHandle = null;
+ let resolved = false;
+
+ const overlayEl = document.getElementById('modal-overlay');
+ const contentEl = document.getElementById('modal-content');
+ const confirmBtnId = '_destConfirmBtn_' + Date.now();
+ const barId = '_destConfirmBar_' + Date.now();
+
+ contentEl.innerHTML = `
+
+ ${escapeHtml(msg)}
+
+
+
+
+
+ `;
+ overlayEl.style.display = 'flex';
+
+ function cleanup() {
+ if (rafHandle) cancelAnimationFrame(rafHandle);
+ if (timerHandle) clearTimeout(timerHandle);
+ rafHandle = timerHandle = null;
+ }
+ function doConfirm() {
+ if (resolved) return;
+ resolved = true;
+ cleanup();
+ closeModal();
+ onConfirm();
+ }
+ function doCancel() {
+ if (resolved) return;
+ resolved = true;
+ cleanup();
+ closeModal();
+ }
+
+ document.getElementById(confirmBtnId).addEventListener('click', doConfirm);
+ document.getElementById('_destCancelBtn').addEventListener('click', doCancel);
+
+ // Countdown animation
+ const barEl = document.getElementById(barId);
+ const start = performance.now();
+ function tick() {
+ const pct = Math.min(100, (performance.now() - start) / DURATION * 100);
+ if (barEl) barEl.style.width = (100 - pct) + '%';
+ if (pct < 100) { rafHandle = requestAnimationFrame(tick); }
+ }
+ rafHandle = requestAnimationFrame(tick);
+ timerHandle = setTimeout(doConfirm, DURATION);
+}
+
async function throwAll() {
closeModal();
- showLoading(true);
- try {
- const result = await api('inventory_use', {}, 'POST', {
- product_id: currentProduct.id,
- use_all: true,
- location: '__all__',
- notes: 'Buttato'
- });
- showLoading(false);
- if (result.success) {
- showToast(t('toast.thrown_away', { name: currentProduct.name }), 'success');
- showPage('dashboard');
- } else {
- showToast(result.error || 'Errore', 'error');
- }
- } catch(e) {
- showLoading(false);
- showToast(t('error.connection'), 'error');
- }
+ const name = currentProduct ? currentProduct.name : '';
+ _showDestructiveConfirm(
+ t('use.throw_all_confirm_title') || '🗑️ Butta tutto',
+ (t('use.throw_all_confirm_msg') || 'Vuoi davvero buttare via tutto il prodotto?') + (name ? `\n"${name}"` : ''),
+ async () => {
+ showLoading(true);
+ try {
+ const result = await api('inventory_use', {}, 'POST', {
+ product_id: currentProduct.id,
+ use_all: true,
+ location: '__all__',
+ notes: 'Buttato'
+ });
+ showLoading(false);
+ if (result.success) {
+ showToast(t('toast.thrown_away', { name: currentProduct.name }), 'success');
+ showPage('dashboard');
+ } else {
+ showToast(result.error || 'Errore', 'error');
+ }
+ } catch(e) {
+ showLoading(false);
+ showToast(t('error.connection'), 'error');
+ }
+ },
+ t('use.throw_all_confirm_btn') || '🗑️ Sì, butta'
+ );
}
async function throwPartial() {
@@ -7124,9 +7202,24 @@ async function confirmMoveAfterUse(productId, fromLoc, toLoc, openedId) {
}
async function submitUseAll() {
+ // Gate: show a countdown-confirmation before the destructive use_all call
+ const name = currentProduct ? currentProduct.name : '';
+ const items0 = _useCurrentItems ? _useCurrentItems.filter(i => parseFloat(i.quantity) > 0) : [];
+ const totalQty = items0.reduce((s, i) => s + parseFloat(i.quantity || 0), 0);
+ const unit = items0[0]?.unit || 'pz';
+ const qtyStr = formatQuantity(totalQty, unit, items0[0]?.default_quantity, items0[0]?.package_unit);
+ _showDestructiveConfirm(
+ t('use.use_all_confirm_title') || '✅ Finisci tutto',
+ `${t('use.use_all_confirm_msg') || 'Conferma che hai finito tutto il prodotto:'} "${name}" (${qtyStr})`,
+ _doSubmitUseAll,
+ t('use.use_all_confirm_btn') || '✅ Sì, finito'
+ );
+}
+
+async function _doSubmitUseAll() {
showLoading(true);
try {
- const currentLoc = document.getElementById('use-location').value;
+ const currentLoc = document.getElementById('use-location')?.value || '__all__';
const items = _useCurrentItems.filter(i => parseFloat(i.quantity) > 0);
const openedAtCurrentLoc = items.find(i => i.location === currentLoc && _isOpenedInventoryItem(i));
@@ -9372,7 +9465,15 @@ async function loadLog(more = false) {
async function undoTransactionEntry(id, type, name) {
const action = type === 'in' ? t('log.undo_action_remove') : t('log.undo_action_restore');
- if (!confirm(t('log.undo_confirm').replace('{action}', action).replace('{name}', name))) return;
+ const msg = t('log.undo_confirm').replace('{action}', action).replace('{name}', name);
+ _showDestructiveConfirm(
+ t('log.undo_title') || '↩ Annulla operazione',
+ msg,
+ () => _doUndoTransaction(id, type, name)
+ );
+}
+
+async function _doUndoTransaction(id, type, name) {
try {
const res = await api('transaction_undo', {}, 'POST', { id });
if (res.success) {
diff --git a/index.html b/index.html
index 97d7d83..5ca9030 100644
--- a/index.html
+++ b/index.html
@@ -1318,6 +1318,6 @@
-
+