Replace ingredients images with SVGs

This commit is contained in:
2026-04-17 20:38:57 +02:00
parent 9bd6627fe2
commit 35b8babd0c
91 changed files with 112 additions and 53 deletions

View File

@@ -38,7 +38,6 @@ export const INGREDIENTS = {
/* ── Nabiał ───────────────────────────────────────── */
jajko: {
id: 'jajko',
image: 'images/ingredients/jajko.jpg',
name: 'Jajka',
category: 'nabial',
pantryUnit: 'szt',
@@ -120,7 +119,6 @@ export const INGREDIENTS = {
},
losos_wedzony: {
id: 'losos_wedzony',
image: 'images/ingredients/losos_wedzony.jpg',
name: 'Łosoś wędzony',
category: 'mieso_ryby',
pantryUnit: 'g',
@@ -138,7 +136,6 @@ export const INGREDIENTS = {
/* ── Warzywa ──────────────────────────────────────── */
pomidor: {
id: 'pomidor',
image: 'images/ingredients/pomidor.jpg',
name: 'Pomidor',
category: 'warzywa',
pantryUnit: 'szt',
@@ -147,7 +144,6 @@ export const INGREDIENTS = {
},
pomidorki_koktajlowe: {
id: 'pomidorki_koktajlowe',
image: 'images/ingredients/pomidorki_koktajlowe.jpg',
name: 'Pomidorki koktajlowe',
category: 'warzywa',
pantryUnit: 'g',
@@ -156,7 +152,6 @@ export const INGREDIENTS = {
},
papryka_czerwona: {
id: 'papryka_czerwona',
image: 'images/ingredients/papryka_czerwona.jpg',
name: 'Papryka czerwona',
category: 'warzywa',
pantryUnit: 'szt',
@@ -165,7 +160,6 @@ export const INGREDIENTS = {
},
ogorek: {
id: 'ogorek',
image: 'images/ingredients/ogorek.jpg',
name: 'Ogórek',
category: 'warzywa',
pantryUnit: 'szt',
@@ -174,7 +168,6 @@ export const INGREDIENTS = {
},
czosnek: {
id: 'czosnek',
image: 'images/ingredients/czosnek.jpg',
name: 'Czosnek',
category: 'warzywa',
pantryUnit: 'szt',
@@ -253,7 +246,6 @@ export const INGREDIENTS = {
/* ── Owoce ────────────────────────────────────────── */
truskawki: {
id: 'truskawki',
image: 'images/ingredients/truskawki.jpg',
name: 'Truskawki',
category: 'owoce',
pantryUnit: 'g',
@@ -261,7 +253,6 @@ export const INGREDIENTS = {
},
borowki_amerykanskie: {
id: 'borowki_amerykanskie',
image: 'images/ingredients/borowki_amerykanskie.jpg',
name: 'Borówki amerykańskie',
category: 'owoce',
pantryUnit: 'g',
@@ -269,7 +260,6 @@ export const INGREDIENTS = {
},
banany: {
id: 'banany',
image: 'images/ingredients/banany.jpg',
name: 'Banany',
category: 'owoce',
pantryUnit: 'g',
@@ -277,7 +267,6 @@ export const INGREDIENTS = {
},
jagody: {
id: 'jagody',
image: 'images/ingredients/jagody.jpg',
name: 'Jagody',
category: 'owoce',
pantryUnit: 'g',
@@ -301,7 +290,6 @@ export const INGREDIENTS = {
/* ── Suche i kasze ────────────────────────────────── */
makaron_suchy: {
id: 'makaron_suchy',
image: 'images/ingredients/makaron_suchy.jpg',
name: 'Makaron',
category: 'suche',
pantryUnit: 'g',
@@ -337,7 +325,6 @@ export const INGREDIENTS = {
},
migdaly: {
id: 'migdaly',
image: 'images/ingredients/migdaly.jpg',
name: 'Migdały',
category: 'suche',
pantryUnit: 'g',
@@ -448,7 +435,6 @@ export const INGREDIENTS = {
},
oliwa: {
id: 'oliwa',
image: 'images/ingredients/oliwa.jpg',
name: 'Oliwa z oliwek',
category: 'inne',
pantryUnit: 'ml',
@@ -456,7 +442,6 @@ export const INGREDIENTS = {
},
hummus: {
id: 'hummus',
image: 'images/ingredients/hummus.jpg',
name: 'Hummus',
category: 'inne',
pantryUnit: 'g',
@@ -528,6 +513,10 @@ export const INGREDIENTS = {
},
};
for (const [id, def] of Object.entries(INGREDIENTS)) {
def.image = `icons/ingredients/${id}.svg`;
}
/**
* @typedef {{ ingredientId: string, amount: number, unit: string, alternatives?: string[] }} RecipeIngredientDef
* @typedef {{ id: string, title: string, minutes: number, thumbLabel: string, image?: string, allowedSlots: string[], tags?: string[], nutritionPerServing: NutritionPer100, ingredients: RecipeIngredientDef[], steps: string[] }} RecipeDef

View File

@@ -1,4 +1,4 @@
import { INGREDIENTS, CATEGORY_LABELS, PRODUCTS, ingredientHasProducts } from '../data/catalog.js?v=8';
import { INGREDIENTS, CATEGORY_LABELS, PRODUCTS, ingredientHasProducts } from '../data/catalog.js?v=9';
import { PANTRY_STORAGE_KEY, PANTRY_STORAGE_KEY_V2, SHOPPING_STORAGE_KEY } from '../storageKeys.js';
export const KITCHEN_LIST_ID = 'kitchen';

View File

@@ -1,4 +1,4 @@
import { INGREDIENTS, RECIPES, PRODUCTS, getProductsForIngredient } from '../data/catalog.js?v=8';
import { INGREDIENTS, RECIPES, PRODUCTS, getProductsForIngredient } from '../data/catalog.js?v=9';
import { MEAL_SLOTS } from '../planner/mealSlots.js';
import { addDays } from './dateUtils.js';
import { dateKey, getDayPlan } from './planStore.js?v=2';

View File

@@ -1,4 +1,4 @@
import { INGREDIENTS, RECIPES, PRODUCTS } from '../data/catalog.js?v=8';
import { INGREDIENTS, RECIPES, PRODUCTS } from '../data/catalog.js?v=9';
import { MEAL_SLOTS } from '../planner/mealSlots.js';
import { PLANS_STORAGE_KEY } from '../storageKeys.js';
import { startOfDay } from './dateUtils.js';

View File

@@ -5,7 +5,7 @@ import {
getProductsForIngredient,
ingredientHasProducts,
pantryQtyStep,
} from '../data/catalog.js?v=8';
} from '../data/catalog.js?v=9';
import {
addOrMergeShoppingLines,
KITCHEN_LIST_ID,
@@ -76,7 +76,8 @@ function macroLine(n) {
function mediaHtml(image, icon, sizeClass = 'w-9 h-9', radiusClass = 'rounded-lg') {
if (image) {
return `<img src="${esc(image)}" alt="" class="${sizeClass} ${radiusClass} object-cover shrink-0">`;
const fit = image.endsWith('.svg') ? 'object-contain' : 'object-cover';
return `<img src="${esc(image)}" alt="" class="${sizeClass} ${radiusClass} ${fit} shrink-0">`;
}
return `<div class="${sizeClass} ${radiusClass} flex items-center justify-center shrink-0" style="background:#2f2f2d;"><i class="fas ${icon} text-sm" style="color:#8f8b84;"></i></div>`;
}
@@ -154,30 +155,35 @@ export function getIngredientCardHTML({
overlayStyle = 'pointer-events:none; background:rgba(0,0,0,0.5);',
cardClass = 'relative w-full max-w-xs rounded-2xl shadow-2xl overflow-hidden',
cardStyle = 'background:#2d2e2b; pointer-events:auto; max-height:85vh; overflow-y:auto; transform:translateY(0.75rem); opacity:0; transition:transform 220ms ease, opacity 220ms ease;',
heroHeightClass = 'h-[180px]',
} = {}) {
if (!idBase) throw new Error('getIngredientCardHTML requires idBase');
return `
<div id="${idBase}-overlay" class="${overlayClass}" style="${overlayStyle}">
<div id="${idBase}" class="${cardClass}" style="${cardStyle}">
<div id="${idBase}-hero" class="relative w-full ${heroHeightClass} overflow-hidden" style="background:#393937;">
<img id="${idBase}-img" class="w-full h-full object-cover hidden" alt="" />
<div id="${idBase}-fallback" class="w-full h-full flex items-center justify-center">
<i id="${idBase}-fallback-icon" class="fas fa-box-open text-3xl" style="color:#6d6c67;"></i>
<div class="relative px-4 pt-4 pb-2">
<div class="flex items-start gap-3 pr-10">
<div id="${idBase}-hero" class="relative w-16 h-16 rounded-2xl flex items-center justify-center shrink-0 overflow-hidden" style="background:#393937;">
<img id="${idBase}-img" class="w-full h-full hidden" alt="" />
<div id="${idBase}-fallback" class="absolute inset-0 flex items-center justify-center">
<i id="${idBase}-fallback-icon" class="fas fa-box-open text-2xl" style="color:#6d6c67;"></i>
</div>
</div>
<div class="flex-1 min-w-0 pt-0.5">
<div class="flex items-center gap-1.5">
<button type="button" id="${idBase}-back" class="hidden w-5 h-5 rounded-full items-center justify-center shrink-0" style="background:#393937; color:#ddd6ca;" aria-label="Wróć do składnika">
<i class="fas fa-chevron-left text-[9px]"></i>
</button>
<p id="${idBase}-category" class="text-[10px] font-semibold uppercase tracking-wider truncate" style="color:#6ee7b7;"></p>
</div>
<h3 id="${idBase}-name" class="text-[15px] font-bold leading-snug mt-0.5" style="color:#ddd6ca;"></h3>
<p id="${idBase}-subtitle" class="text-[11px] mt-0.5 hidden" style="color:#9b978f;"></p>
</div>
</div>
<button type="button" id="${idBase}-back" class="absolute top-3 left-3 w-8 h-8 rounded-full hidden flex items-center justify-center" style="background:rgba(0,0,0,0.5); color:#fff;" aria-label="Wróć do składnika">
<i class="fas fa-chevron-left text-sm"></i>
</button>
<button type="button" id="${idBase}-close" class="absolute top-3 right-3 w-8 h-8 rounded-full flex items-center justify-center" style="background:rgba(0,0,0,0.5); color:#fff;" aria-label="Zamknij">
<button type="button" id="${idBase}-close" class="absolute top-3 right-3 w-8 h-8 rounded-full flex items-center justify-center" style="background:#393937; color:#ddd6ca;" aria-label="Zamknij">
<i class="fas fa-times text-sm"></i>
</button>
</div>
<div class="px-4 pt-3 pb-4 space-y-3">
<div>
<p id="${idBase}-category" class="text-[10px] font-semibold uppercase tracking-wider" style="color:#6ee7b7;"></p>
<h3 id="${idBase}-name" class="text-[15px] font-bold leading-snug mt-0.5" style="color:#ddd6ca;"></h3>
<p id="${idBase}-subtitle" class="text-[11px] mt-0.5 hidden" style="color:#9b978f;"></p>
</div>
<div class="px-4 pt-2 pb-4 space-y-3">
<div id="${idBase}-nutrition"></div>
<div id="${idBase}-stock"></div>
<div id="${idBase}-products"></div>
@@ -239,23 +245,22 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
const fallback = el('fallback');
const fallbackIcon = el('fallback-icon');
if (heroEl) {
heroEl.style.height = '';
heroEl.style.background = '#393937';
}
if (img && fallback) {
const image = isListMode ? def.image : (product?.image || def.image);
const altName = isListMode ? def.name : (product?.name || def.name);
if (image) {
img.src = image;
img.alt = altName;
const isSvg = image.endsWith('.svg');
img.classList.toggle('object-contain', isSvg);
img.classList.toggle('object-cover', !isSvg);
img.style.padding = isSvg ? '6px' : '';
img.classList.remove('hidden');
fallback.classList.add('hidden');
} else {
img.classList.add('hidden');
fallback.classList.remove('hidden');
if (fallbackIcon) fallbackIcon.className = `fas ${icon} text-3xl`;
if (fallbackIcon) fallbackIcon.className = `fas ${icon} text-2xl`;
}
}
@@ -286,6 +291,7 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
if (backBtn) {
backBtn.classList.toggle('hidden', !isBackAvailable);
backBtn.classList.toggle('flex', Boolean(isBackAvailable));
}
}

View File

@@ -1,4 +1,4 @@
import { INGREDIENTS, RECIPES, PRODUCTS, getProductsForIngredient } from '../data/catalog.js?v=8';
import { INGREDIENTS, RECIPES, PRODUCTS, getProductsForIngredient } from '../data/catalog.js?v=9';
import { MEAL_SLOTS } from '../planner/mealSlots.js';
import {
addDays,
@@ -24,7 +24,7 @@ import {
renderCalendarGrid,
syncCalendarTodayButton,
} from './mealCalendar.js?v=11';
import { createIngredientCardController, getIngredientCardHTML } from './ingredientCard.js?v=20260417-113';
import { createIngredientCardController, getIngredientCardHTML } from './ingredientCard.js?v=20260417-115';
function esc(s) {
return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');

View File

@@ -1,4 +1,4 @@
import { RECIPES } from '../data/catalog.js?v=8';
import { RECIPES } from '../data/catalog.js?v=9';
import { MEAL_SLOTS } from '../planner/mealSlots.js';
import { applyFilters, getFilterState } from './RecipeList.js';

View File

@@ -1,4 +1,4 @@
import { INGREDIENTS, RECIPES } from '../data/catalog.js?v=8';
import { INGREDIENTS, RECIPES } from '../data/catalog.js?v=9';
import { MEAL_SLOTS } from '../planner/mealSlots.js';
import {
addMonths,

View File

@@ -1,7 +1,7 @@
import {
INGREDIENTS,
CATEGORY_LABELS,
} from '../data/catalog.js?v=8';
} from '../data/catalog.js?v=9';
import { loadPantry, getPantryTotal } from '../services/pantryShopping.js?v=2';
import { loadPlans } from '../services/planStore.js?v=2';
import { addDays, addMonths, sameDay, sameMonth, startOfDay, startOfMonth } from '../services/dateUtils.js';
@@ -13,7 +13,7 @@ import {
renderCalendarGrid,
syncCalendarTodayButton,
} from '../ui/mealCalendar.js?v=11';
import { createIngredientCardController, getIngredientCardHTML } from '../ui/ingredientCard.js?v=20260417-113';
import { createIngredientCardController, getIngredientCardHTML } from '../ui/ingredientCard.js?v=20260417-115';
/* ── helpers ── */

View File

@@ -1,5 +1,5 @@
import { RECIPES, INGREDIENTS, PRODUCTS } from '../data/catalog.js?v=8';
import { createIngredientCardController, getIngredientCardHTML } from '../ui/ingredientCard.js?v=20260417-113';
import { RECIPES, INGREDIENTS, PRODUCTS } from '../data/catalog.js?v=9';
import { createIngredientCardController, getIngredientCardHTML } from '../ui/ingredientCard.js?v=20260417-115';
function escapeHtml(s) {
return String(s)

View File

@@ -1,4 +1,4 @@
import { RECIPES } from '../data/catalog.js?v=8';
import { RECIPES } from '../data/catalog.js?v=9';
import { getRecipeGridSectionHTML, renderRecipeGrid } from '../ui/recipeGrid.js';
const DEFAULT_MIN_MINUTES = 5;

View File

@@ -1,4 +1,4 @@
import { INGREDIENTS, CATEGORY_LABELS } from '../data/catalog.js?v=8';
import { INGREDIENTS, CATEGORY_LABELS } from '../data/catalog.js?v=9';
import {
KITCHEN_LIST_ID,
loadShoppingState,
@@ -98,8 +98,9 @@ function itemRowHtml(item) {
const image = def?.image;
const checked = item.checked;
const mediaFit = image && image.endsWith('.svg') ? 'object-contain' : 'object-cover';
const mediaHtml = image
? `<img src="${esc(image)}" alt="" class="w-8 h-8 rounded-lg object-cover shrink-0">`
? `<img src="${esc(image)}" alt="" class="w-8 h-8 rounded-lg ${mediaFit} shrink-0">`
: `<div class="w-8 h-8 rounded-lg flex items-center justify-center shrink-0" style="background:#2f2f2d;"><i class="fas ${icon} text-xs" style="color:#8f8b84;"></i></div>`;
return `