sozday-sovremennyy-premialny/script.js

372 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;