Files
recipe-mockup/js/views/Filter.js
ulfrxdev f80b115cae
All checks were successful
Build and Deploy / build-and-push (push) Successful in 23s
Reorganise the views and prepare summary
2026-03-26 22:29:06 +01:00

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, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
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');
};
}