import { INGREDIENTS, CATEGORY_LABELS } from '../data/catalog.js?v=9';
import {
KITCHEN_LIST_ID,
loadShoppingState,
saveShoppingState,
addOrMergeShoppingLines,
removeItemFromList,
clearCheckedInList,
loadPantry,
savePantry,
computeShortfalls,
categoryLabel,
} from '../services/pantryShopping.js?v=2';
import { aggregateWeekIngredientNeed } from '../services/planIngredients.js?v=2';
import { loadPlans } from '../services/planStore.js?v=2';
import { startOfWeekMonday } from '../services/dateUtils.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 formatQty(n) {
const rounded = Math.round((Number(n) || 0) * 10) / 10;
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1).replace(/\.0$/, '');
}
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',
};
const CATEGORY_ORDER = ['pieczywo', 'nabial', 'mieso_ryby', 'warzywa', 'owoce', 'suche', 'przyprawy', 'inne'];
/* ══════════════════════ HTML SHELL ══════════════════════ */
export function getShoppingListHTML() {
return `
`;
}
/* ══════════════════════ RENDERING ══════════════════════ */
function getKitchenItems() {
const state = loadShoppingState();
const list = state.lists.find((l) => l.id === KITCHEN_LIST_ID && l.type === 'kitchen');
return list ? /** @type {import('../services/pantryShopping.js').KitchenShoppingItem[]} */ (list.items) : [];
}
function groupItemsByCategory(items) {
/** @type {Map} */
const groups = new Map();
for (const item of items) {
const cat = item.category || 'inne';
if (!groups.has(cat)) groups.set(cat, []);
groups.get(cat).push(item);
}
return CATEGORY_ORDER
.filter((cat) => groups.has(cat))
.map((cat) => ({ cat, items: groups.get(cat) }));
}
function itemRowHtml(item) {
const def = INGREDIENTS[item.ingredientId];
const icon = def ? (CATEGORY_ICONS[def.category] || 'fa-jar') : 'fa-jar';
const image = def?.image;
const checked = item.checked;
const mediaFit = image && image.endsWith('.svg') ? 'object-contain' : 'object-cover';
const mediaHtml = image
? `
`
: `
`;
return `
${mediaHtml}
${esc(item.name)}
${esc(formatQty(item.amount))} ${esc(unitLabel(item.unit))}
`;
}
function renderBoard() {
const root = document.getElementById('sl-board');
if (!root) return;
const items = getKitchenItems();
if (items.length === 0) {
root.innerHTML = `
Lista jest pusta
Kliknij „Generuj braki z planera" aby dodać składniki na bieżący tydzień.
`;
return;
}
const groups = groupItemsByCategory(items);
const html = groups.map(({ cat, items: catItems }) => {
const icon = CATEGORY_ICONS[cat] || 'fa-jar';
const uncheckedCount = catItems.filter((i) => !i.checked).length;
return `
${esc(categoryLabel(cat))}
${uncheckedCount}/${catItems.length}
${catItems.map((item) => itemRowHtml(item)).join('')}
`;
}).join('');
root.innerHTML = html;
bindRowEvents(root);
}
/* ══════════════════════ EVENTS ══════════════════════ */
function bindRowEvents(root) {
root.querySelectorAll('.sl-row').forEach((row) => {
const id = row.dataset.id;
row.querySelector('.sl-check')?.addEventListener('click', () => {
handleToggle(id);
});
row.querySelector('.sl-remove')?.addEventListener('click', () => {
removeItemFromList(KITCHEN_LIST_ID, id);
renderBoard();
updateBadge();
});
});
}
function handleToggle(itemId) {
const state = loadShoppingState();
const list = state.lists.find((l) => l.id === KITCHEN_LIST_ID);
if (!list) return;
const item = list.items.find((i) => i.id === itemId);
if (!item) return;
const wasChecked = item.checked;
item.checked = !wasChecked;
saveShoppingState(state);
if (!wasChecked) {
// Just checked → apply to pantry
applyItemToPantry(item);
}
renderBoard();
updateBadge();
if (typeof window.refreshPantry === 'function') window.refreshPantry();
}
function applyItemToPantry(item) {
const def = INGREDIENTS[item.ingredientId];
if (!def) return;
const pantry = loadPantry();
if (item.productId) {
let val = pantry[item.ingredientId];
if (!val || typeof val === 'number') {
val = { items: [], _total: 0 };
pantry[item.ingredientId] = val;
}
const idx = val.items.findIndex((i) => i.productId === item.productId);
if (idx >= 0) val.items[idx].qty = Math.round((val.items[idx].qty + item.amount) * 1000) / 1000;
else val.items.push({ productId: item.productId, qty: Math.round(item.amount * 1000) / 1000 });
val._total = Math.round(val.items.reduce((s, i) => s + i.qty, 0) * 1000) / 1000;
} else {
const cur = typeof pantry[item.ingredientId] === 'number' ? pantry[item.ingredientId] : 0;
pantry[item.ingredientId] = Math.round((cur + item.amount) * 1000) / 1000;
}
savePantry(pantry);
}
function handleGenerate() {
const plans = loadPlans();
const weekStart = startOfWeekMonday(new Date());
const needLines = aggregateWeekIngredientNeed(plans, weekStart);
const pantry = loadPantry();
const shortfalls = computeShortfalls(needLines, pantry);
if (shortfalls.length === 0) {
showAppToast('Wszystko masz w spiżarni!');
return;
}
const lines = shortfalls.map((s) => ({
ingredientId: s.ingredientId,
amount: s.shortfall,
unit: s.unit,
name: s.name,
category: s.category,
sourceNote: 'Z planera',
}));
addOrMergeShoppingLines(lines, KITCHEN_LIST_ID);
renderBoard();
updateBadge();
showAppToast(`Dodano ${shortfalls.length} pozycji z planera`);
}
function handleClearChecked() {
clearCheckedInList(KITCHEN_LIST_ID);
renderBoard();
updateBadge();
}
/* ══════════════════════ BADGE ══════════════════════ */
function updateBadge() {
const items = getKitchenItems();
const uncheckedCount = items.filter((i) => !i.checked).length;
const badge = document.getElementById('nav-shopping-badge');
if (!badge) return;
if (uncheckedCount > 0) {
badge.textContent = String(uncheckedCount > 99 ? '99+' : uncheckedCount);
badge.classList.remove('hidden');
} else {
badge.classList.add('hidden');
}
}
/* ══════════════════════ PUBLIC API ══════════════════════ */
export function refreshShoppingList() {
renderBoard();
updateBadge();
}
export function setupShoppingList() {
renderBoard();
updateBadge();
document.getElementById('sl-generate')?.addEventListener('click', handleGenerate);
document.getElementById('sl-clear-checked')?.addEventListener('click', handleClearChecked);
window.refreshShoppingList = refreshShoppingList;
}