The catalog
Choose your next wearable project
Filter by pace, level, and format to find a focused way into painted textiles.
`;
const footerHTML = ``;
document.querySelector('header').innerHTML = headerHTML;
document.querySelector('footer').innerHTML = footerHTML;
window.initSharedUI();
let products = [], filtered = [], page = 1; const perPage = 6;
const fallowsKey = 'backloom-fallows', cartKey = 'backloom-cart';
function updateCart(id) {
let cart = JSON.parse(localStorage.getItem(cartKey) || '[]');
const idx = cart.findIndex(i => i.id === id);
if (idx > -1) cart[idx].quantity++;
else cart.push({id, quantity: 1});
localStorage.setItem(cartKey, JSON.stringify(cart));
alert('Added to cart');
}
function updateFallows(id) {
let f = JSON.parse(localStorage.getItem(fallowsKey) || '[]');
if (!f.includes(id)) f.push(id);
localStorage.setItem(fallowsKey, JSON.stringify(f));
alert('Added to Fallows');
}
function showModal(product) {
const modal = document.querySelector('#product-modal');
const content = document.querySelector('#modal-content');
content.innerHTML = `${product.title}
${product.category} · ${product.level} · ${product.format} · ${product.duration}
${product.description}
Materials: ${product.materials.join(', ')}
Palette: ${product.palette}
$${product.price.toFixed(2)} ${product.currency}
`;
modal.showModal();
modal.querySelectorAll('a').forEach(a => a.addEventListener('click', () => modal.close(), {once:true}));
}
function render() {
const q = document.querySelector('#search').value.toLowerCase();
const c = document.querySelector('#category').value;
const l = document.querySelector('#level').value;
filtered = products.filter(x => (!q || (x.title + x.description + x.category).toLowerCase().includes(q)) && (!c || x.category === c) && (!l || x.level === l));
const start = (page - 1) * perPage;
const items = filtered.slice(start, start + perPage);
document.querySelector('#product-grid').innerHTML = items.map(x => `
${x.category} · ${x.level}
${x.title}
${x.description}
$${x.price.toFixed(2)}
`).join('');
document.querySelector('#pagination').innerHTML = Array.from({length: Math.ceil(filtered.length / perPage)}, (_, i) => `${i+1}`).join('');
document.querySelectorAll('[data-cart]').forEach(el => el.addEventListener('click', e => { e.preventDefault(); updateCart(el.dataset.cart); }));
document.querySelectorAll('[data-fallow]').forEach(el => el.addEventListener('click', e => { e.preventDefault(); updateFallows(el.dataset.fallow); }));
document.querySelectorAll('[data-detail]').forEach(el => el.addEventListener('click', e => { e.preventDefault(); const p = products.find(pr => pr.id === el.dataset.detail); showModal(p); }));
document.querySelectorAll('[data-page]').forEach(el => el.addEventListener('click', e => { e.preventDefault(); page = +el.dataset.page; render(); }));
}
fetch('./catalog.json').then(r => r.json()).then(d => {
products = d;
['category','level'].forEach(k => {
[...new Set(d.map(x => x[k]))].forEach(v => document.querySelector('#'+k).insertAdjacentHTML('beforeend', ``));
});
render();
});
document.addEventListener('input', e => {
if (e.target.matches('#search,#category,#level')) { page = 1; render(); }
});