Redesign ingredient card
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m13s
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m13s
This commit is contained in:
@@ -8,11 +8,15 @@ import {
|
||||
} from '../data/catalog.js?v=8';
|
||||
import {
|
||||
addOrMergeShoppingLines,
|
||||
KITCHEN_LIST_ID,
|
||||
loadPantry,
|
||||
loadShoppingState,
|
||||
removeItemFromList,
|
||||
setPantryQty,
|
||||
setPantryProductQty,
|
||||
getPantryTotal,
|
||||
getPantryProducts,
|
||||
updateKitchenItemAmount,
|
||||
} from '../services/pantryShopping.js?v=2';
|
||||
import { showAppToast } from './toast.js';
|
||||
|
||||
@@ -91,6 +95,18 @@ function sortProductsByStock(products, pantryItems) {
|
||||
});
|
||||
}
|
||||
|
||||
function formatPackAwareAmount(amount, pantryUnit, packSize, packLabel) {
|
||||
const qty = Number(amount) || 0;
|
||||
const unit = unitLabel(pantryUnit);
|
||||
if (packSize && packSize > 0) {
|
||||
const packs = qty / packSize;
|
||||
if (qty > 0 && Number.isFinite(packs) && Math.abs(packs - Math.round(packs)) < 0.001) {
|
||||
return `${formatQty(Math.round(packs))} x ${packLabel || `${formatQty(packSize)} ${unit}`}`;
|
||||
}
|
||||
}
|
||||
return `${formatQty(qty)} ${unit}`;
|
||||
}
|
||||
|
||||
export function getIngredientCardHTML({
|
||||
idBase,
|
||||
overlayClass = 'fixed inset-0 z-[70] hidden opacity-0 transition-opacity duration-200 flex items-center justify-center p-5',
|
||||
@@ -140,12 +156,36 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
sourceNote: defaultSourceNote,
|
||||
onProductChange: null,
|
||||
onAfterChange: null,
|
||||
stockEditorOpen: false,
|
||||
stockDraftQty: null,
|
||||
shopEditorOpen: false,
|
||||
shopDraftQty: null,
|
||||
closeTimer: null,
|
||||
};
|
||||
let bound = false;
|
||||
|
||||
const el = (suffix = '') => document.getElementById(suffix ? `${idBase}-${suffix}` : idBase);
|
||||
|
||||
function resetInlineEditors() {
|
||||
state.stockEditorOpen = false;
|
||||
state.stockDraftQty = null;
|
||||
state.shopEditorOpen = false;
|
||||
state.shopDraftQty = null;
|
||||
}
|
||||
|
||||
function getCurrentShoppingItem(def) {
|
||||
const shopping = loadShoppingState();
|
||||
const kitchen = shopping.lists.find((list) => list.id === KITCHEN_LIST_ID && list.type === 'kitchen');
|
||||
if (!kitchen || kitchen.type !== 'kitchen') return null;
|
||||
const unit = unitLabel(def.pantryUnit);
|
||||
return kitchen.items.find((item) => {
|
||||
if (item.checked) return false;
|
||||
return item.ingredientId === state.ingredientId
|
||||
&& item.unit === unit
|
||||
&& (item.productId || '') === (state.productId || '');
|
||||
}) || null;
|
||||
}
|
||||
|
||||
function renderHeader(def, product, pantry) {
|
||||
const hasProducts = ingredientHasProducts(def.id);
|
||||
const icon = CATEGORY_ICONS[def.category] || 'fa-jar';
|
||||
@@ -213,11 +253,12 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
? 'dokładne dla produktu'
|
||||
: hasProducts
|
||||
? 'orientacyjnie dla składnika'
|
||||
: 'bazowe wartości';
|
||||
: '';
|
||||
const nutritionMeta = hint ? `${unitScope} • ${hint}` : unitScope;
|
||||
|
||||
wrap.innerHTML = `
|
||||
<p class="text-[9px] font-semibold uppercase tracking-wide mb-1.5" style="color:#9b978f;">Wartości odżywcze</p>
|
||||
<p class="text-[10px] mb-1.5" style="color:#9b978f;">${esc(unitScope)} • ${esc(hint)}</p>
|
||||
<p class="text-[10px] mb-1.5" style="color:#9b978f;">${esc(nutritionMeta)}</p>
|
||||
<div class="grid grid-cols-4 gap-1.5">
|
||||
<div class="rounded-xl px-2 py-1.5 text-center" style="background:#393937;">
|
||||
<p class="text-[15px] font-bold tabular-nums leading-tight" style="color:#ddd6ca;">${nutrition.kcal}</p>
|
||||
@@ -252,18 +293,17 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
|
||||
if (hasProducts && !product) {
|
||||
const stockedCount = getPantryProducts(state.ingredientId, pantry).filter((i) => i.qty > 0).length;
|
||||
const summaryNutrition = nutritionForQty(def, totalQty);
|
||||
wrap.innerHTML = `
|
||||
<p class="text-[9px] font-semibold uppercase tracking-wide mb-1.5" style="color:#9b978f;">Zapas</p>
|
||||
<div class="rounded-xl px-3 py-2.5" style="background:#393937;">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-[17px] font-bold tabular-nums" style="color:#6ee7b7;">${esc(formatQty(totalQty))} ${esc(unit)}</p>
|
||||
<p class="text-[11px] mt-0.5" style="color:#9b978f;">${stockedCount} z ${getProductsForIngredient(state.ingredientId).length} produktów ma stan</p>
|
||||
<div class="rounded-2xl border px-3 py-3" style="background:#393937; border-color:#444442;">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-[10px] font-semibold uppercase tracking-wide" style="color:#9b978f;">Stan łączny</p>
|
||||
<p class="text-[16px] font-bold tabular-nums mt-1" style="color:#6ee7b7;">${esc(formatQty(totalQty))} ${esc(unit)}</p>
|
||||
<p class="text-[11px] mt-1 leading-snug" style="color:#9b978f;">${stockedCount} z ${getProductsForIngredient(state.ingredientId).length} produktów ma zapas</p>
|
||||
</div>
|
||||
<span class="text-[10px] text-right max-w-[92px]" style="color:#9b978f;">Wybierz produkt niżej, aby zmienić stan</span>
|
||||
<span class="inline-flex items-center rounded-full px-2 py-1 text-[10px] font-semibold shrink-0" style="background:#2f2f2d; color:#d7d2c8;">Wybierz produkt</span>
|
||||
</div>
|
||||
${summaryNutrition ? `<p class="text-[10px] mt-2 tabular-nums" style="color:#9b978f;">${esc(macroLine(summaryNutrition))}</p>` : ''}
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
@@ -272,42 +312,88 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
? (getPantryProducts(state.ingredientId, pantry).find((i) => i.productId === state.productId)?.qty || 0)
|
||||
: totalQty;
|
||||
const step = product ? (product.packSize || pantryQtyStep(state.ingredientId)) : pantryQtyStep(state.ingredientId);
|
||||
const stockNutrition = nutritionForQty(def, qty, product?.nutritionPer100g || def.nutritionPer100g);
|
||||
const packSize = product?.packSize || def.purchasePack?.amount || 0;
|
||||
const packLabel = product?.packLabel || def.purchasePack?.label || '';
|
||||
const draftQty = state.stockEditorOpen
|
||||
? Math.max(0, Number(state.stockDraftQty ?? qty) || 0)
|
||||
: qty;
|
||||
const actionLabel = state.stockEditorOpen ? 'Anuluj' : 'Zmień';
|
||||
|
||||
wrap.innerHTML = `
|
||||
<p class="text-[9px] font-semibold uppercase tracking-wide mb-1.5" style="color:#9b978f;">Zapas</p>
|
||||
<div class="flex items-center justify-center gap-3 rounded-xl px-3 py-2" style="background:#393937;">
|
||||
<button type="button" class="ingredient-card-stock-btn w-9 h-9 rounded-xl flex items-center justify-center active:scale-95" style="background:#2f2f2d; color:#d7d2c8;" data-pid="${esc(state.productId || '_generic')}" data-step="${step}" data-dir="-1" aria-label="Zmniejsz stan">
|
||||
<i class="fas fa-minus text-xs"></i>
|
||||
</button>
|
||||
<div class="flex-1 text-center">
|
||||
<p class="text-[17px] font-bold tabular-nums" style="color:#6ee7b7;">${esc(formatQty(qty))} ${esc(unit)}</p>
|
||||
<p class="text-[10px] mt-0.5" style="color:#9b978f;">Krok: ${esc(formatQty(step))} ${esc(unit)}</p>
|
||||
<div class="rounded-2xl border px-3 py-3" style="background:#393937; border-color:#444442;">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-[16px] font-bold tabular-nums" style="color:#6ee7b7;">${esc(formatPackAwareAmount(qty, def.pantryUnit, packSize, packLabel))}</p>
|
||||
</div>
|
||||
<button type="button" class="ingredient-card-stock-toggle inline-flex items-center rounded-full px-2.5 py-1 text-[10px] font-semibold shrink-0" style="background:${state.stockEditorOpen ? '#23221e' : '#2f2f2d'}; color:${state.stockEditorOpen ? '#f2efe8' : '#d7d2c8'};">
|
||||
${esc(actionLabel)}
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="ingredient-card-stock-btn w-9 h-9 rounded-xl flex items-center justify-center active:scale-95" style="background:#2f2f2d; color:#d7d2c8;" data-pid="${esc(state.productId || '_generic')}" data-step="${step}" data-dir="1" aria-label="Zwiększ stan">
|
||||
<i class="fas fa-plus text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
${stockNutrition ? `<p class="text-[10px] mt-1.5 tabular-nums" style="color:#9b978f;">${esc(macroLine(stockNutrition))} na stanie</p>` : ''}`;
|
||||
${state.stockEditorOpen ? `
|
||||
<div class="mt-3 pt-3 border-t" style="border-color:#444442;">
|
||||
<div class="flex items-center gap-2">
|
||||
<button type="button" class="ingredient-card-stock-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="background:#2f2f2d; color:#d7d2c8;" data-dir="-1" aria-label="Zmniejsz szkic zapasu">
|
||||
<i class="fas fa-minus text-xs"></i>
|
||||
</button>
|
||||
<label class="flex-1 rounded-xl px-3 py-2 flex items-center justify-center gap-2" style="background:#2f2f2d;">
|
||||
<input type="number" min="0" step="${step}" value="${formatQty(draftQty)}" class="ingredient-card-stock-input w-20 bg-transparent text-center text-[14px] font-semibold tabular-nums outline-none appearance-none" style="color:#ddd6ca; background:transparent !important; border:none !important; box-shadow:none !important; -webkit-appearance:none; -moz-appearance:textfield;">
|
||||
<span class="text-[12px] font-medium shrink-0" style="color:#9b978f;">${esc(unit)}</span>
|
||||
</label>
|
||||
<button type="button" class="ingredient-card-stock-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="background:#2f2f2d; color:#d7d2c8;" data-dir="1" aria-label="Zwiększ szkic zapasu">
|
||||
<i class="fas fa-plus text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 mt-3">
|
||||
<button type="button" class="ingredient-card-stock-clear text-[11px] font-semibold" style="color:#9b978f;">Wyzeruj</button>
|
||||
<button type="button" class="ingredient-card-stock-save inline-flex items-center rounded-full px-3 py-1.5 text-[11px] font-semibold" style="background:#ddd6ca; color:#2d2e2b;">Zapisz</button>
|
||||
</div>
|
||||
</div>` : ''}
|
||||
</div>`;
|
||||
|
||||
wrap.querySelectorAll('.ingredient-card-stock-btn').forEach((btn) => {
|
||||
wrap.querySelector('.ingredient-card-stock-toggle')?.addEventListener('click', () => {
|
||||
if (state.stockEditorOpen) {
|
||||
state.stockEditorOpen = false;
|
||||
state.stockDraftQty = null;
|
||||
} else {
|
||||
state.stockEditorOpen = true;
|
||||
state.shopEditorOpen = false;
|
||||
state.stockDraftQty = qty;
|
||||
}
|
||||
render();
|
||||
});
|
||||
|
||||
wrap.querySelectorAll('.ingredient-card-stock-step').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const pid = btn.dataset.pid;
|
||||
const stepVal = Number(btn.dataset.step) || 1;
|
||||
const dir = Number(btn.dataset.dir) || 1;
|
||||
const pantryState = loadPantry();
|
||||
if (pid === '_generic') {
|
||||
const current = getPantryTotal(state.ingredientId, pantryState);
|
||||
setPantryQty(state.ingredientId, Math.max(0, current + stepVal * dir));
|
||||
} else {
|
||||
const items = getPantryProducts(state.ingredientId, pantryState);
|
||||
const current = items.find((i) => i.productId === pid)?.qty || 0;
|
||||
setPantryProductQty(state.ingredientId, pid, Math.max(0, current + stepVal * dir));
|
||||
}
|
||||
const next = Math.max(0, Math.round(((Number(state.stockDraftQty ?? qty) || 0) + step * dir) * 100) / 100);
|
||||
state.stockDraftQty = next;
|
||||
render();
|
||||
state.onAfterChange?.();
|
||||
});
|
||||
});
|
||||
|
||||
wrap.querySelector('.ingredient-card-stock-input')?.addEventListener('input', (event) => {
|
||||
state.stockDraftQty = Math.max(0, Number(event.target.value) || 0);
|
||||
});
|
||||
|
||||
wrap.querySelector('.ingredient-card-stock-clear')?.addEventListener('click', () => {
|
||||
state.stockDraftQty = 0;
|
||||
render();
|
||||
});
|
||||
|
||||
wrap.querySelector('.ingredient-card-stock-save')?.addEventListener('click', () => {
|
||||
const input = wrap.querySelector('.ingredient-card-stock-input');
|
||||
const nextQty = Math.max(0, Math.round((Number(input?.value ?? state.stockDraftQty ?? qty) || 0) * 100) / 100);
|
||||
if (state.productId) {
|
||||
setPantryProductQty(state.ingredientId, state.productId, nextQty);
|
||||
} else {
|
||||
setPantryQty(state.ingredientId, nextQty);
|
||||
}
|
||||
state.stockEditorOpen = false;
|
||||
state.stockDraftQty = null;
|
||||
render();
|
||||
state.onAfterChange?.();
|
||||
});
|
||||
}
|
||||
|
||||
function productRowHtml(ingredientId, productId, pantry, selectedProductId) {
|
||||
@@ -359,6 +445,7 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
const nextProductId = btn.dataset.productId || null;
|
||||
state.productId = nextProductId;
|
||||
state.selectedProductId = nextProductId;
|
||||
resetInlineEditors();
|
||||
if (nextProductId) state.onProductChange?.(nextProductId);
|
||||
render();
|
||||
state.onAfterChange?.();
|
||||
@@ -377,37 +464,114 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
const packSize = product?.packSize || def.purchasePack?.amount;
|
||||
const packLabel = product?.packLabel || def.purchasePack?.label;
|
||||
const usesPacks = Boolean(packSize && packSize > 0);
|
||||
const btnLabel = usesPacks
|
||||
? `Dodaj na listę (${packLabel || `${formatQty(packSize)} ${unitLabel(def.pantryUnit)}`})`
|
||||
: 'Dodaj na listę';
|
||||
const helperText = hasProducts && !product
|
||||
? 'Doda składnik bez wskazanej marki. Jeśli chcesz konkretny produkt, wybierz go wyżej.'
|
||||
: product
|
||||
? 'Pozycja trafi na listę zakupów z dokładnym produktem.'
|
||||
: `Szybki skrót do listy zakupów ${defaultSourceNote === 'Ze spiżarni' ? 'ze spiżarni' : 'z planera'}.`;
|
||||
const defaultAmount = usesPacks ? packSize : pantryQtyStep(state.ingredientId);
|
||||
const shoppingItem = getCurrentShoppingItem(def);
|
||||
const hasShoppingItem = Boolean(shoppingItem);
|
||||
const shoppingAmount = shoppingItem?.amount || 0;
|
||||
const draftQty = state.shopEditorOpen
|
||||
? Math.max(0, Number(state.shopDraftQty ?? (shoppingAmount || defaultAmount)) || 0)
|
||||
: shoppingAmount;
|
||||
const actionLabel = state.shopEditorOpen ? 'Anuluj' : 'Zmień';
|
||||
|
||||
wrap.innerHTML = `
|
||||
<p class="text-[9px] font-semibold uppercase tracking-wide mb-1.5" style="color:#9b978f;">Lista zakupów</p>
|
||||
<p class="text-[10px] mb-1.5" style="color:#9b978f;">${esc(helperText)}</p>
|
||||
<button type="button" class="ingredient-card-add-list w-full flex items-center justify-center gap-2 py-2.5 rounded-xl text-[13px] font-semibold transition-colors active:scale-[0.98]" style="background:#ddd6ca; color:#2d2e2b;">
|
||||
<i class="fas fa-cart-plus text-[11px]"></i>${esc(btnLabel)}
|
||||
</button>`;
|
||||
<div class="rounded-2xl border px-3 py-3" style="background:#393937; border-color:#444442;">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-[16px] font-bold tabular-nums" style="color:${hasShoppingItem ? '#ddd6ca' : '#9b978f'};">${esc(hasShoppingItem ? formatPackAwareAmount(shoppingAmount, def.pantryUnit, packSize, packLabel) : 'Brak na liscie')}</p>
|
||||
</div>
|
||||
<button type="button" class="ingredient-card-shop-toggle inline-flex items-center rounded-full px-2.5 py-1 text-[10px] font-semibold shrink-0" style="background:${state.shopEditorOpen ? '#23221e' : '#2f2f2d'}; color:${state.shopEditorOpen ? '#f2efe8' : '#d7d2c8'};">
|
||||
${esc(actionLabel)}
|
||||
</button>
|
||||
</div>
|
||||
${state.shopEditorOpen ? `
|
||||
<div class="mt-3 pt-3 border-t" style="border-color:#444442;">
|
||||
<div class="flex items-center gap-2">
|
||||
<button type="button" class="ingredient-card-shop-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="background:#2f2f2d; color:#d7d2c8;" data-dir="-1" aria-label="Zmniejsz ilość na liście">
|
||||
<i class="fas fa-minus text-xs"></i>
|
||||
</button>
|
||||
<label class="flex-1 rounded-xl px-3 py-2 flex items-center justify-center gap-2" style="background:#2f2f2d;">
|
||||
<input type="number" min="0" step="${defaultAmount}" value="${formatQty(draftQty)}" class="ingredient-card-shop-input w-20 bg-transparent text-center text-[14px] font-semibold tabular-nums outline-none appearance-none" style="color:#ddd6ca; background:transparent !important; border:none !important; box-shadow:none !important; -webkit-appearance:none; -moz-appearance:textfield;">
|
||||
<span class="text-[12px] font-medium shrink-0" style="color:#9b978f;">${esc(unitLabel(def.pantryUnit))}</span>
|
||||
</label>
|
||||
<button type="button" class="ingredient-card-shop-step w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style="background:#2f2f2d; color:#d7d2c8;" data-dir="1" aria-label="Zwiększ ilość na liście">
|
||||
<i class="fas fa-plus text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 mt-3">
|
||||
${hasShoppingItem
|
||||
? '<button type="button" class="ingredient-card-shop-remove text-[11px] font-semibold" style="color:#9b978f;">Usuń z listy</button>'
|
||||
: '<span></span>'}
|
||||
<button type="button" class="ingredient-card-shop-save inline-flex items-center rounded-full px-3 py-1.5 text-[11px] font-semibold" style="background:#ddd6ca; color:#2d2e2b;">Zapisz</button>
|
||||
</div>
|
||||
</div>` : ''}
|
||||
</div>`;
|
||||
|
||||
wrap.querySelector('.ingredient-card-add-list')?.addEventListener('click', () => {
|
||||
const amount = usesPacks ? packSize : pantryQtyStep(state.ingredientId);
|
||||
const note = usesPacks ? (packLabel || `${formatQty(packSize)} ${unitLabel(def.pantryUnit)}`) : undefined;
|
||||
const line = {
|
||||
ingredientId: state.ingredientId,
|
||||
amount,
|
||||
unit: unitLabel(def.pantryUnit),
|
||||
name: product?.name || def.name,
|
||||
category: def.category,
|
||||
sourceNote: note || state.sourceNote || defaultSourceNote,
|
||||
};
|
||||
if (state.productId) line.productId = state.productId;
|
||||
addOrMergeShoppingLines([line]);
|
||||
showAppToast(`Dodano ${product?.name || def.name} na listę.`);
|
||||
wrap.querySelector('.ingredient-card-shop-toggle')?.addEventListener('click', () => {
|
||||
if (state.shopEditorOpen) {
|
||||
state.shopEditorOpen = false;
|
||||
state.shopDraftQty = null;
|
||||
} else {
|
||||
state.shopEditorOpen = true;
|
||||
state.stockEditorOpen = false;
|
||||
state.shopDraftQty = shoppingAmount || defaultAmount;
|
||||
}
|
||||
render();
|
||||
});
|
||||
|
||||
wrap.querySelectorAll('.ingredient-card-shop-step').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const dir = Number(btn.dataset.dir) || 1;
|
||||
const next = Math.max(0, Math.round(((Number(state.shopDraftQty ?? (shoppingAmount || defaultAmount)) || 0) + defaultAmount * dir) * 100) / 100);
|
||||
state.shopDraftQty = next;
|
||||
render();
|
||||
});
|
||||
});
|
||||
|
||||
wrap.querySelector('.ingredient-card-shop-input')?.addEventListener('input', (event) => {
|
||||
state.shopDraftQty = Math.max(0, Number(event.target.value) || 0);
|
||||
});
|
||||
|
||||
wrap.querySelector('.ingredient-card-shop-remove')?.addEventListener('click', () => {
|
||||
if (!shoppingItem) return;
|
||||
removeItemFromList(KITCHEN_LIST_ID, shoppingItem.id);
|
||||
state.shopEditorOpen = false;
|
||||
state.shopDraftQty = null;
|
||||
render();
|
||||
state.onAfterChange?.();
|
||||
window.refreshShopping?.();
|
||||
showAppToast(`Usunieto ${product?.name || def.name} z listy.`);
|
||||
});
|
||||
|
||||
wrap.querySelector('.ingredient-card-shop-save')?.addEventListener('click', () => {
|
||||
const input = wrap.querySelector('.ingredient-card-shop-input');
|
||||
const nextAmount = Math.max(0, Math.round((Number(input?.value ?? state.shopDraftQty ?? defaultAmount) || 0) * 100) / 100);
|
||||
let toastText = null;
|
||||
if (shoppingItem) {
|
||||
updateKitchenItemAmount(KITCHEN_LIST_ID, shoppingItem.id, nextAmount);
|
||||
toastText = nextAmount > 0
|
||||
? `Zaktualizowano ${product?.name || def.name}.`
|
||||
: `Usunieto ${product?.name || def.name} z listy.`;
|
||||
} else if (nextAmount > 0) {
|
||||
const note = usesPacks ? (packLabel || `${formatQty(packSize)} ${unitLabel(def.pantryUnit)}`) : undefined;
|
||||
const line = {
|
||||
ingredientId: state.ingredientId,
|
||||
amount: nextAmount,
|
||||
unit: unitLabel(def.pantryUnit),
|
||||
name: product?.name || def.name,
|
||||
category: def.category,
|
||||
sourceNote: note || state.sourceNote || defaultSourceNote,
|
||||
};
|
||||
if (state.productId) line.productId = state.productId;
|
||||
addOrMergeShoppingLines([line]);
|
||||
toastText = `Dodano ${product?.name || def.name}.`;
|
||||
}
|
||||
state.shopEditorOpen = false;
|
||||
state.shopDraftQty = null;
|
||||
render();
|
||||
state.onAfterChange?.();
|
||||
window.refreshShopping?.();
|
||||
if (toastText) showAppToast(toastText);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -444,6 +608,7 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
state.sourceNote = sourceNote;
|
||||
state.onProductChange = onProductChange;
|
||||
state.onAfterChange = onAfterChange;
|
||||
resetInlineEditors();
|
||||
render();
|
||||
|
||||
clearTimeout(state.closeTimer);
|
||||
@@ -472,6 +637,7 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
state.onProductChange = null;
|
||||
state.onAfterChange = null;
|
||||
state.sourceNote = defaultSourceNote;
|
||||
resetInlineEditors();
|
||||
}
|
||||
|
||||
function bind() {
|
||||
@@ -480,6 +646,7 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
|
||||
el('close')?.addEventListener('click', close);
|
||||
el('back')?.addEventListener('click', () => {
|
||||
state.productId = null;
|
||||
resetInlineEditors();
|
||||
render();
|
||||
});
|
||||
el('overlay')?.addEventListener('click', (event) => {
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
renderCalendarGrid,
|
||||
syncCalendarTodayButton,
|
||||
} from './mealCalendar.js?v=1';
|
||||
import { createIngredientCardController, getIngredientCardHTML } from './ingredientCard.js?v=20260410-99';
|
||||
import { createIngredientCardController, getIngredientCardHTML } from './ingredientCard.js?v=20260410-106';
|
||||
|
||||
function esc(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
|
||||
Reference in New Issue
Block a user