333 lines
10 KiB
JavaScript
333 lines
10 KiB
JavaScript
// Kazakhtelecom Website — JavaScript
|
||
|
||
const API = '/api/data';
|
||
let siteData = null;
|
||
let currentTariffCategory = 'internet';
|
||
|
||
// Initialize
|
||
window.addEventListener('DOMContentLoaded', async () => {
|
||
await loadData();
|
||
renderHero();
|
||
renderServices();
|
||
renderTariffs('internet');
|
||
renderBundles();
|
||
renderNews();
|
||
renderFAQ();
|
||
setupAnimations();
|
||
setupTariffTabs();
|
||
setupContactForm();
|
||
});
|
||
|
||
// Load data from JSON
|
||
async function loadData() {
|
||
try {
|
||
const res = await fetch(API);
|
||
const json = await res.json();
|
||
if (json.ok) {
|
||
siteData = json.data;
|
||
}
|
||
} catch (e) {
|
||
console.error('Failed to load data:', e);
|
||
}
|
||
}
|
||
|
||
// Render Hero
|
||
function renderHero() {
|
||
const { heroTitle, heroSubtitle, heroDescription } = siteData.siteSettings;
|
||
document.getElementById('heroTitle').textContent = heroTitle;
|
||
document.getElementById('heroSubtitle').textContent = heroSubtitle;
|
||
document.getElementById('heroDescription').textContent = heroDescription;
|
||
}
|
||
|
||
// Render Services
|
||
function renderServices() {
|
||
const grid = document.getElementById('servicesGrid');
|
||
grid.innerHTML = siteData.services.map(service => `
|
||
<div class="service-card" style="--service-color: ${service.color}">
|
||
<span class="service-icon">${service.icon}</span>
|
||
<h3>${service.name}</h3>
|
||
<p>${service.description}</p>
|
||
<div class="service-features">
|
||
${service.features.map(f => `<div class="service-feature">${f}</div>`).join('')}
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// Render Tariffs
|
||
function renderTariffs(category) {
|
||
currentTariffCategory = category;
|
||
const grid = document.getElementById('tariffsGrid');
|
||
const tariffs = siteData.tariffs[category] || [];
|
||
|
||
if (tariffs.length === 0) {
|
||
grid.innerHTML = '<p style="text-align:center;color:var(--text-secondary);grid-column:1/-1">Тарифы загружаются...</p>';
|
||
return;
|
||
}
|
||
|
||
grid.innerHTML = tariffs.map(tariff => {
|
||
const speedOrChannels = tariff.speed || tariff.channels;
|
||
const priceLabel = category === 'smart' ? '/мес' : '';
|
||
|
||
return `
|
||
<div class="tariff-card ${tariff.popular ? 'popular' : ''}">
|
||
<h3 class="tariff-name">${tariff.name}</h3>
|
||
<div class="tariff-speed">${speedOrChannels}</div>
|
||
<div class="tariff-price">${tariff.price.toLocaleString()} ${tariff.currency}${priceLabel}</div>
|
||
<div class="tariff-features">
|
||
${tariff.features.map(f => `<div class="tariff-feature">${f}</div>`).join('')}
|
||
</div>
|
||
<button class="btn-tariff" onclick="selectTariff('${tariff.id}')">Подключить</button>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
}
|
||
|
||
// Setup Tariff Tabs
|
||
function setupTariffTabs() {
|
||
const tabs = document.querySelectorAll('.tab');
|
||
tabs.forEach(tab => {
|
||
tab.addEventListener('click', () => {
|
||
tabs.forEach(t => t.classList.remove('active'));
|
||
tab.classList.add('active');
|
||
renderTariffs(tab.dataset.category);
|
||
});
|
||
});
|
||
}
|
||
|
||
// Render Bundles
|
||
function renderBundles() {
|
||
const grid = document.getElementById('bundlesGrid');
|
||
grid.innerHTML = siteData.bundles.map(bundle => `
|
||
<div class="bundle-card ${bundle.popular ? 'popular' : ''}">
|
||
<h3 class="bundle-name">${bundle.name}</h3>
|
||
<p class="bundle-description">${bundle.description}</p>
|
||
<div class="bundle-pricing">
|
||
<div class="bundle-original">${bundle.originalPrice.toLocaleString()} ₸</div>
|
||
<div class="bundle-price">
|
||
${bundle.bundlePrice.toLocaleString()} ₸
|
||
<span class="bundle-discount">-${bundle.discount}%</span>
|
||
</div>
|
||
</div>
|
||
<div class="bundle-includes">
|
||
${bundle.includes.map(item => `<div class="bundle-item">${item}</div>`).join('')}
|
||
</div>
|
||
<button class="btn-tariff" onclick="selectBundle('${bundle.id}')">Выбрать пакет</button>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// Render News
|
||
function renderNews() {
|
||
const grid = document.getElementById('newsGrid');
|
||
grid.innerHTML = siteData.news.map(news => `
|
||
<div class="news-card">
|
||
<img src="${news.image}" alt="${news.title}" class="news-image">
|
||
<div class="news-content">
|
||
<div class="news-meta">
|
||
<span class="news-category">${news.category}</span>
|
||
<span>${formatDate(news.date)}</span>
|
||
</div>
|
||
<h3>${news.title}</h3>
|
||
<p>${news.excerpt}</p>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// Render FAQ
|
||
function renderFAQ() {
|
||
const container = document.getElementById('faqContainer');
|
||
container.innerHTML = siteData.faq.map((item, index) => `
|
||
<div class="faq-item">
|
||
<button class="faq-question" onclick="toggleFaq(${index})">
|
||
${item.question}
|
||
<span class="faq-icon">+</span>
|
||
</button>
|
||
<div class="faq-answer">
|
||
<p>${item.answer}</p>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// Toggle FAQ
|
||
function toggleFaq(index) {
|
||
const items = document.querySelectorAll('.faq-item');
|
||
items.forEach((item, i) => {
|
||
if (i === index) {
|
||
item.classList.toggle('active');
|
||
} else {
|
||
item.classList.remove('active');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Contact Form
|
||
function setupContactForm() {
|
||
// Phone mask
|
||
const phoneInput = document.getElementById('contactPhone');
|
||
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 = '+7 (' + value.slice(1, 4);
|
||
}
|
||
if (value.length > 6) {
|
||
value = value.slice(0, 6) + ') ' + value.slice(6, 9);
|
||
}
|
||
if (value.length > 11) {
|
||
value = value.slice(0, 11) + '-' + value.slice(11, 13);
|
||
}
|
||
if (value.length > 14) {
|
||
value = value.slice(0, 14) + '-' + value.slice(14, 16);
|
||
}
|
||
|
||
e.target.value = value;
|
||
});
|
||
}
|
||
|
||
async function submitContact(event) {
|
||
event.preventDefault();
|
||
|
||
const name = document.getElementById('contactName').value;
|
||
const phone = document.getElementById('contactPhone').value;
|
||
const service = document.getElementById('contactService').value;
|
||
|
||
try {
|
||
const res = await fetch('/api/contact', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ name, phone, service })
|
||
});
|
||
const json = await res.json();
|
||
|
||
if (json.ok) {
|
||
alert('✅ ' + json.message);
|
||
document.getElementById('contactForm').reset();
|
||
} else {
|
||
alert('Ошибка: ' + json.error);
|
||
}
|
||
} catch (e) {
|
||
alert('Ошибка отправки: ' + e.message);
|
||
}
|
||
}
|
||
|
||
// Select tariff
|
||
function selectTariff(tariffId) {
|
||
const tariff = siteData.tariffs[currentTariffCategory].find(t => t.id === tariffId);
|
||
if (!tariff) return;
|
||
|
||
const service = currentTariffCategory === 'tv' ? 'Цифровое ТВ' :
|
||
currentTariffCategory === 'mobile' ? 'Мобильная связь' :
|
||
currentTariffCategory === 'smart' ? 'Умный дом' : 'Домашний интернет';
|
||
|
||
document.getElementById('contactService').value = currentTariffCategory;
|
||
document.getElementById('contactName').focus();
|
||
|
||
alert(`Вы выбрали тариф "${tariff.name}" за ${tariff.price} ₸\n\nЗаполните форму ниже, и мы свяжемся с вами для подключения!`);
|
||
|
||
scrollToSection('contacts');
|
||
}
|
||
|
||
// Select bundle
|
||
function selectBundle(bundleId) {
|
||
const bundle = siteData.bundles.find(b => b.id === bundleId);
|
||
if (!bundle) return;
|
||
|
||
document.getElementById('contactService').value = 'bundle';
|
||
document.getElementById('contactName').focus();
|
||
|
||
alert(`Вы выбрали пакет "${bundle.name}" за ${bundle.bundlePrice} ₸ (экономия ${bundle.discount}%)\n\nЗаполните форму ниже для подключения!`);
|
||
|
||
scrollToSection('contacts');
|
||
}
|
||
|
||
// Scroll to section
|
||
function scrollToSection(id) {
|
||
const element = document.getElementById(id);
|
||
if (element) {
|
||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||
}
|
||
}
|
||
|
||
// Setup animations
|
||
function setupAnimations() {
|
||
// Nav scroll effect
|
||
window.addEventListener('scroll', () => {
|
||
const nav = document.getElementById('nav');
|
||
if (window.scrollY > 50) {
|
||
nav.classList.add('scrolled');
|
||
} else {
|
||
nav.classList.remove('scrolled');
|
||
}
|
||
});
|
||
|
||
// Animate stats
|
||
const stats = document.querySelectorAll('.stat-number');
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
const target = parseInt(entry.target.dataset.target);
|
||
animateNumber(entry.target, target);
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, { threshold: 0.5 });
|
||
|
||
stats.forEach(stat => observer.observe(stat));
|
||
|
||
// Reveal on scroll
|
||
const reveals = document.querySelectorAll('.service-card, .tariff-card, .bundle-card, .news-card');
|
||
const revealObserver = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.style.opacity = '1';
|
||
entry.target.style.transform = 'translateY(0)';
|
||
}
|
||
});
|
||
}, { threshold: 0.1 });
|
||
|
||
reveals.forEach(el => {
|
||
el.style.opacity = '0';
|
||
el.style.transform = 'translateY(30px)';
|
||
el.style.transition = 'all 0.6s cubic-bezier(0.25, 1, 0.5, 1)';
|
||
revealObserver.observe(el);
|
||
});
|
||
}
|
||
|
||
// Animate number
|
||
function animateNumber(element, target) {
|
||
const duration = 2000;
|
||
const start = 0;
|
||
const increment = target / (duration / 16);
|
||
let current = start;
|
||
|
||
const timer = setInterval(() => {
|
||
current += increment;
|
||
if (current >= target) {
|
||
element.textContent = formatNumber(target);
|
||
clearInterval(timer);
|
||
} else {
|
||
element.textContent = formatNumber(Math.floor(current));
|
||
}
|
||
}, 16);
|
||
}
|
||
|
||
// Format number with spaces
|
||
function formatNumber(num) {
|
||
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
||
}
|
||
|
||
// Format date
|
||
function formatDate(isoString) {
|
||
const date = new Date(isoString);
|
||
return date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' });
|
||
}
|
||
|
||
// Open personal cabinet (placeholder)
|
||
function openPersonal() {
|
||
alert('Личный кабинет в разработке 🔜\n\nВы можете войти через:\n• Мобильное приложение\n• my.kazakhtelecom.kz');
|
||
}
|