Merge develop: fix use-all confirm HTML + disambiguation UX
This commit is contained in:
+47
-38
@@ -7736,12 +7736,20 @@ async function confirmMoveAfterUse(productId, fromLoc, toLoc, openedId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function submitUseAll() {
|
async function submitUseAll() {
|
||||||
// Gate: show a countdown-confirmation before the destructive use_all call
|
|
||||||
const name = currentProduct ? currentProduct.name : '';
|
const name = currentProduct ? currentProduct.name : '';
|
||||||
const items0 = _useCurrentItems ? _useCurrentItems.filter(i => parseFloat(i.quantity) > 0) : [];
|
const items0 = _useCurrentItems ? _useCurrentItems.filter(i => parseFloat(i.quantity) > 0) : [];
|
||||||
|
|
||||||
|
// If there are opened packages, show the disambiguation FIRST (before the destructive confirm)
|
||||||
|
const allOpened = items0.filter(_isOpenedInventoryItem);
|
||||||
|
if (allOpened.length >= 1) {
|
||||||
|
_showUseAllDisambiguation(allOpened, items0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No opened packages → standard destructive confirm
|
||||||
const totalQty = items0.reduce((s, i) => s + parseFloat(i.quantity || 0), 0);
|
const totalQty = items0.reduce((s, i) => s + parseFloat(i.quantity || 0), 0);
|
||||||
const unit = items0[0]?.unit || 'pz';
|
const unit = items0[0]?.unit || 'pz';
|
||||||
const qtyStr = formatQuantity(totalQty, unit, items0[0]?.default_quantity, items0[0]?.package_unit);
|
const qtyStr = stripHtml(formatQuantity(totalQty, unit, items0[0]?.default_quantity, items0[0]?.package_unit));
|
||||||
_showDestructiveConfirm(
|
_showDestructiveConfirm(
|
||||||
t('use.use_all_confirm_title') || '✅ Finisci tutto',
|
t('use.use_all_confirm_title') || '✅ Finisci tutto',
|
||||||
`${t('use.use_all_confirm_msg') || 'Conferma che hai finito tutto il prodotto:'} "${name}" (${qtyStr})`,
|
`${t('use.use_all_confirm_msg') || 'Conferma che hai finito tutto il prodotto:'} "${name}" (${qtyStr})`,
|
||||||
@@ -7751,44 +7759,20 @@ async function submitUseAll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function _doSubmitUseAll() {
|
async function _doSubmitUseAll() {
|
||||||
|
// Called only when there are no opened packages (submitUseAll already handles disambiguation)
|
||||||
showLoading(true);
|
showLoading(true);
|
||||||
try {
|
try {
|
||||||
const currentLoc = document.getElementById('use-location')?.value || '__all__';
|
|
||||||
const items = _useCurrentItems.filter(i => parseFloat(i.quantity) > 0);
|
|
||||||
|
|
||||||
const allOpened = items.filter(_isOpenedInventoryItem);
|
|
||||||
|
|
||||||
let useLocation;
|
|
||||||
|
|
||||||
if (allOpened.length >= 1) {
|
|
||||||
// One or more opened packages → always ask the user what they mean
|
|
||||||
showLoading(false);
|
|
||||||
_showUseAllDisambiguation(allOpened, items);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// No opened packages anywhere → finish everything (original behaviour)
|
|
||||||
useLocation = '__all__';
|
|
||||||
}
|
|
||||||
|
|
||||||
const isOpenedFinish = useLocation !== '__all__' && items.some(
|
|
||||||
i => i.location === useLocation && _isOpenedInventoryItem(i)
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await api('inventory_use', {}, 'POST', {
|
const result = await api('inventory_use', {}, 'POST', {
|
||||||
product_id: currentProduct.id,
|
product_id: currentProduct.id,
|
||||||
use_all: true,
|
use_all: true,
|
||||||
location: useLocation,
|
location: '__all__',
|
||||||
});
|
});
|
||||||
showLoading(false);
|
showLoading(false);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const toastMsg = isOpenedFinish
|
showToast(`📤 ${currentProduct.name} terminato!`, 'success');
|
||||||
? `🔓 ${t('use.toast_opened_finished').replace('{name}', currentProduct.name)}`
|
|
||||||
: `📤 ${currentProduct.name} terminato!`;
|
|
||||||
showToast(toastMsg, 'success');
|
|
||||||
if (result.added_to_bring) {
|
if (result.added_to_bring) {
|
||||||
setTimeout(() => showToast(t('use.toast_bring'), 'info'), 1500);
|
setTimeout(() => showToast(t('use.toast_bring'), 'info'), 1500);
|
||||||
}
|
}
|
||||||
// Check low stock (product may exist at other locations)
|
|
||||||
showLowStockBringPrompt(result, () => showPage('dashboard'));
|
showLowStockBringPrompt(result, () => showPage('dashboard'));
|
||||||
} else {
|
} else {
|
||||||
showToast(result.error || 'Errore', 'error');
|
showToast(result.error || 'Errore', 'error');
|
||||||
@@ -7805,23 +7789,23 @@ async function _doSubmitUseAll() {
|
|||||||
*/
|
*/
|
||||||
function _showUseAllDisambiguation(openedItems, allItems) {
|
function _showUseAllDisambiguation(openedItems, allItems) {
|
||||||
const contentEl = document.getElementById('modal-content');
|
const contentEl = document.getElementById('modal-content');
|
||||||
|
const name = currentProduct ? currentProduct.name : '';
|
||||||
|
|
||||||
const locButtons = openedItems.map(item => {
|
const locButtons = openedItems.map(item => {
|
||||||
const locInfo = LOCATIONS[item.location] || { icon: '📦', label: item.location };
|
const locInfo = LOCATIONS[item.location] || { icon: '📦', label: item.location };
|
||||||
const qtyStr = formatQuantity(parseFloat(item.quantity), item.unit, item.default_quantity, item.package_unit);
|
const qtyStr = stripHtml(formatQuantity(parseFloat(item.quantity), item.unit, item.default_quantity, item.package_unit));
|
||||||
return `<button class="btn btn-warning full-width" style="justify-content:flex-start;gap:10px;text-align:left;margin-bottom:8px"
|
return `<button class="btn btn-warning full-width" style="justify-content:flex-start;gap:10px;text-align:left;margin-bottom:8px"
|
||||||
onclick="closeModal(); _submitUseAllAt('${item.location}', true)">
|
onclick="closeModal(); _confirmThenSubmitUseAllAt('${item.location}', true)">
|
||||||
<span style="font-size:1.3rem">${locInfo.icon}</span>
|
<span style="font-size:1.3rem">${locInfo.icon}</span>
|
||||||
<span><strong>${locInfo.label}</strong> — 🔓 ${t('use.opened_badge')}<br>
|
<span><strong>${escapeHtml(locInfo.label)}</strong> — 🔓 ${t('use.opened_badge')}<br>
|
||||||
<small style="opacity:0.8">${qtyStr}</small></span>
|
<small style="opacity:0.8">${escapeHtml(qtyStr)}</small></span>
|
||||||
</button>`;
|
</button>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
// Option to finish everything
|
// Option to finish everything
|
||||||
const totalQty = allItems.reduce((s, i) => s + parseFloat(i.quantity), 0);
|
const totalQty = allItems.reduce((s, i) => s + parseFloat(i.quantity), 0);
|
||||||
const unit = allItems[0]?.unit || 'pz';
|
const unit = allItems[0]?.unit || 'pz';
|
||||||
const defaultQty = allItems[0]?.default_quantity;
|
const totalStr = stripHtml(formatQuantity(totalQty, unit, allItems[0]?.default_quantity, allItems[0]?.package_unit));
|
||||||
const pkgUnit = allItems[0]?.package_unit;
|
|
||||||
const totalStr = formatQuantity(totalQty, unit, defaultQty, pkgUnit);
|
|
||||||
|
|
||||||
contentEl.innerHTML = `
|
contentEl.innerHTML = `
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@@ -7831,13 +7815,33 @@ function _showUseAllDisambiguation(openedItems, allItems) {
|
|||||||
<p style="font-size:0.9rem;color:var(--text-muted);margin:0 0 14px">${t('use.disambiguation_hint')}</p>
|
<p style="font-size:0.9rem;color:var(--text-muted);margin:0 0 14px">${t('use.disambiguation_hint')}</p>
|
||||||
${locButtons}
|
${locButtons}
|
||||||
<button class="btn btn-danger full-width" style="margin-top:4px"
|
<button class="btn btn-danger full-width" style="margin-top:4px"
|
||||||
onclick="closeModal(); _submitUseAllAt('__all__', false)">
|
onclick="closeModal(); _confirmThenSubmitUseAllAt('__all__', false)">
|
||||||
🗑️ ${t('use.disambiguation_all').replace('{qty}', totalStr)}
|
🗑️ ${t('use.disambiguation_all').replace('{qty}', escapeHtml(totalStr))}
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
document.getElementById('modal-overlay').style.display = 'flex';
|
document.getElementById('modal-overlay').style.display = 'flex';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _confirmThenSubmitUseAllAt(location, isOpenedOnly) {
|
||||||
|
const name = currentProduct ? currentProduct.name : '';
|
||||||
|
const items = _useCurrentItems ? _useCurrentItems.filter(i => parseFloat(i.quantity) > 0) : [];
|
||||||
|
if (isOpenedOnly) {
|
||||||
|
// Finisce solo la confezione aperta — azione leggera, nessun confirm necessario
|
||||||
|
_submitUseAllAt(location, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Finisce tutto — richiede confirm distruttivo
|
||||||
|
const totalQty = items.reduce((s, i) => s + parseFloat(i.quantity || 0), 0);
|
||||||
|
const unit = items[0]?.unit || 'pz';
|
||||||
|
const qtyStr = stripHtml(formatQuantity(totalQty, unit, items[0]?.default_quantity, items[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})`,
|
||||||
|
() => _submitUseAllAt('__all__', false),
|
||||||
|
t('use.use_all_confirm_btn') || '✅ Sì, finito'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function _submitUseAllAt(location, isOpenedOnly) {
|
async function _submitUseAllAt(location, isOpenedOnly) {
|
||||||
showLoading(true);
|
showLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -10321,6 +10325,11 @@ function escapeHtml(str) {
|
|||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stripHtml(str) {
|
||||||
|
if (!str) return '';
|
||||||
|
return str.replace(/<[^>]*>/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
function formatDate(dateStr) {
|
||||||
if (!dateStr) return '';
|
if (!dateStr) return '';
|
||||||
const d = new Date(dateStr + 'T00:00:00');
|
const d = new Date(dateStr + 'T00:00:00');
|
||||||
|
|||||||
+1
-1
@@ -1462,6 +1462,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="assets/js/app.js?v=20260507n"></script>
|
<script src="assets/js/app.js?v=20260507o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user