Extract colors
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m10s

This commit is contained in:
2026-04-18 11:12:05 +02:00
parent 59340e8afd
commit 5c21fb1e64
14 changed files with 485 additions and 430 deletions

View File

@@ -37,21 +37,21 @@ const slotLabel = Object.fromEntries(MEAL_SLOTS.map((s) => [s.id, s.label]));
export function getMealPlanEditorHTML() {
return `
<div id="mpe-overlay" class="absolute inset-0 z-[55] bg-black/45 hidden flex items-end" style="pointer-events:none">
<div id="mpe-sheet" class="w-full bg-[#2d2e2b] rounded-t-3xl shadow-lg flex flex-col overflow-hidden" style="pointer-events:auto; background:#2d2e2b !important; background-image:none !important; backdrop-filter:none !important; height:100dvh; max-height:100dvh; transform:translateY(100%); transition:transform 300ms cubic-bezier(0.32,0.72,0,1)">
<div class="shrink-0 px-5 pt-3 pb-2.5 border-b border-gray-100 bg-[#2d2e2b]" style="background:#2d2e2b !important; background-image:none !important;">
<div id="mpe-sheet" class="w-full bg-[rgb(var(--app-bg-rgb))] rounded-t-3xl shadow-lg flex flex-col overflow-hidden" style="pointer-events:auto; background:rgb(var(--app-bg-rgb)) !important; background-image:none !important; backdrop-filter:none !important; height:100dvh; max-height:100dvh; transform:translateY(100%); transition:transform 300ms cubic-bezier(0.32,0.72,0,1)">
<div class="shrink-0 px-5 pt-3 pb-2.5 border-b border-gray-100 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important;">
<div class="w-10 h-1 bg-gray-200 rounded-full mx-auto mb-3"></div>
<div class="flex items-start justify-between gap-3">
<div class="min-w-0 flex-1">
<h2 id="mpe-title" class="text-[15px] font-bold text-gray-900 leading-tight"></h2>
<p id="mpe-subtitle" class="text-[11px] text-gray-500 mt-0.5 truncate"></p>
</div>
<button id="mpe-confirm-btn" type="button" aria-label="Dodaj do planu" class="shrink-0 mt-0.5 border h-8 px-3 rounded-full font-semibold text-[12px] transition-colors inline-flex items-center justify-center gap-1.5" style="background:#dcd6cb !important; color:#2d2e2b !important; background-image:none !important; border-color:#dcd6cb !important; box-shadow:0 2px 10px rgba(0,0,0,0.18);">
<button id="mpe-confirm-btn" type="button" aria-label="Dodaj do planu" class="shrink-0 mt-0.5 border h-8 px-3 rounded-full font-semibold text-[12px] transition-colors inline-flex items-center justify-center gap-1.5" style="background:rgb(var(--text-body-rgb)) !important; color:rgb(var(--app-bg-rgb)) !important; background-image:none !important; border-color:rgb(var(--text-body-rgb)) !important; box-shadow:0 2px 10px rgba(0,0,0,0.18);">
<i id="mpe-confirm-icon" class="fas fa-plus text-[10px]" aria-hidden="true"></i>
<span id="mpe-confirm-label">Dodaj</span>
</button>
</div>
</div>
<div id="mpe-cal-wrap" class="hidden relative z-[1] shrink-0 px-5 pt-3 pb-3 bg-[#2d2e2b]" style="background:#2d2e2b !important; background-image:none !important;">
<div id="mpe-cal-wrap" class="hidden relative z-[1] shrink-0 px-5 pt-3 pb-3 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important;">
<div id="mpe-cal-section" class="hidden">
${createCalendarTopbarHTML({
prevId: 'mpe-cal-prev',
@@ -66,12 +66,12 @@ export function getMealPlanEditorHTML() {
<div id="mpe-slot-chips" class="flex flex-wrap gap-1.5"></div>
</div>
</div>
<div id="mpe-summary-wrap" class="relative z-[1] shrink-0 px-5 pb-3 bg-[#2d2e2b]" style="background:#2d2e2b !important; background-image:none !important;">
<div id="mpe-summary-wrap" class="relative z-[1] shrink-0 px-5 pb-3 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important;">
<div id="mpe-nutrition-section"></div>
<div id="mpe-servings-row" class="mt-3"></div>
<div id="mpe-top-shadow" class="pointer-events-none absolute inset-x-0 -bottom-3 h-3 opacity-0 transition-opacity duration-200" style="background:linear-gradient(to bottom, rgba(0,0,0,0.12), rgba(0,0,0,0.03), rgba(0,0,0,0));"></div>
</div>
<div id="mpe-ing-scroll" class="flex-1 min-h-0 overflow-y-auto no-scrollbar px-5 bg-[#2d2e2b]" style="background:#2d2e2b !important; background-image:none !important; padding-bottom:calc(1.5rem + env(safe-area-inset-bottom));">
<div id="mpe-ing-scroll" class="flex-1 min-h-0 overflow-y-auto no-scrollbar px-5 bg-[rgb(var(--app-bg-rgb))]" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important; padding-bottom:calc(1.5rem + env(safe-area-inset-bottom));">
<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>
@@ -214,9 +214,9 @@ export function setupMealPlanEditor() {
if (!r) return;
el.innerHTML = MEAL_SLOTS.filter((s) => r.allowedSlots.includes(s.id)).map((s) => {
const sel = s.id === S.slotId;
const bg = sel ? '#23221e' : '#2f2f2d';
const border = sel ? '#787876' : '#444442';
const text = sel ? '#f2efe8' : '#d7d2c8';
const bg = sel ? 'rgb(var(--sunken-rgb))' : 'rgb(var(--card-soft-rgb))';
const border = sel ? 'rgb(var(--border-input-rgb))' : 'rgb(var(--card-strong-rgb))';
const text = sel ? 'rgb(var(--text-emphasis-rgb))' : 'rgb(var(--text-body-soft-rgb))';
return `<button type="button" class="mpe-slot-btn px-3 py-1.5 rounded-full border text-[12px] font-semibold transition-colors" data-slot-id="${s.id}" style="background:${bg} !important; background-image:none !important; box-shadow:none !important; border-color:${border} !important; color:${text} !important;">${esc(s.label)}</button>`;
}).join('');
}
@@ -245,12 +245,12 @@ export function setupMealPlanEditor() {
el.innerHTML = `
<div class="flex items-center justify-between gap-3">
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider">Porcje</p>
<div class="flex h-[2rem] w-[5.25rem] shrink-0 items-center gap-0.5 rounded-full border px-0.5" style="background:#2f2f2d;border-color:#444442;box-shadow:0 2px 8px rgba(0,0,0,0.25);">
<button type="button" id="mpe-serv-minus" class="shrink-0 w-7 h-full flex items-center justify-center rounded-full border-0 bg-transparent text-[#d7d2c8] transition-colors" aria-label="Zmniejsz liczbę porcji">
<div class="flex h-[2rem] w-[5.25rem] shrink-0 items-center gap-0.5 rounded-full border px-0.5" style="background:rgb(var(--card-soft-rgb));border-color:rgb(var(--card-strong-rgb));box-shadow:0 2px 8px rgba(0,0,0,0.25);">
<button type="button" id="mpe-serv-minus" class="shrink-0 w-7 h-full flex items-center justify-center rounded-full border-0 bg-transparent text-[rgb(var(--text-body-soft-rgb))] transition-colors" aria-label="Zmniejsz liczbę porcji">
<i class="fas fa-minus text-[10px]"></i>
</button>
<span id="mpe-serv-count" class="flex-1 h-full inline-flex items-center justify-center px-0.5 text-[12px] font-semibold leading-none text-[#d7d2c8] tabular-nums">${S.servings}</span>
<button type="button" id="mpe-serv-plus" class="shrink-0 w-7 h-full flex items-center justify-center rounded-full border-0 bg-transparent text-[#d7d2c8] transition-colors" aria-label="Zwiększ liczbę porcji">
<span id="mpe-serv-count" class="flex-1 h-full inline-flex items-center justify-center px-0.5 text-[12px] font-semibold leading-none text-[rgb(var(--text-body-soft-rgb))] tabular-nums">${S.servings}</span>
<button type="button" id="mpe-serv-plus" class="shrink-0 w-7 h-full flex items-center justify-center rounded-full border-0 bg-transparent text-[rgb(var(--text-body-soft-rgb))] transition-colors" aria-label="Zwiększ liczbę porcji">
<i class="fas fa-plus text-[10px]"></i>
</button>
</div>
@@ -269,7 +269,7 @@ export function setupMealPlanEditor() {
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>`;
const ingredientRowClass = 'mpe-ing-row rounded-xl px-3 py-3';
const ingredientRowStyle = 'background:#393937 !important; background-image:none !important; box-shadow:0 2px 8px rgba(0,0,0,0.25) !important; border:none !important;';
const ingredientRowStyle = 'background:rgb(var(--card-rgb)) !important; background-image:none !important; box-shadow:0 2px 8px rgba(0,0,0,0.25) !important; border:none !important;';
for (const ing of r.ingredients) {
const id = ing.ingredientId;
@@ -317,12 +317,12 @@ export function setupMealPlanEditor() {
const isSel = eid === altId;
const checkbox = `
<span class="ml-auto self-center w-[18px] h-[18px] rounded-full shrink-0 flex items-center justify-center"
style="border:1.5px solid #56534f; background:transparent;">
${isSel ? '<i class="fas fa-check" style="color:#9b978f; font-size:8px; line-height:1; display:block; transform:translateY(0.5px);"></i>' : ''}
style="border:1.5px solid rgba(var(--border-input-rgb), 0.58); background:transparent;">
${isSel ? '<i class="fas fa-check" style="color:rgb(var(--text-dim-rgb)); font-size:8px; line-height:1; display:block; transform:translateY(0.5px);"></i>' : ''}
</span>`;
const n = nutFor(altId, disp, ing.unit);
const nLine = n ? `<div class="text-[10px] text-gray-400 mt-0.5 tabular-nums">${n.kcal} kcal · ${n.protein}g B · ${n.fat}g T · ${n.carbs}g W</div>` : '';
html += `<button type="button" class="mpe-alt-pick w-full text-left p-2.5 rounded-lg transition-all" style="background:#2f2f2d !important; background-image:none !important; border:none !important; box-shadow:none !important;" data-orig-id="${esc(id)}" data-alt-id="${esc(altId)}"><div class="flex items-center gap-3"><div class="min-w-0 flex-1"><div class="text-[11px] font-semibold text-gray-900">${esc(name)}</div>${nLine}</div>${checkbox}</div></button>`;
html += `<button type="button" class="mpe-alt-pick w-full text-left p-2.5 rounded-lg transition-all" style="background:rgb(var(--card-soft-rgb)) !important; background-image:none !important; border:none !important; box-shadow:none !important;" data-orig-id="${esc(id)}" data-alt-id="${esc(altId)}"><div class="flex items-center gap-3"><div class="min-w-0 flex-1"><div class="text-[11px] font-semibold text-gray-900">${esc(name)}</div>${nLine}</div>${checkbox}</div></button>`;
}
html += '</div>';
}
@@ -341,7 +341,7 @@ export function setupMealPlanEditor() {
const addedBadge = addedProduct
? `<div class="flex items-center gap-1 mt-0.5"><span class="text-[10px] text-emerald-400 truncate">${esc(addedProduct.name)}</span></div>`
: '';
html += `<div class="flex-1 min-w-0 mpe-open-product-card cursor-pointer" data-eid="${esc(a.ingredientId)}" data-pid="${esc(addedPid)}"><div class="flex items-center gap-1.5"><span class="text-[12px] font-semibold text-gray-900 truncate">${esc(name)}</span><span class="shrink-0 inline-flex items-center justify-center text-[#8f8b84]" title="Dodany składnik" aria-label="Dodany składnik"><i class="fas fa-plus text-[8px]"></i></span></div>${addedBadge}</div>`;
html += `<div class="flex-1 min-w-0 mpe-open-product-card cursor-pointer" data-eid="${esc(a.ingredientId)}" data-pid="${esc(addedPid)}"><div class="flex items-center gap-1.5"><span class="text-[12px] font-semibold text-gray-900 truncate">${esc(name)}</span><span class="shrink-0 inline-flex items-center justify-center text-[rgb(var(--text-faint-rgb))]" title="Dodany składnik" aria-label="Dodany składnik"><i class="fas fa-plus text-[8px]"></i></span></div>${addedBadge}</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>`;
@@ -365,7 +365,7 @@ export function setupMealPlanEditor() {
const el = document.getElementById('mpe-add-area');
if (!el) return;
if (!S.addOpen) {
el.innerHTML = `<button type="button" id="mpe-add-btn" class="w-full py-2 rounded-xl border border-dashed text-[12px] font-semibold transition-colors" style="border-color:#444442; color:#9b978f; background:transparent !important; box-shadow:none !important; -webkit-tap-highlight-color:transparent;"><i class="fas fa-plus text-[10px] mr-1.5 opacity-70"></i>Dodaj składnik</button>`;
el.innerHTML = `<button type="button" id="mpe-add-btn" class="w-full py-2 rounded-xl border border-dashed text-[12px] font-semibold transition-colors" style="border-color:rgb(var(--card-strong-rgb)); color:rgb(var(--text-dim-rgb)); background:transparent !important; box-shadow:none !important; -webkit-tap-highlight-color:transparent;"><i class="fas fa-plus text-[10px] mr-1.5 opacity-70"></i>Dodaj składnik</button>`;
return;
}
const recipe = RECIPES[S.recipeId];
@@ -376,15 +376,15 @@ export function setupMealPlanEditor() {
const q = S.addQuery.toLowerCase().trim();
const avail = Object.values(INGREDIENTS).filter((i) => !usedIds.has(i.id) && (!q || i.name.toLowerCase().includes(q)));
el.innerHTML = `
<div class="rounded-xl p-3" style="background:#393937 !important; border:1px solid #444442;">
<div class="rounded-xl p-3" style="background:rgb(var(--card-rgb)) !important; border:1px solid rgb(var(--card-strong-rgb));">
<div class="flex items-center gap-2 mb-2">
<input type="text" id="mpe-add-search" class="flex-1 rounded-lg px-3 py-1.5 text-[12px] outline-none placeholder:text-[#8f8b84]" style="background:#2f2f2d !important; border:1px solid #444442; color:#ddd6ca;" placeholder="Szukaj składnika…" value="${esc(S.addQuery)}">
<button type="button" id="mpe-add-cancel" class="text-[11px] font-semibold px-2 py-1 transition-colors" style="color:#9b978f;">Anuluj</button>
<input type="text" id="mpe-add-search" class="flex-1 rounded-lg px-3 py-1.5 text-[12px] outline-none placeholder:text-[rgb(var(--text-faint-rgb))]" style="background:rgb(var(--card-soft-rgb)) !important; border:1px solid rgb(var(--card-strong-rgb)); color:rgb(var(--text-body-rgb));" placeholder="Szukaj składnika…" value="${esc(S.addQuery)}">
<button type="button" id="mpe-add-cancel" class="text-[11px] font-semibold px-2 py-1 transition-colors" style="color:rgb(var(--text-dim-rgb));">Anuluj</button>
</div>
<div class="max-h-40 overflow-y-auto space-y-1 no-scrollbar" id="mpe-add-results">
${avail.length === 0
? '<p class="rounded-lg px-2.5 py-3 text-[11px] text-center" style="background:#2f2f2d !important; color:#9b978f;">Brak wyników</p>'
: avail.slice(0, 20).map((i) => `<button type="button" class="mpe-add-pick w-full text-left px-3 py-3 rounded-lg transition-colors text-[12px] font-medium" style="background:#2f2f2d !important; color:#ddd6ca;" data-ing-id="${esc(i.id)}">${esc(i.name)}</button>`).join('')}
? '<p class="rounded-lg px-2.5 py-3 text-[11px] text-center" style="background:rgb(var(--card-soft-rgb)) !important; color:rgb(var(--text-dim-rgb));">Brak wyników</p>'
: avail.slice(0, 20).map((i) => `<button type="button" class="mpe-add-pick w-full text-left px-3 py-3 rounded-lg transition-colors text-[12px] font-medium" style="background:rgb(var(--card-soft-rgb)) !important; color:rgb(var(--text-body-rgb));" data-ing-id="${esc(i.id)}">${esc(i.name)}</button>`).join('')}
</div>
</div>`;
}
@@ -400,8 +400,8 @@ export function setupMealPlanEditor() {
const q = S.addQuery.toLowerCase().trim();
const avail = Object.values(INGREDIENTS).filter((i) => !usedIds.has(i.id) && (!q || i.name.toLowerCase().includes(q)));
results.innerHTML = avail.length === 0
? '<p class="rounded-lg px-2.5 py-3 text-[11px] text-center" style="background:#2f2f2d !important; color:#9b978f;">Brak wyników</p>'
: avail.slice(0, 20).map((i) => `<button type="button" class="mpe-add-pick w-full text-left px-3 py-3 rounded-lg transition-colors text-[12px] font-medium" style="background:#2f2f2d !important; color:#ddd6ca;" data-ing-id="${esc(i.id)}">${esc(i.name)}</button>`).join('');
? '<p class="rounded-lg px-2.5 py-3 text-[11px] text-center" style="background:rgb(var(--card-soft-rgb)) !important; color:rgb(var(--text-dim-rgb));">Brak wyników</p>'
: avail.slice(0, 20).map((i) => `<button type="button" class="mpe-add-pick w-full text-left px-3 py-3 rounded-lg transition-colors text-[12px] font-medium" style="background:rgb(var(--card-soft-rgb)) !important; color:rgb(var(--text-body-rgb));" data-ing-id="${esc(i.id)}">${esc(i.name)}</button>`).join('');
}
function renderIngredients() {
@@ -417,23 +417,23 @@ export function setupMealPlanEditor() {
if (!el) return;
const n = totalNutrition();
el.innerHTML = `
<div class="h-full pb-2 flex flex-col" style="background:#2d2e2b !important; background-image:none !important; box-shadow:none !important;">
<div class="h-full pb-2 flex flex-col" style="background:rgb(var(--app-bg-rgb)) !important; background-image:none !important; box-shadow:none !important;">
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mb-2">Wartości odżywcze</p>
<div class="flex-1 flex items-center">
<div class="grid grid-cols-4 gap-1.5 w-full">
<div class="rounded-xl px-2 py-[0.5625rem] text-center" style="background:#393937;">
<div class="rounded-xl px-2 py-[0.5625rem] text-center" style="background:rgb(var(--card-rgb));">
<p class="text-[15px] font-bold text-gray-100 tabular-nums leading-tight">${n.kcal}</p>
<p class="text-[9px] text-gray-500 font-medium">kcal</p>
</div>
<div class="rounded-xl px-2 py-[0.5625rem] text-center" style="background:#393937;">
<div class="rounded-xl px-2 py-[0.5625rem] text-center" style="background:rgb(var(--card-rgb));">
<p class="text-[15px] font-bold text-blue-400 tabular-nums leading-tight">${n.protein}g</p>
<p class="text-[9px] text-gray-500 font-medium">białko</p>
</div>
<div class="rounded-xl px-2 py-[0.5625rem] text-center" style="background:#393937;">
<div class="rounded-xl px-2 py-[0.5625rem] text-center" style="background:rgb(var(--card-rgb));">
<p class="text-[15px] font-bold text-amber-400 tabular-nums leading-tight">${n.fat}g</p>
<p class="text-[9px] text-gray-500 font-medium">tłuszcz</p>
</div>
<div class="rounded-xl px-2 py-[0.5625rem] text-center" style="background:#393937;">
<div class="rounded-xl px-2 py-[0.5625rem] text-center" style="background:rgb(var(--card-rgb));">
<p class="text-[15px] font-bold text-orange-400 tabular-nums leading-tight">${n.carbs}g</p>
<p class="text-[9px] text-gray-500 font-medium">węglowodany</p>
</div>
@@ -753,7 +753,7 @@ export function setupMealPlanEditor() {
const ingBase = S.overrides[origId] ?? recipeIng?.amount ?? 0;
const ingDisp = ingBase * S.servings;
const checkmark = (sel) => `<span class="ml-auto self-center w-[18px] h-[18px] rounded-full shrink-0 flex items-center justify-center" style="border:1.5px solid #56534f; background:transparent;">${sel ? '<i class="fas fa-check" style="color:#9b978f; font-size:8px; line-height:1; display:block; transform:translateY(0.5px);"></i>' : ''}</span>`;
const checkmark = (sel) => `<span class="ml-auto self-center w-[18px] h-[18px] rounded-full shrink-0 flex items-center justify-center" style="border:1.5px solid rgba(var(--border-input-rgb), 0.58); background:transparent;">${sel ? '<i class="fas fa-check" style="color:rgb(var(--text-dim-rgb)); font-size:8px; line-height:1; display:block; transform:translateY(0.5px);"></i>' : ''}</span>`;
let pickerHtml = '<div class="mpe-product-picker mt-2 ml-1 space-y-1">';
for (const p of products) {
@@ -766,7 +766,7 @@ export function setupMealPlanEditor() {
const f = g / 100;
const n = pNut ? { kcal: Math.round(pNut.kcal * f), protein: Math.round(pNut.protein * f * 10) / 10, fat: Math.round(pNut.fat * f * 10) / 10, carbs: Math.round(pNut.carbs * f * 10) / 10 } : null;
const nLine = n ? `<div class="text-[10px] text-gray-400 mt-0.5 tabular-nums">${n.kcal} kcal · ${n.protein}g B · ${n.fat}g T · ${n.carbs}g W</div>` : '';
pickerHtml += `<button type="button" class="mpe-prod-pick w-full text-left p-2.5 rounded-lg transition-all" style="background:#2f2f2d !important; background-image:none !important; border:none !important; box-shadow:none !important;" data-eid="${esc(eid)}" data-prod-id="${esc(p.id)}">
pickerHtml += `<button type="button" class="mpe-prod-pick w-full text-left p-2.5 rounded-lg transition-all" style="background:rgb(var(--card-soft-rgb)) !important; background-image:none !important; border:none !important; box-shadow:none !important;" data-eid="${esc(eid)}" data-prod-id="${esc(p.id)}">
<div class="flex items-center gap-3"><div class="min-w-0 flex-1"><div class="text-[11px] font-semibold text-gray-900">${esc(p.name)}</div>${nLine}</div>${checkmark(isSel)}</div>
</button>`;
}