const baseProducts = [
{ title: 'Красный клоунский нос', category: 'Носы', price: 490, rating: 4.9, tag: 'clown,nose' },
{ title: 'Премиум парик Радуга', category: 'Парики', price: 2190, rating: 4.7, tag: 'clown,wig' },
{ title: 'Гигантские башмаки Jumbo', category: 'Обувь', price: 3890, rating: 4.8, tag: 'clown,shoes' },
{ title: 'Галстук-бабочка XL', category: 'Галстуки', price: 890, rating: 4.6, tag: 'bow,tie,colorful' },
{ title: 'Набор для жонглирования PRO', category: 'Жонглирование', price: 2690, rating: 4.9, tag: 'juggling,clubs' },
{ title: 'Цветок-брызгалка', category: 'Реквизит', price: 990, rating: 4.5, tag: 'clown,prop' },
{ title: 'Набор грима FunnyFace', category: 'Грим', price: 1490, rating: 4.8, tag: 'face,paint,festival' },
{ title: 'Подтяжки Circus Style', category: 'Одежда', price: 1290, rating: 4.4, tag: 'circus,costume' },
{ title: 'Мини-шляпка Весёлый купол', category: 'Головные уборы', price: 1590, rating: 4.6, tag: 'clown,hat' },
{ title: 'Набор накладных ресниц Stage', category: 'Грим', price: 690, rating: 4.3, tag: 'theater,makeup' },
{ title: 'Мега-помпоны для костюма', category: 'Одежда', price: 790, rating: 4.5, tag: 'colorful,pom,pom' },
{ title: 'Кейс реквизита артиста', category: 'Реквизит', price: 4590, rating: 4.9, tag: 'circus,backstage' }
];
function buildCatalog(minCount = 120) {
const products = [];
for (let i = 0; i < minCount; i += 1) {
const base = baseProducts[i % baseProducts.length];
const variant = Math.floor(i / baseProducts.length) + 1;
const lock = 1000 + i;
products.push({
id: i + 1,
title: `${base.title} #${variant}`,
category: base.category,
price: base.price + (variant * 35),
rating: Number(Math.max(4.0, base.rating - ((variant - 1) * 0.05)).toFixed(1)),
image: `https://loremflickr.com/800/600/${base.tag}?lock=${lock}`
});
}
return products;
}
const products = buildCatalog(120);
const state = {
cart: JSON.parse(localStorage.getItem('cart') || '{}'),
favorites: JSON.parse(localStorage.getItem('favorites') || '[]'),
search: '',
category: 'all',
sort: 'popular'
};
const el = {
catalog: document.getElementById('catalog'),
searchInput: document.getElementById('searchInput'),
categoryFilter: document.getElementById('categoryFilter'),
sortSelect: document.getElementById('sortSelect'),
cartCount: document.getElementById('cartCount'),
favoritesCount: document.getElementById('favoritesCount'),
cartItems: document.getElementById('cartItems'),
favoritesItems: document.getElementById('favoritesItems'),
cartTotal: document.getElementById('cartTotal'),
cartDrawer: document.getElementById('cartDrawer'),
favoritesDrawer: document.getElementById('favoritesDrawer'),
checkoutModal: document.getElementById('checkoutModal'),
checkoutForm: document.getElementById('checkoutForm'),
toast: document.getElementById('toast')
};
function save() {
localStorage.setItem('cart', JSON.stringify(state.cart));
localStorage.setItem('favorites', JSON.stringify(state.favorites));
}
function toast(message) {
el.toast.textContent = message;
el.toast.classList.remove('hidden');
setTimeout(() => el.toast.classList.add('hidden'), 3200);
}
function money(value) {
return `${value.toLocaleString('ru-RU')} ₽`;
}
function filteredProducts() {
let list = [...products];
if (state.search) list = list.filter((p) => p.title.toLowerCase().includes(state.search.toLowerCase()));
if (state.category !== 'all') list = list.filter((p) => p.category === state.category);
if (state.sort === 'price_asc') list.sort((a, b) => a.price - b.price);
if (state.sort === 'price_desc') list.sort((a, b) => b.price - a.price);
if (state.sort === 'title') list.sort((a, b) => a.title.localeCompare(b.title, 'ru'));
if (state.sort === 'popular') list.sort((a, b) => b.rating - a.rating);
return list;
}
function renderCatalog() {
const list = filteredProducts();
el.catalog.innerHTML = list.map((item) => `
Корзина пуста.
'; el.cartTotal.textContent = money(0); return; } let total = 0; el.cartItems.innerHTML = list.map((item) => { const qty = state.cart[item.id]; total += qty * item.price; return `Пока ничего не добавлено.
'; return; } el.favoritesItems.innerHTML = list.map((item) => `