372 lines
18 KiB
JavaScript
372 lines
18 KiB
JavaScript
const PRODUCTS = [
|
||
{id: 1, title: "Ылғалдандырғыш бет кремі", category: "face", price: 18900, image: "https://picsum.photos/seed/ffup1/600/600", badge: "Хит", description: "Барлық тері түрлеріне арналған қарқынды ылғалдандыру. Гиалурон қышқылы мен табиғи майлар.", specs: {"Көлемі": "50 мл", "Тері түрі": "Барлығы", "Әсері": "Ылғалдандыру"}},
|
||
{id: 2, title: "С C дәрумені сарысуы", category: "face", price: 24500, image: "https://picsum.photos/seed/ffup2/600/600", badge: "Жаңа", description: "Пигментацияны ағартады, тері түсін тегістейді, коллаген өндірісін ынталандырады.", specs: {"Көлемі": "30 мл", "Тері түрі": "Барлығы", "Әсері": "Ағарту"}},
|
||
{id: 3, title: "Тазартушы гель", category: "care", price: 12900, image: "https://picsum.photos/seed/ffup3/600/600", description: "Қорғаныс кедергісін бұзбай нәзік тазартады. Күнделікті қолдануға жарамды.", specs: {"Көлемі": "200 мл", "Тері түрі": "Майлы, аралас", "Әсері": "Тазарту"}},
|
||
{id: 4, title: "Түнгі қалпына келтіруші крем", category: "face", price: 21900, image: "https://picsum.photos/seed/ffup4/600/600", description: "Ұйқы кезінде жұмыс істейді, теріні қалпына келтіреді және қартаю белгілерін азайтады.", specs: {"Көлемі": "50 мл", "Тері түрі": "Құрғақ, қалыпты", "Әсері": "Қалпына келтіру"}},
|
||
{id: 5, title: "Дене сүті", category: "body", price: 16500, image: "https://picsum.photos/seed/ffup5/600/600", description: "Бүкіл денеге нәзік ылғалдандыру. Жабыспай тез сіңеді.", specs: {"Көлемі": "250 мл", "Тері түрі": "Барлығы", "Әсері": "Ылғалдандыру"}},
|
||
{id: 6, title: "Дене скрабы", category: "body", price: 14900, image: "https://picsum.photos/seed/ffup6/600/600", description: "Нәзік эксфолиация, теріні жұмсақ әрі тегіс етеді. Тропиктік жеміс хош иісі.", specs: {"Көлемі": "200 мл", "Тері түрі": "Барлығы", "Әсері": "Эксфолиация"}},
|
||
{id: 7, title: "Бет тонигі", category: "care", price: 11900, image: "https://picsum.photos/seed/ffup7/600/600", description: "Тазартудан кейін терінің pH-ын теңестіреді, крем жағуға дайындайды.", specs: {"Көлемі": "200 мл", "Тері түрі": "Барлығы", "Әсері": "Тонус"}},
|
||
{id: 8, title: "Қол кремі", category: "body", price: 8900, image: "https://picsum.photos/seed/ffup8/600/600", description: "Құрғақ қол терісіне арналған қарқынды қорек. Тез сіңеді.", specs: {"Көлемі": "75 мл", "Тері түрі": "Құрғақ", "Әсері": "Қорек"}},
|
||
{id: 9, title: "'Базалық күтім' жиынтығы", category: "sets", price: 45900, image: "https://picsum.photos/seed/ffup9/600/600", badge: "15% үнемдеу", description: "Жиынтықта: тазартушы гель, тоник, ылғалдандырғыш крем. Күнделікті күтімге қажеттінің бәрі.", specs: {"Құрамы": "3 өнім", "Тері түрі": "Барлығы", "Үнемдеу": "15%"}},
|
||
{id: 10, title: "Бет маскасы", category: "care", price: 9900, image: "https://picsum.photos/seed/ffup10/600/600", description: "Қарқынды қорек және ылғалдандыру. Аптасына 2-3 рет 15 минут жағу.", specs: {"Көлемі": "75 мл", "Тері түрі": "Барлығы", "Әсері": "Қорек"}},
|
||
{id: 11, title: "Көз айналасына крем", category: "face", price: 19900, image: "https://picsum.photos/seed/ffup11/600/600", description: "Қара шеңберлер мен ісінуді азайтады. Нәзік аймаққа арналған формула.", specs: {"Көлемі": "15 мл", "Тері түрі": "Барлығы", "Әсері": "Жасарту"}},
|
||
{id: 12, title: "'Тері жылтыры' жиынтығы", category: "sets", price: 52900, image: "https://picsum.photos/seed/ffup12/600/600", badge: "Премиум", description: "Жиынтықта: С дәрумені сарысуы, түнгі крем, маска. Жылтыр реңк үшін.", specs: {"Құрамы": "3 өнім", "Тері түрі": "Барлығы", "Әсері": "Жылтыр"}}
|
||
];
|
||
|
||
const REVIEWS = [
|
||
{name: "Алия К.", avatar: "https://picsum.photos/seed/r1/100/100", rating: 5, text: "Ылғалдандырғыш кремді тапсырыс бердім — бірінші қолданудан ғашықпын! Тері жұмсақ, ылғалды болды. Тағы тапсырыс беремін."},
|
||
{name: "Мария В.", avatar: "https://picsum.photos/seed/r2/100/100", rating: 5, text: "Formula For You өнімдерін жарты жыл қолданамын. Сапасы жоғары, терім өзгерді. Жеткізу жылдам, қаптама әдемі."},
|
||
{name: "Елена С.", avatar: "https://picsum.photos/seed/r3/100/100", rating: 5, text: "С дәрумені сарысуы ғажайып! Пигментация айтарлықтай ағарды. Барлық құрбыларға ұсынамын."},
|
||
{name: "Динара Т.", avatar: "https://picsum.photos/seed/r4/100/100", rating: 5, text: "'Базалық күтім' жиынтығын алдым — бәрін бірден сынап көруге тамаша мүмкіндік. Бәрі сәйкес келді, тері риза!"}
|
||
];
|
||
|
||
let cart = JSON.parse(localStorage.getItem('ffu_cart')) || [];
|
||
let currentFilter = 'all';
|
||
let currentSlide = 0;
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
initHeader();
|
||
renderCatalog();
|
||
renderReviews();
|
||
initFilters();
|
||
initModals();
|
||
initFAQ();
|
||
initScrollAnimations();
|
||
updateCartCount();
|
||
initPhoneValidation();
|
||
});
|
||
|
||
function initHeader() {
|
||
const header = document.getElementById('header');
|
||
const burgerBtn = document.getElementById('burgerBtn');
|
||
const nav = document.getElementById('nav');
|
||
window.addEventListener('scroll', () => {
|
||
if (window.scrollY > 50) header.classList.add('scrolled');
|
||
else header.classList.remove('scrolled');
|
||
});
|
||
burgerBtn.addEventListener('click', () => {
|
||
burgerBtn.classList.toggle('active');
|
||
nav.classList.toggle('active');
|
||
});
|
||
document.querySelectorAll('.nav__link').forEach(link => {
|
||
link.addEventListener('click', () => {
|
||
burgerBtn.classList.remove('active');
|
||
nav.classList.remove('active');
|
||
});
|
||
});
|
||
document.getElementById('headerOrderBtn').addEventListener('click', () => {
|
||
document.getElementById('catalog').scrollIntoView({ behavior: 'smooth' });
|
||
});
|
||
}
|
||
|
||
function renderCatalog(filter = 'all') {
|
||
const grid = document.getElementById('catalogGrid');
|
||
const filtered = filter === 'all' ? PRODUCTS : PRODUCTS.filter(p => p.category === filter);
|
||
grid.innerHTML = filtered.map(product => `
|
||
<div class="product-card" data-id="${product.id}">
|
||
<div class="product-card__image">
|
||
<img src="${product.image}" alt="${product.title}" loading="lazy">
|
||
${product.badge ? `<span class="product-card__badge">${product.badge}</span>` : ''}
|
||
</div>
|
||
<div class="product-card__content">
|
||
<span class="product-card__category">${getCategoryName(product.category)}</span>
|
||
<h3 class="product-card__title">${product.title}</h3>
|
||
<p class="product-card__desc">${product.description}</p>
|
||
<div class="product-card__footer">
|
||
<span class="product-card__price">${formatPrice(product.price)}</span>
|
||
<div class="product-card__actions">
|
||
<button class="btn btn--secondary btn--small" onclick="openProductModal(${product.id})">Толығырақ</button>
|
||
<button class="btn btn--primary btn--small" onclick="addToCart(${product.id})">Тапсырыс</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function getCategoryName(cat) {
|
||
const names = {face: 'Бет', body: 'Дене', care: 'Күтім', sets: 'Жиынтықтар'};
|
||
return names[cat] || cat;
|
||
}
|
||
|
||
function formatPrice(price) {
|
||
return price.toLocaleString('ru-RU') + ' ₸';
|
||
}
|
||
|
||
function initFilters() {
|
||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
currentFilter = btn.dataset.filter;
|
||
renderCatalog(currentFilter);
|
||
});
|
||
});
|
||
}
|
||
|
||
function openProductModal(id) {
|
||
const product = PRODUCTS.find(p => p.id === id);
|
||
if (!product) return;
|
||
const modalBody = document.getElementById('modalBody');
|
||
modalBody.innerHTML = `
|
||
<div class="product-modal">
|
||
<div class="product-modal__image"><img src="${product.image}" alt="${product.title}"></div>
|
||
<div class="product-modal__info">
|
||
<h3>${product.title}</h3>
|
||
<p class="product-modal__price">${formatPrice(product.price)}</p>
|
||
<p class="product-modal__desc">${product.description}</p>
|
||
<div class="product-modal__specs">
|
||
<h4 class="product-modal__specs-title">Сипаттамасы</h4>
|
||
<ul class="product-modal__specs-list">
|
||
${Object.entries(product.specs).map(([key, value]) => `<li><span>${key}</span><span>${value}</span></li>`).join('')}
|
||
</ul>
|
||
</div>
|
||
<div class="product-modal__quantity">
|
||
<button class="quantity-btn" onclick="changeQuantity(-1)">−</button>
|
||
<span class="quantity-value" id="modalQuantity">1</span>
|
||
<button class="quantity-btn" onclick="changeQuantity(1)">+</button>
|
||
</div>
|
||
<button class="btn btn--primary btn--large btn--full" onclick="addToCartFromModal(${product.id})">Себетке қосу</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
openModal('productModal');
|
||
}
|
||
|
||
function changeQuantity(delta) {
|
||
const el = document.getElementById('modalQuantity');
|
||
let val = parseInt(el.textContent) + delta;
|
||
if (val < 1) val = 1;
|
||
el.textContent = val;
|
||
}
|
||
|
||
function addToCartFromModal(id) {
|
||
const qty = parseInt(document.getElementById('modalQuantity').textContent);
|
||
addToCart(id, qty);
|
||
closeModal('productModal');
|
||
}
|
||
|
||
function addToCart(id, qty = 1) {
|
||
const existing = cart.find(item => item.id === id);
|
||
if (existing) existing.quantity += qty;
|
||
else {
|
||
const product = PRODUCTS.find(p => p.id === id);
|
||
cart.push({...product, quantity: qty});
|
||
}
|
||
saveCart();
|
||
updateCartCount();
|
||
showNotification('Тауар себетке қосылды', 'success');
|
||
}
|
||
|
||
function saveCart() {
|
||
localStorage.setItem('ffu_cart', JSON.stringify(cart));
|
||
}
|
||
|
||
function updateCartCount() {
|
||
const count = cart.reduce((sum, item) => sum + item.quantity, 0);
|
||
document.getElementById('cartCount').textContent = count;
|
||
}
|
||
|
||
function initModals() {
|
||
document.getElementById('cartBtn').addEventListener('click', openCart);
|
||
document.getElementById('modalOverlay').addEventListener('click', () => closeModal('productModal'));
|
||
document.getElementById('modalClose').addEventListener('click', () => closeModal('productModal'));
|
||
document.getElementById('cartOverlay').addEventListener('click', () => closeModal('cartModal'));
|
||
document.getElementById('cartModalClose').addEventListener('click', () => closeModal('cartModal'));
|
||
document.getElementById('checkoutOverlay').addEventListener('click', () => closeModal('checkoutModal'));
|
||
document.getElementById('checkoutModalClose').addEventListener('click', () => closeModal('checkoutModal'));
|
||
document.getElementById('successClose').addEventListener('click', () => {
|
||
closeModal('successModal');
|
||
cart = [];
|
||
saveCart();
|
||
updateCartCount();
|
||
renderCart();
|
||
});
|
||
document.getElementById('checkoutBtn').addEventListener('click', openCheckout);
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Escape') document.querySelectorAll('.modal.active').forEach(modal => modal.classList.remove('active'));
|
||
});
|
||
}
|
||
|
||
function openModal(id) {
|
||
document.getElementById(id).classList.add('active');
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
function closeModal(id) {
|
||
document.getElementById(id).classList.remove('active');
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
function openCart() {
|
||
renderCart();
|
||
openModal('cartModal');
|
||
}
|
||
|
||
function renderCart() {
|
||
const empty = document.getElementById('cartEmpty');
|
||
const content = document.getElementById('cartContent');
|
||
const itemsContainer = document.getElementById('cartItems');
|
||
if (cart.length === 0) {
|
||
empty.style.display = 'block';
|
||
content.style.display = 'none';
|
||
return;
|
||
}
|
||
empty.style.display = 'none';
|
||
content.style.display = 'block';
|
||
itemsContainer.innerHTML = cart.map(item => `
|
||
<div class="cart-item">
|
||
<div class="cart-item__image"><img src="${item.image}" alt="${item.title}"></div>
|
||
<div class="cart-item__content">
|
||
<h4 class="cart-item__title">${item.title}</h4>
|
||
<p class="cart-item__price">${formatPrice(item.price)} × ${item.quantity}</p>
|
||
<div class="cart-item__actions">
|
||
<div style="display:flex;align-items:center;gap:10px;">
|
||
<button class="quantity-btn" style="width:32px;height:32px;font-size:16px;" onclick="updateCartQuantity(${item.id}, -1)">−</button>
|
||
<span style="font-weight:600;min-width:30px;text-align:center;">${item.quantity}</span>
|
||
<button class="quantity-btn" style="width:32px;height:32px;font-size:16px;" onclick="updateCartQuantity(${item.id}, 1)">+</button>
|
||
</div>
|
||
<button class="cart-item__remove" onclick="removeFromCart(${item.id})">Жою</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
const subtotal = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
||
document.getElementById('cartTotalCount').textContent = cart.reduce((sum, item) => sum + item.quantity, 0);
|
||
document.getElementById('cartSubtotal').textContent = formatPrice(subtotal);
|
||
document.getElementById('cartTotal').textContent = formatPrice(subtotal);
|
||
}
|
||
|
||
function updateCartQuantity(id, delta) {
|
||
const item = cart.find(i => i.id === id);
|
||
if (!item) return;
|
||
item.quantity += delta;
|
||
if (item.quantity <= 0) { removeFromCart(id); return; }
|
||
saveCart();
|
||
updateCartCount();
|
||
renderCart();
|
||
}
|
||
|
||
function removeFromCart(id) {
|
||
cart = cart.filter(item => item.id !== id);
|
||
saveCart();
|
||
updateCartCount();
|
||
renderCart();
|
||
showNotification('Тауар себеттен жойылды', 'success');
|
||
}
|
||
|
||
function openCheckout() {
|
||
const subtotal = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
||
document.getElementById('checkoutTotal').textContent = formatPrice(subtotal);
|
||
closeModal('cartModal');
|
||
openModal('checkoutModal');
|
||
}
|
||
|
||
function initPhoneValidation() {
|
||
const phoneInput = document.getElementById('checkoutPhone');
|
||
phoneInput.addEventListener('input', (e) => {
|
||
let value = e.target.value.replace(/\D/g, '');
|
||
if (value.length > 11) value = value.slice(0, 11);
|
||
if (value.length > 0 && (value[0] === '7' || value[0] === '8')) value = value.slice(1);
|
||
let formatted = '+7';
|
||
if (value.length > 0) formatted += ' (' + value.slice(0, 3);
|
||
if (value.length >= 3) formatted += ') ' + value.slice(3, 6);
|
||
if (value.length >= 6) formatted += '-' + value.slice(6, 8);
|
||
if (value.length >= 8) formatted += '-' + value.slice(8, 10);
|
||
e.target.value = formatted;
|
||
});
|
||
}
|
||
|
||
document.getElementById('checkoutForm').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
const formData = {
|
||
name: document.getElementById('checkoutName').value,
|
||
phone: document.getElementById('checkoutPhone').value,
|
||
city: document.getElementById('checkoutCity').value,
|
||
address: document.getElementById('checkoutAddress').value,
|
||
comment: document.getElementById('checkoutComment').value,
|
||
items: [...cart],
|
||
total: cart.reduce((sum, item) => sum + item.price * item.quantity, 0)
|
||
};
|
||
const message = `Сәлем! Тапсырыс: ${formData.name}\nТелефон: ${formData.phone}\nҚала: ${formData.city}\nМекенжай: ${formData.address}\nСомма: ${formatPrice(formData.total)}`;
|
||
window.open('https://wa.me/77771234567?text=' + encodeURIComponent(message));
|
||
closeModal('checkoutModal');
|
||
openModal('successModal');
|
||
document.getElementById('checkoutForm').reset();
|
||
});
|
||
|
||
function initFAQ() {
|
||
document.querySelectorAll('.faq-item__header').forEach(header => {
|
||
header.addEventListener('click', () => {
|
||
const expanded = header.getAttribute('aria-expanded') === 'true';
|
||
document.querySelectorAll('.faq-item__header').forEach(h => h.setAttribute('aria-expanded', 'false'));
|
||
document.querySelectorAll('.faq-item__content').forEach(content => content.style.maxHeight = null);
|
||
if (!expanded) {
|
||
header.setAttribute('aria-expanded', 'true');
|
||
const content = header.nextElementSibling;
|
||
content.style.maxHeight = content.scrollHeight + 'px';
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
function initScrollAnimations() {
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => { if (entry.isIntersecting) entry.target.classList.add('in'); });
|
||
}, { threshold: 0.1 });
|
||
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
|
||
}
|
||
|
||
function renderReviews() {
|
||
const track = document.getElementById('reviewsTrack');
|
||
track.innerHTML = REVIEWS.map(review => `
|
||
<div class="review-card">
|
||
<div class="review-card__header">
|
||
<img src="${review.avatar}" alt="${review.name}" class="review-card__avatar">
|
||
<div class="review-card__info">
|
||
<h4>${review.name}</h4>
|
||
<div class="review-card__stars">${'★'.repeat(review.rating)}${'☆'.repeat(5 - review.rating)}</div>
|
||
</div>
|
||
</div>
|
||
<p class="review-card__text">"${review.text}"</p>
|
||
</div>
|
||
`).join('');
|
||
updateReviewsSlider();
|
||
document.getElementById('reviewsPrev').addEventListener('click', () => {
|
||
currentSlide = Math.max(0, currentSlide - 1);
|
||
updateReviewsSlider();
|
||
});
|
||
document.getElementById('reviewsNext').addEventListener('click', () => {
|
||
const maxSlide = Math.max(0, Math.ceil(REVIEWS.length / getVisibleReviews()) - 1);
|
||
currentSlide = Math.min(maxSlide, currentSlide + 1);
|
||
updateReviewsSlider();
|
||
});
|
||
}
|
||
|
||
function getVisibleReviews() {
|
||
if (window.innerWidth <= 768) return 1;
|
||
if (window.innerWidth <= 1024) return 2;
|
||
return 3;
|
||
}
|
||
|
||
function updateReviewsSlider() {
|
||
const track = document.getElementById('reviewsTrack');
|
||
const cardWidth = track.querySelector('.review-card')?.offsetWidth || 0;
|
||
const gap = 30;
|
||
track.style.transform = `translateX(-${currentSlide * (cardWidth + gap)}px)`;
|
||
}
|
||
|
||
function showNotification(message, type = 'success') {
|
||
const notification = document.getElementById('notification');
|
||
notification.textContent = message;
|
||
notification.className = 'notification show ' + type;
|
||
setTimeout(() => notification.classList.remove('show'), 3000);
|
||
}
|
||
|
||
window.openProductModal = openProductModal;
|
||
window.changeQuantity = changeQuantity;
|
||
window.addToCartFromModal = addToCartFromModal;
|
||
window.addToCart = addToCart;
|
||
window.updateCartQuantity = updateCartQuantity;
|
||
window.removeFromCart = removeFromCart;
|