Add ingredients' products
Some checks failed
Build and Deploy / build-and-push (push) Failing after 1m20s
Some checks failed
Build and Deploy / build-and-push (push) Failing after 1m20s
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { RECIPES } from '../data/catalog.js?v=2';
|
||||
import { RECIPES } from '../data/catalog.js?v=6';
|
||||
import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
||||
import { applyFilters, getFilterState } from './RecipeList.js';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { INGREDIENTS, RECIPES } from '../data/catalog.js?v=2';
|
||||
import { INGREDIENTS, RECIPES } from '../data/catalog.js?v=6';
|
||||
import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
||||
import {
|
||||
addMonths,
|
||||
@@ -15,15 +15,15 @@ import {
|
||||
countDayShortfalls,
|
||||
dayHasAnyMeal,
|
||||
sumDayNutrition,
|
||||
} from '../services/planIngredients.js';
|
||||
import { addOrMergeShoppingLines, loadPantry } from '../services/pantryShopping.js';
|
||||
} from '../services/planIngredients.js?v=3';
|
||||
import { addOrMergeShoppingLines, loadPantry } from '../services/pantryShopping.js?v=2';
|
||||
import {
|
||||
dateKey,
|
||||
getDayPlan,
|
||||
loadPlans,
|
||||
newPlanEntryId,
|
||||
savePlans,
|
||||
} from '../services/planStore.js';
|
||||
} from '../services/planStore.js?v=2';
|
||||
import {
|
||||
CALENDAR_HANDLE_CLASS,
|
||||
CALENDAR_MONTHS_SHORT,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import {
|
||||
INGREDIENTS,
|
||||
CATEGORY_LABELS,
|
||||
PRODUCTS,
|
||||
pantryQtyStep,
|
||||
} from '../data/catalog.js?v=2';
|
||||
import { addIngredientToKitchenList, categoryLabel, loadPantry, setPantryQty } from '../services/pantryShopping.js';
|
||||
getProductsForIngredient,
|
||||
ingredientHasProducts,
|
||||
} from '../data/catalog.js?v=6';
|
||||
import { addIngredientToKitchenList, categoryLabel, loadPantry, setPantryQty, setPantryProductQty, getPantryTotal, getPantryProducts, getPantryGeneric } from '../services/pantryShopping.js?v=2';
|
||||
import { showAppToast } from '../ui/toast.js';
|
||||
|
||||
/* ── helpers ── */
|
||||
@@ -97,6 +100,8 @@ export function getPantryHTML() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="pv2-product-breakdown" class="shrink-0"></div>
|
||||
|
||||
<div class="border-t border-gray-100 shrink-0"></div>
|
||||
|
||||
<div class="shrink-0 space-y-1">
|
||||
@@ -172,7 +177,7 @@ function getFilteredIds(searchRaw) {
|
||||
|
||||
function chipHtml(id, pantry) {
|
||||
const def = INGREDIENTS[id];
|
||||
const qty = Number(pantry[id]) || 0;
|
||||
const qty = getPantryTotal(id, pantry);
|
||||
const u = unitLabel(def.pantryUnit);
|
||||
|
||||
if (qty > 0) {
|
||||
@@ -209,7 +214,7 @@ function renderBoard() {
|
||||
const pantry = loadPantry();
|
||||
const allFiltered = getFilteredIds(q);
|
||||
const visible = showOnlyStock
|
||||
? allFiltered.filter(id => (Number(pantry[id]) || 0) > 0)
|
||||
? allFiltered.filter(id => getPantryTotal(id, pantry) > 0)
|
||||
: allFiltered;
|
||||
|
||||
if (visible.length === 0) {
|
||||
@@ -271,7 +276,7 @@ function openEditSheet(ingredientId) {
|
||||
editingId = ingredientId;
|
||||
|
||||
const pantry = loadPantry();
|
||||
const qty = Number(pantry[ingredientId]) || 0;
|
||||
const qty = getPantryTotal(ingredientId, pantry);
|
||||
const u = unitLabel(def.pantryUnit);
|
||||
const step = pantryQtyStep(ingredientId);
|
||||
const pack = def.purchasePack;
|
||||
@@ -311,6 +316,16 @@ function openEditSheet(ingredientId) {
|
||||
}
|
||||
}
|
||||
|
||||
// Hide main +/- when products exist (total is sum of product rows)
|
||||
const hasProds = ingredientHasProducts(ingredientId);
|
||||
const mainMinus = document.getElementById('pv2-edit-minus');
|
||||
const mainPlus = document.getElementById('pv2-edit-plus');
|
||||
const mainQtyInput = document.getElementById('pv2-edit-qty');
|
||||
if (mainMinus) mainMinus.classList.toggle('hidden', hasProds);
|
||||
if (mainPlus) mainPlus.classList.toggle('hidden', hasProds);
|
||||
if (mainQtyInput) mainQtyInput.readOnly = hasProds;
|
||||
|
||||
renderProductBreakdown(ingredientId, pantry);
|
||||
renderNutritionInSheet(def);
|
||||
|
||||
const bg = document.getElementById('pv2-edit-bg');
|
||||
@@ -331,6 +346,83 @@ function nutritionListRow(label, valueHtml) {
|
||||
</li>`;
|
||||
}
|
||||
|
||||
function renderProductBreakdown(ingredientId, pantry) {
|
||||
const wrap = document.getElementById('pv2-product-breakdown');
|
||||
if (!wrap) return;
|
||||
const products = getProductsForIngredient(ingredientId);
|
||||
if (products.length === 0) { wrap.innerHTML = ''; return; }
|
||||
|
||||
const def = INGREDIENTS[ingredientId];
|
||||
const u = unitLabel(def.pantryUnit);
|
||||
const pantryProducts = getPantryProducts(ingredientId, pantry);
|
||||
const generic = getPantryGeneric(ingredientId, pantry);
|
||||
|
||||
const productQty = (pid) => {
|
||||
const item = pantryProducts.find(i => i.productId === pid);
|
||||
return item ? item.qty : 0;
|
||||
};
|
||||
|
||||
const rows = products.map(p => {
|
||||
const q = Math.round(productQty(p.id));
|
||||
return `<div class="flex items-center gap-1.5 py-1" data-product-row="${esc(p.id)}">
|
||||
<span class="flex-1 text-[12px] text-gray-700 truncate" title="${esc(p.name)}">${esc(p.name)}</span>
|
||||
<button type="button" class="pv2-prod-minus w-7 h-7 rounded-lg bg-gray-100 text-gray-600 hover:bg-gray-200 flex items-center justify-center transition-colors active:scale-95 shrink-0" data-pid="${esc(p.id)}" data-step="${p.packSize}">
|
||||
<i class="fas fa-minus text-[9px]"></i>
|
||||
</button>
|
||||
<span class="pv2-prod-qty w-12 text-center text-[13px] font-semibold tabular-nums text-gray-800">${q} ${esc(u)}</span>
|
||||
<button type="button" class="pv2-prod-plus w-7 h-7 rounded-lg bg-gray-100 text-gray-600 hover:bg-gray-200 flex items-center justify-center transition-colors active:scale-95 shrink-0" data-pid="${esc(p.id)}" data-step="${p.packSize}">
|
||||
<i class="fas fa-plus text-[9px]"></i>
|
||||
</button>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
const genericRow = `<div class="flex items-center gap-1.5 py-1" data-product-row="_generic">
|
||||
<span class="flex-1 text-[12px] text-gray-500 italic truncate">Nieokreślony</span>
|
||||
<button type="button" class="pv2-prod-minus w-7 h-7 rounded-lg bg-gray-100 text-gray-600 hover:bg-gray-200 flex items-center justify-center transition-colors active:scale-95 shrink-0" data-pid="_generic" data-step="${pantryQtyStep(ingredientId)}">
|
||||
<i class="fas fa-minus text-[9px]"></i>
|
||||
</button>
|
||||
<span class="pv2-prod-qty w-12 text-center text-[13px] font-semibold tabular-nums text-gray-800">${Math.round(generic)} ${esc(u)}</span>
|
||||
<button type="button" class="pv2-prod-plus w-7 h-7 rounded-lg bg-gray-100 text-gray-600 hover:bg-gray-200 flex items-center justify-center transition-colors active:scale-95 shrink-0" data-pid="_generic" data-step="${pantryQtyStep(ingredientId)}">
|
||||
<i class="fas fa-plus text-[9px]"></i>
|
||||
</button>
|
||||
</div>`;
|
||||
|
||||
wrap.innerHTML = `
|
||||
<div class="space-y-0.5 px-0.5">
|
||||
<p class="text-[9px] font-semibold uppercase tracking-wide text-gray-400 mb-1">Produkty</p>
|
||||
${rows}
|
||||
${genericRow}
|
||||
</div>`;
|
||||
|
||||
// Bind product +/- buttons
|
||||
wrap.querySelectorAll('.pv2-prod-plus, .pv2-prod-minus').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
if (!editingId) return;
|
||||
const pid = btn.dataset.pid;
|
||||
const step = Number(btn.dataset.step) || 1;
|
||||
const isPlus = btn.classList.contains('pv2-prod-plus');
|
||||
const pantry = loadPantry();
|
||||
|
||||
if (pid === '_generic') {
|
||||
const cur = getPantryGeneric(editingId, pantry);
|
||||
const next = Math.max(0, cur + (isPlus ? step : -step));
|
||||
setPantryQty(editingId, next);
|
||||
} else {
|
||||
const items = getPantryProducts(editingId, pantry);
|
||||
const cur = items.find(i => i.productId === pid)?.qty || 0;
|
||||
const next = Math.max(0, cur + (isPlus ? step : -step));
|
||||
setPantryProductQty(editingId, pid, next);
|
||||
}
|
||||
|
||||
// Re-render breakdown and total
|
||||
const freshPantry = loadPantry();
|
||||
renderProductBreakdown(editingId, freshPantry);
|
||||
const totalQty = getPantryTotal(editingId, freshPantry);
|
||||
setEditQty(totalQty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renderNutritionInSheet(def) {
|
||||
const wrap = document.getElementById('pv2-edit-nutrition');
|
||||
if (!wrap) return;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RECIPES, INGREDIENTS } from '../data/catalog.js?v=2';
|
||||
import { RECIPES, INGREDIENTS } from '../data/catalog.js?v=6';
|
||||
import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
||||
|
||||
function escapeHtml(s) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RECIPES } from '../data/catalog.js?v=2';
|
||||
import { RECIPES } from '../data/catalog.js?v=6';
|
||||
import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
||||
|
||||
function escapeHtml(s) {
|
||||
|
||||
Reference in New Issue
Block a user