Removable ingredients in planner

This commit is contained in:
2026-04-03 15:08:28 +02:00
parent 3758ab7b66
commit c11f184d1c
3 changed files with 50 additions and 105 deletions

View File

@@ -61,13 +61,13 @@ export function getMealPlanEditorHTML() {
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mt-3 mb-2">Pora posiłku</p>
<div id="mpe-slot-chips" class="flex flex-wrap gap-1.5"></div>
</div>
<div id="mpe-nutrition-section" class="mb-4"></div>
<div id="mpe-servings-row" class="flex items-center justify-between mb-4"></div>
<div id="mpe-ing-section" class="mb-4">
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mb-2">Składniki</p>
<div id="mpe-ing-list" class="space-y-1.5"></div>
<div id="mpe-add-area" class="mt-2"></div>
</div>
<div id="mpe-nutrition-section"></div>
</div>
<div class="shrink-0 px-5 pb-8 pt-3 border-t border-gray-100">
<button id="mpe-confirm-btn" type="button" class="w-full bg-gray-900 hover:bg-black text-white py-3 rounded-xl font-semibold text-[13px] transition-colors flex items-center justify-center gap-2">
@@ -230,9 +230,13 @@ export function setupMealPlanEditor() {
if (!r) return;
let html = '';
const removeBtn = (cls, attrs) =>
`<button type="button" class="${cls} shrink-0 w-6 h-6 rounded-full border border-gray-200 text-gray-300 hover:text-red-500 hover:border-red-200 hover:bg-red-50 flex items-center justify-center transition-colors" ${attrs}><i class="fas fa-minus text-[8px]"></i></button>`;
for (const ing of r.ingredients) {
const id = ing.ingredientId;
const excl = S.excluded.has(id);
if (S.excluded.has(id)) continue;
const eid = S.subs[id] || id;
const eDef = INGREDIENTS[eid];
const eName = eDef?.name || eid;
@@ -243,34 +247,26 @@ export function setupMealPlanEditor() {
const disp = base * S.servings;
const modified = id in S.overrides;
const checkCls = excl
? 'w-5 h-5 rounded-md border-2 border-gray-300 bg-white'
: 'w-5 h-5 rounded-md border-2 border-gray-900 bg-gray-900';
const checkIco = excl ? '' : '<i class="fas fa-check text-white text-[8px]"></i>';
const rowBorder = excl ? 'border-gray-100' : swapped ? 'border-amber-200' : 'border-gray-200';
const rowBg = excl ? 'bg-gray-50/50' : swapped ? 'bg-amber-50/30' : 'bg-white';
const rowOp = excl ? 'opacity-50' : '';
const nameCls = excl ? 'text-[12px] font-semibold text-gray-400 line-through' : 'text-[12px] font-semibold text-gray-900';
const amtCls = excl ? 'text-gray-300' : 'text-gray-900';
const unitCls = excl ? 'text-gray-300' : 'text-gray-500';
const rowBorder = swapped ? 'border-amber-200' : 'border-gray-200';
const rowBg = swapped ? 'bg-amber-50/30' : 'bg-white';
const shuffleBtn = hasAlts && !excl
const shuffleBtn = hasAlts
? `<button type="button" class="mpe-shuffle shrink-0 w-5 h-5 rounded-full ${swapped ? 'bg-amber-50 text-amber-500' : 'bg-gray-100 text-gray-400 hover:bg-gray-200'} flex items-center justify-center transition-colors" data-orig-id="${esc(id)}"><i class="fas fa-shuffle text-[8px]"></i></button>`
: '';
const modDot = modified && !excl ? '<span class="w-1.5 h-1.5 rounded-full bg-amber-400 shrink-0"></span>' : '';
const modDot = modified ? '<span class="w-1.5 h-1.5 rounded-full bg-amber-400 shrink-0"></span>' : '';
html += `<div class="mpe-ing-row rounded-xl border ${rowBorder} ${rowBg} ${rowOp} p-2.5" data-orig-id="${esc(id)}" data-type="recipe">`;
html += `<div class="flex items-center gap-2.5">`;
html += `<button type="button" class="mpe-toggle ${checkCls} flex items-center justify-center shrink-0" data-orig-id="${esc(id)}">${checkIco}</button>`;
html += `<div class="flex-1 min-w-0 flex items-center gap-1.5"><span class="${nameCls} truncate">${esc(eName)}</span>${shuffleBtn}</div>`;
html += `<button type="button" class="mpe-edit-amt shrink-0 flex items-center gap-1 px-2 py-1 rounded-lg ${excl ? '' : 'hover:bg-gray-100'} transition-colors" data-orig-id="${esc(id)}" data-type="recipe" ${excl ? 'disabled' : ''}>`;
html += `${modDot}<span class="text-[12px] font-semibold ${amtCls} tabular-nums">${fmtAmt(disp)}</span>`;
html += `<span class="text-[11px] ${unitCls}">${esc(ing.unit)}</span></button>`;
html += `<div class="mpe-ing-row rounded-xl border ${rowBorder} ${rowBg} p-2.5" data-orig-id="${esc(id)}" data-type="recipe">`;
html += `<div class="flex items-center gap-2">`;
html += `<div class="flex-1 min-w-0 flex items-center gap-1.5"><span class="text-[12px] font-semibold text-gray-900 truncate">${esc(eName)}</span>${shuffleBtn}</div>`;
html += `<button type="button" class="mpe-edit-amt shrink-0 flex items-center gap-1 px-2 py-1 rounded-lg hover:bg-gray-100 transition-colors" data-orig-id="${esc(id)}" data-type="recipe">`;
html += `${modDot}<span class="text-[12px] font-semibold text-gray-900 tabular-nums">${fmtAmt(disp)}</span>`;
html += `<span class="text-[11px] text-gray-500">${esc(ing.unit)}</span></button>`;
html += removeBtn('mpe-remove-ing', `data-orig-id="${esc(id)}" data-type="recipe"`);
html += `</div>`;
if (hasAlts && altOpen && !excl) {
if (hasAlts && altOpen) {
const opts = [id, ...ing.alternatives];
html += '<div class="mt-2 ml-7 space-y-1">';
html += '<div class="mt-2 ml-1 space-y-1">';
for (const altId of opts) {
const def = INGREDIENTS[altId];
const name = def?.name || altId;
@@ -293,17 +289,23 @@ export function setupMealPlanEditor() {
const def = INGREDIENTS[a.ingredientId];
const name = def?.name || a.ingredientId;
const disp = a.amount * S.servings;
html += `<div class="mpe-ing-row rounded-xl border border-emerald-200 bg-emerald-50/30 p-2.5" data-ing-id="${esc(a.ingredientId)}" data-type="added">
<div class="flex items-center gap-2.5">
<span class="shrink-0 w-5 h-5 rounded-md bg-emerald-100 flex items-center justify-center text-emerald-600"><i class="fas fa-plus text-[8px]"></i></span>
<span class="flex-1 min-w-0 text-[12px] font-semibold text-gray-900 truncate">${esc(name)}</span>
<button type="button" class="mpe-edit-amt shrink-0 flex items-center gap-1 px-2 py-1 rounded-lg hover:bg-gray-100 transition-colors" data-ing-id="${esc(a.ingredientId)}" data-type="added">
<span class="text-[12px] font-semibold text-gray-900 tabular-nums">${fmtAmt(disp)}</span>
<span class="text-[11px] text-gray-500">${esc(a.unit)}</span>
</button>
<button type="button" class="mpe-remove-added shrink-0 w-6 h-6 rounded-full border border-gray-200 text-gray-400 hover:text-red-600 hover:border-red-200 hover:bg-red-50 flex items-center justify-center transition-colors" data-ing-id="${esc(a.ingredientId)}"><i class="fas fa-times text-[8px]"></i></button>
</div>
</div>`;
html += `<div class="mpe-ing-row rounded-xl border border-dashed border-gray-300 bg-white p-2.5" data-ing-id="${esc(a.ingredientId)}" data-type="added">`;
html += `<div class="flex items-center gap-2">`;
html += `<div class="flex-1 min-w-0 flex items-center gap-1.5"><span class="text-[12px] font-semibold text-gray-900 truncate">${esc(name)}</span><span class="text-[9px] px-1.5 py-0.5 rounded bg-gray-100 text-gray-400 font-medium shrink-0">Dodany</span></div>`;
html += `<button type="button" class="mpe-edit-amt shrink-0 flex items-center gap-1 px-2 py-1 rounded-lg hover:bg-gray-100 transition-colors" data-ing-id="${esc(a.ingredientId)}" data-type="added">`;
html += `<span class="text-[12px] font-semibold text-gray-900 tabular-nums">${fmtAmt(disp)}</span>`;
html += `<span class="text-[11px] text-gray-500">${esc(a.unit)}</span></button>`;
html += removeBtn('mpe-remove-ing', `data-ing-id="${esc(a.ingredientId)}" data-type="added"`);
html += `</div></div>`;
}
if (S.excluded.size > 0) {
const cnt = S.excluded.size;
const label = cnt === 1 ? '1 składnik usunięty' : cnt < 5 ? `${cnt} składniki usunięte` : `${cnt} składników usuniętych`;
html += `<div class="flex items-center justify-between py-2 px-1">`;
html += `<span class="text-[11px] text-gray-400">${label}</span>`;
html += `<button type="button" id="mpe-restore-all" class="text-[11px] font-semibold text-gray-500 hover:text-gray-900 transition-colors">Przywróć</button>`;
html += `</div>`;
}
list.innerHTML = html;
@@ -515,10 +517,19 @@ export function setupMealPlanEditor() {
const ingSec = document.getElementById('mpe-ing-section');
ingSec?.addEventListener('click', (e) => {
const toggle = e.target.closest('.mpe-toggle');
if (toggle) {
const id = toggle.dataset.origId;
if (S.excluded.has(id)) S.excluded.delete(id); else S.excluded.add(id);
const remove = e.target.closest('.mpe-remove-ing');
if (remove) {
if (remove.dataset.type === 'added') {
S.added = S.added.filter((a) => a.ingredientId !== remove.dataset.ingId);
} else {
S.excluded.add(remove.dataset.origId);
}
renderIngredients(); renderNutrition();
return;
}
if (e.target.closest('#mpe-restore-all')) {
S.excluded.clear();
renderIngredients(); renderNutrition();
return;
}
@@ -547,13 +558,6 @@ export function setupMealPlanEditor() {
return;
}
const removeAdded = e.target.closest('.mpe-remove-added');
if (removeAdded) {
S.added = S.added.filter((a) => a.ingredientId !== removeAdded.dataset.ingId);
renderIngredients(); renderNutrition();
return;
}
if (e.target.closest('#mpe-add-btn')) {
S.addOpen = true; S.addQuery = '';
renderAddArea();