import { INGREDIENTS, CATEGORY_LABELS, pantryQtyStep, } from '../data/catalog.js'; import { addIngredientToKitchenList, categoryLabel, loadPantry, setPantryQty } from '../services/pantryShopping.js'; import { showAppToast } from '../ui/toast.js'; /* ── helpers ── */ function esc(s) { return String(s).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } function unitLabel(u) { return u === 'szt' ? 'szt.' : u; } function normalizeSearch(q) { return String(q).trim().toLowerCase(); } const CATEGORY_ICONS = { pieczywo: 'fa-bread-slice', nabial: 'fa-cheese', mieso_ryby: 'fa-drumstick-bite', warzywa: 'fa-carrot', owoce: 'fa-apple-whole', suche: 'fa-wheat-awn', przyprawy: 'fa-leaf', inne: 'fa-jar', }; /* ── state ── */ let showOnlyStock = false; let editingId = null; /** @type {Set} */ const selectedCategories = new Set(); let editShopStep = 1; let editShopUsesPacks = false; const BOTTOM = '5.25rem'; const HIDDEN_Y = `translateY(calc(100% + ${BOTTOM}))`; /* ══════════════════════ HTML SHELL ══════════════════════ */ export function getPantryHTML() { return ` `; } /* ══════════════════════ CATEGORY CHIPS (multi-select) ══════════════════════ */ function allCategoryKeys() { const s = new Set(); Object.values(INGREDIENTS).forEach(d => s.add(d.category)); return [...s].sort((a, b) => categoryLabel(a).localeCompare(categoryLabel(b))); } function renderCategoryChips() { const wrap = document.getElementById('pantry-category-chips'); if (!wrap) return; const keys = allCategoryKeys(); wrap.innerHTML = keys.map(k => { const active = selectedCategories.has(k); const icon = CATEGORY_ICONS[k] || 'fa-jar'; const cls = active ? 'shrink-0 inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-xs font-semibold bg-gray-900 text-white transition-colors' : 'shrink-0 inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-xs font-semibold bg-gray-100 text-gray-600 hover:bg-gray-200 transition-colors'; return ``; }).join(''); wrap.querySelectorAll('.pv2-cat-chip').forEach(btn => { btn.addEventListener('click', () => { const cat = btn.dataset.cat; if (selectedCategories.has(cat)) selectedCategories.delete(cat); else selectedCategories.add(cat); renderCategoryChips(); renderBoard(); }); }); } /* ══════════════════════ BOARD RENDERING ══════════════════════ */ function getFilteredIds(searchRaw) { const q = normalizeSearch(searchRaw); return Object.keys(INGREDIENTS).filter(id => { const d = INGREDIENTS[id]; if (selectedCategories.size > 0 && !selectedCategories.has(d.category)) return false; if (!q) return true; return d.name.toLowerCase().includes(q) || (CATEGORY_LABELS[d.category] || '').toLowerCase().includes(q); }).sort((a, b) => INGREDIENTS[a].name.localeCompare(INGREDIENTS[b].name, 'pl')); } function chipHtml(id, pantry) { const def = INGREDIENTS[id]; const qty = Number(pantry[id]) || 0; const u = unitLabel(def.pantryUnit); if (qty > 0) { return ``; } return ``; } function groupByCategory(ids) { /** @type {Map} */ const groups = new Map(); for (const id of ids) { const cat = INGREDIENTS[id].category; if (!groups.has(cat)) groups.set(cat, []); groups.get(cat).push(id); } return [...groups.keys()] .sort((a, b) => categoryLabel(a).localeCompare(categoryLabel(b))) .map(cat => ({ cat, ids: groups.get(cat) })); } function renderBoard() { const root = document.getElementById('pantry-board'); if (!root) return; const q = document.getElementById('pantry-search')?.value || ''; const pantry = loadPantry(); const allFiltered = getFilteredIds(q); const visible = showOnlyStock ? allFiltered.filter(id => (Number(pantry[id]) || 0) > 0) : allFiltered; if (visible.length === 0) { root.innerHTML = showOnlyStock ? `

Nic na stanie

Wyłącz filtr, aby zobaczyć cały katalog produktów

` : `

Brak wyników — zmień wyszukiwanie lub filtry.

`; return; } const groups = groupByCategory(visible); let html = ''; for (const { cat, ids } of groups) { const icon = CATEGORY_ICONS[cat] || 'fa-jar'; html += `

${esc(categoryLabel(cat))}

${ids.map(id => chipHtml(id, pantry)).join('')}
`; } root.innerHTML = html; root.querySelectorAll('.pv2-chip').forEach(btn => { btn.addEventListener('click', () => openEditSheet(btn.dataset.id)); }); } /* ══════════════════════ STOCK TOGGLE ══════════════════════ */ function updateToggleVisuals() { const btn = document.getElementById('pantry-stock-toggle'); if (!btn) return; const thumb = btn.querySelector('span'); btn.setAttribute('aria-checked', String(showOnlyStock)); if (showOnlyStock) { btn.classList.remove('bg-gray-200'); btn.classList.add('bg-emerald-500'); thumb?.classList.add('translate-x-[18px]'); } else { btn.classList.add('bg-gray-200'); btn.classList.remove('bg-emerald-500'); thumb?.classList.remove('translate-x-[18px]'); } } /* ══════════════════════ EDIT BOTTOM SHEET ══════════════════════ */ function openEditSheet(ingredientId) { const def = INGREDIENTS[ingredientId]; if (!def) return; editingId = ingredientId; const pantry = loadPantry(); const qty = Number(pantry[ingredientId]) || 0; const u = unitLabel(def.pantryUnit); const step = pantryQtyStep(ingredientId); const pack = def.purchasePack; const nameEl = document.getElementById('pv2-edit-name'); if (nameEl) nameEl.textContent = def.name; const metaEl = document.getElementById('pv2-edit-meta'); if (metaEl) { let meta = categoryLabel(def.category); if (pack) meta += ` · ${pack.label || `${pack.amount} ${u}`}`; metaEl.textContent = meta; } const qtyEl = document.getElementById('pv2-edit-qty'); if (qtyEl) qtyEl.value = qty > 0 ? String(Math.round(qty)) : '0'; const unitEl = document.getElementById('pv2-edit-unit'); if (unitEl) unitEl.textContent = u; editShopUsesPacks = Boolean(pack && pack.amount > 0); editShopStep = editShopUsesPacks ? 1 : step; const shopQtyEl = document.getElementById('pv2-shop-qty'); if (shopQtyEl) shopQtyEl.value = String(editShopStep); const shopUnitEl = document.getElementById('pv2-shop-unit'); if (shopUnitEl) shopUnitEl.textContent = editShopUsesPacks ? 'opak.' : u; const shopHintEl = document.getElementById('pv2-shop-hint'); if (shopHintEl) { if (editShopUsesPacks) { const lab = pack.label || `${pack.amount} ${u}`; shopHintEl.textContent = `1 opak. = ${lab}`; } else { shopHintEl.textContent = ''; } } renderNutritionInSheet(def); const bg = document.getElementById('pv2-edit-bg'); const sheet = document.getElementById('pv2-edit-sheet'); if (!bg || !sheet) return; bg.classList.remove('hidden'); sheet.classList.remove('hidden'); requestAnimationFrame(() => { bg.classList.remove('opacity-0'); sheet.style.transform = 'translateY(0)'; }); } function nutritionListRow(label, valueHtml) { return `
  • ${esc(label)} ${valueHtml}
  • `; } function renderNutritionInSheet(def) { const wrap = document.getElementById('pv2-edit-nutrition'); if (!wrap) return; const n = def.nutritionPer100g; if (!n) { wrap.innerHTML = ''; return; } const refLabel = def.pantryUnit === 'ml' ? '100 ml produktu' : '100 g produktu'; wrap.innerHTML = `

    ${esc(refLabel)}

      ${nutritionListRow('Energia', `${n.kcal} kcal`)} ${nutritionListRow('Białko', `${n.protein} g`)} ${nutritionListRow('Tłuszcz', `${n.fat} g`)} ${nutritionListRow('Węglowodany', `${n.carbs} g`)}
    `; } function closeEditSheet() { editingId = null; const bg = document.getElementById('pv2-edit-bg'); const sheet = document.getElementById('pv2-edit-sheet'); if (sheet) sheet.style.transform = HIDDEN_Y; if (bg) bg.classList.add('opacity-0'); setTimeout(() => { bg?.classList.add('hidden'); sheet?.classList.add('hidden'); }, 300); renderBoard(); } function getEditQty() { const el = document.getElementById('pv2-edit-qty'); return Math.max(0, parseFloat(String(el?.value).replace(',', '.')) || 0); } function setEditQty(v) { const el = document.getElementById('pv2-edit-qty'); if (el) el.value = String(Math.max(0, Math.round(v))); } function applyEditQty(newQty) { if (!editingId) return; const v = Math.max(0, Math.round(Number(newQty) * 1000) / 1000 || 0); setPantryQty(editingId, v); setEditQty(v); } function readShopQty() { const el = document.getElementById('pv2-shop-qty'); return Math.max(1, Math.round(parseFloat(String(el?.value).replace(',', '.')) || 0)); } function setShopQty(v) { const el = document.getElementById('pv2-shop-qty'); if (el) el.value = String(Math.max(1, Math.round(Number(v)))); } function bindEditSheet() { document.getElementById('pv2-edit-bg')?.addEventListener('click', closeEditSheet); document.getElementById('pv2-edit-minus')?.addEventListener('click', () => { if (!editingId) return; applyEditQty(Math.max(0, getEditQty() - pantryQtyStep(editingId))); }); document.getElementById('pv2-edit-plus')?.addEventListener('click', () => { if (!editingId) return; applyEditQty(getEditQty() + pantryQtyStep(editingId)); }); document.getElementById('pv2-edit-qty')?.addEventListener('change', () => { applyEditQty(getEditQty()); }); document.getElementById('pv2-shop-minus')?.addEventListener('click', () => { setShopQty(Math.max(1, readShopQty() - editShopStep)); }); document.getElementById('pv2-shop-plus')?.addEventListener('click', () => { setShopQty(readShopQty() + editShopStep); }); document.getElementById('pv2-shop-qty')?.addEventListener('change', () => { setShopQty(readShopQty()); }); document.getElementById('pv2-shop-add')?.addEventListener('click', () => { if (!editingId) return; const def = INGREDIENTS[editingId]; if (!def) return; const count = readShopQty(); const u = unitLabel(def.pantryUnit); if (editShopUsesPacks && def.purchasePack) { const packAmt = def.purchasePack.amount; const total = count * packAmt; const note = `${count}× ${def.purchasePack.label || `${packAmt} ${u}`}`; addIngredientToKitchenList(editingId, total, note); showAppToast(`Dodano ${count} op. (${total} ${u}) na listę.`); } else { addIngredientToKitchenList(editingId, count); showAppToast(`Dodano ${count} ${u} na listę.`); } window.refreshShopping?.(); }); } /* ══════════════════════ PUBLIC API ══════════════════════ */ export function refreshPantry() { renderCategoryChips(); renderBoard(); } export function setupPantry() { renderCategoryChips(); renderBoard(); bindEditSheet(); document.getElementById('pantry-search')?.addEventListener('input', () => renderBoard()); document.getElementById('pantry-stock-toggle')?.addEventListener('click', () => { showOnlyStock = !showOnlyStock; updateToggleVisuals(); renderBoard(); }); window.refreshPantry = refreshPantry; }