All checks were successful
Build and Deploy / build-and-push (push) Successful in 23s
179 lines
7.1 KiB
JavaScript
179 lines
7.1 KiB
JavaScript
import { RECIPES } from '../data/catalog.js';
|
|
import { MEAL_SLOTS } from '../planner/mealSlots.js';
|
|
import { applyFilters, getFilterState, getFilteredCount, refreshRecipeList } from './RecipeList.js';
|
|
|
|
function escapeHtml(s) {
|
|
return String(s)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"');
|
|
}
|
|
|
|
function collectAllTags() {
|
|
const tags = new Set();
|
|
for (const r of Object.values(RECIPES)) {
|
|
for (const t of (r.tags || [])) tags.add(t);
|
|
}
|
|
return [...tags].sort((a, b) => a.localeCompare(b, 'pl'));
|
|
}
|
|
|
|
export function getFilterHTML() {
|
|
return `
|
|
<div id="filter-view" class="absolute inset-0 bg-white z-50 hidden flex-col">
|
|
<div class="p-4 border-b border-gray-200 flex items-center justify-between mt-4">
|
|
<button id="filter-close-btn" class="w-10 h-10 flex items-center justify-center text-gray-600 hover:bg-gray-100 rounded-full transition-colors"><i class="fas fa-arrow-left text-lg"></i></button>
|
|
<h2 class="text-lg font-semibold text-black">Filtry</h2>
|
|
<button id="filter-clear-btn" class="px-2 text-sm font-medium text-gray-500 hover:text-black transition-colors">Wyczyść</button>
|
|
</div>
|
|
|
|
<div class="flex-1 overflow-y-auto p-6 space-y-8">
|
|
<div>
|
|
<h3 class="text-base font-semibold text-black mb-4">Pora posiłku</h3>
|
|
<div id="filter-slot-chips" class="flex flex-wrap gap-2.5"></div>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-base font-semibold text-black mb-4">Dieta i tagi</h3>
|
|
<div id="filter-tag-chips" class="flex flex-wrap gap-2.5"></div>
|
|
</div>
|
|
<div>
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-base font-semibold text-black">Maks. czas przygotowania</h3>
|
|
<span id="time-display" class="text-sm font-medium text-gray-600">120 min</span>
|
|
</div>
|
|
<div class="px-1">
|
|
<input type="range" id="prep-time-slider" min="5" max="120" step="5" value="120" class="w-full appearance-none bg-transparent">
|
|
<div class="flex justify-between text-xs text-gray-400 mt-3 font-medium">
|
|
<span>5 min</span><span>30 min</span><span>1 godz.</span><span>2 godz.+</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="p-4 border-t border-gray-200 bg-white mt-auto">
|
|
<button id="filter-apply-btn" class="w-full bg-gray-900 hover:bg-black text-white py-3.5 rounded-xl font-semibold shadow-sm transition-colors text-sm"></button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
let localSlots = [];
|
|
let localTags = [];
|
|
let localMaxMinutes = 120;
|
|
|
|
function renderSlotChips() {
|
|
const wrap = document.getElementById('filter-slot-chips');
|
|
if (!wrap) return;
|
|
|
|
wrap.innerHTML = MEAL_SLOTS.map((slot) => {
|
|
const active = localSlots.includes(slot.id);
|
|
const cls = active
|
|
? 'px-4 py-2 bg-gray-900 text-white rounded-full text-sm font-medium transition-colors'
|
|
: 'px-4 py-2 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-full text-sm font-medium transition-colors';
|
|
return `<button type="button" data-filter-slot="${escapeHtml(slot.id)}" class="${cls}">${escapeHtml(slot.label)}</button>`;
|
|
}).join('');
|
|
|
|
wrap.querySelectorAll('[data-filter-slot]').forEach((btn) => {
|
|
btn.addEventListener('click', () => {
|
|
const id = btn.dataset.filterSlot;
|
|
const idx = localSlots.indexOf(id);
|
|
if (idx >= 0) localSlots.splice(idx, 1);
|
|
else localSlots.push(id);
|
|
renderSlotChips();
|
|
updateResultCount();
|
|
});
|
|
});
|
|
}
|
|
|
|
function renderTagChips() {
|
|
const wrap = document.getElementById('filter-tag-chips');
|
|
if (!wrap) return;
|
|
|
|
const allTags = collectAllTags();
|
|
wrap.innerHTML = allTags.map((tag) => {
|
|
const active = localTags.includes(tag.toLowerCase());
|
|
const cls = active
|
|
? 'px-4 py-2 bg-gray-900 text-white rounded-full text-sm font-medium transition-colors'
|
|
: 'px-4 py-2 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-full text-sm font-medium transition-colors';
|
|
return `<button type="button" data-filter-tag="${escapeHtml(tag)}" class="${cls}">${escapeHtml(tag)}</button>`;
|
|
}).join('');
|
|
|
|
wrap.querySelectorAll('[data-filter-tag]').forEach((btn) => {
|
|
btn.addEventListener('click', () => {
|
|
const tag = btn.dataset.filterTag.toLowerCase();
|
|
const idx = localTags.indexOf(tag);
|
|
if (idx >= 0) localTags.splice(idx, 1);
|
|
else localTags.push(tag);
|
|
renderTagChips();
|
|
updateResultCount();
|
|
});
|
|
});
|
|
}
|
|
|
|
function updateResultCount() {
|
|
applyFilters({ slots: localSlots, tags: localTags, maxMinutes: localMaxMinutes });
|
|
const count = getFilteredCount();
|
|
const applyBtn = document.getElementById('filter-apply-btn');
|
|
if (applyBtn) applyBtn.textContent = `Pokaż ${count} wyników`;
|
|
}
|
|
|
|
export function setupFilter() {
|
|
const timeSlider = document.getElementById('prep-time-slider');
|
|
const timeDisplay = document.getElementById('time-display');
|
|
|
|
if (timeSlider) {
|
|
timeSlider.addEventListener('input', (e) => {
|
|
const val = Number(e.target.value);
|
|
localMaxMinutes = val;
|
|
timeDisplay.textContent = val >= 120 ? 'ponad 120 min' : `${val} min`;
|
|
updateResultCount();
|
|
});
|
|
}
|
|
|
|
document.getElementById('filter-close-btn')?.addEventListener('click', () => {
|
|
window.closeFilters();
|
|
});
|
|
|
|
document.getElementById('filter-apply-btn')?.addEventListener('click', () => {
|
|
applyFilters({ slots: localSlots, tags: localTags, maxMinutes: localMaxMinutes });
|
|
refreshRecipeList();
|
|
window.closeFilters();
|
|
});
|
|
|
|
document.getElementById('filter-clear-btn')?.addEventListener('click', () => {
|
|
localSlots = [];
|
|
localTags = [];
|
|
localMaxMinutes = 120;
|
|
if (timeSlider) timeSlider.value = '120';
|
|
if (timeDisplay) timeDisplay.textContent = '120 min';
|
|
renderSlotChips();
|
|
renderTagChips();
|
|
applyFilters({ slots: [], tags: [], maxMinutes: 120 });
|
|
updateResultCount();
|
|
});
|
|
|
|
window.openFilters = () => {
|
|
const state = getFilterState();
|
|
localSlots = [...state.slots];
|
|
localTags = [...state.tags];
|
|
localMaxMinutes = state.maxMinutes;
|
|
|
|
if (timeSlider) timeSlider.value = String(localMaxMinutes);
|
|
if (timeDisplay) timeDisplay.textContent = localMaxMinutes >= 120 ? 'ponad 120 min' : `${localMaxMinutes} min`;
|
|
|
|
renderSlotChips();
|
|
renderTagChips();
|
|
updateResultCount();
|
|
|
|
const fv = document.getElementById('filter-view');
|
|
fv.classList.remove('hidden');
|
|
fv.classList.add('flex');
|
|
};
|
|
|
|
window.closeFilters = () => {
|
|
const fv = document.getElementById('filter-view');
|
|
fv.classList.add('hidden');
|
|
fv.classList.remove('flex');
|
|
};
|
|
}
|