kalkulyator-stoimosti-podkly/index.html

330 lines
12 KiB
HTML
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.

<!DOCTYPE html>
<html lang="ru" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Калькулятор стоимости подключения</title>
<link rel="stylesheet" href="design-system/kt-ai-fonts.css">
<link rel="stylesheet" href="design-system/kt-ai-tokens.css">
<link rel="stylesheet" href="design-system/kt-ai-components.css">
<link rel="stylesheet" href="design-system/kt-ai-page.css">
<style>
.calc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--kt-ai-space-6); margin-top: var(--kt-ai-space-8); }
.calc-card { cursor: pointer; transition: all 0.2s ease; }
.calc-card:hover { transform: translateY(-2px); box-shadow: var(--kt-ai-shadow-lg); }
.calc-card.selected { border-color: var(--kt-ai-primary); background: var(--kt-ai-primary-muted); }
.price-badge { font-size: var(--kt-ai-text-xl); font-weight: var(--kt-ai-weight-bold); color: var(--kt-ai-primary); }
.total-block { background: var(--kt-ai-card-bg); border: 1px solid var(--kt-ai-border); border-radius: var(--kt-ai-radius-3xl); padding: var(--kt-ai-space-8); text-align: center; margin-top: var(--kt-ai-space-10); }
.total-amount { font-size: clamp(32px, 5vw, 48px); font-weight: var(--kt-ai-weight-bold); color: var(--kt-ai-primary); margin: var(--kt-ai-space-4) 0; }
.radio-hidden { position: absolute; opacity: 0; pointer-events: none; }
.journal { margin-top: var(--kt-ai-space-8); padding: var(--kt-ai-space-6); background: var(--kt-ai-bg-soft); border-radius: var(--kt-ai-radius-xl); }
.journal-entry { padding: var(--kt-ai-space-3) 0; border-bottom: 1px solid var(--kt-ai-divider); font-size: var(--kt-ai-text-sm); }
.journal-entry:last-child { border-bottom: none; }
.reveal { opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease, transform 0.6s ease; }
.reveal.in { opacity: 1; transform: none; }
</style>
</head>
<body>
<header class="kt-ai-pagehead">
<div class="kt-ai-wrap kt-ai-pagehead-in">
<strong>Калькулятор подключения</strong>
<span class="kt-ai-spacer"></span>
<a class="kt-ai-btn" data-variant="ghost" href="#journal">Журнал</a>
</div>
</header>
<section class="kt-ai-hero" data-align="center">
<div class="kt-ai-wrap">
<h1 class="reveal" style="color: var(--kt-ai-ok)">Рассчитайте стоимость подключения</h1>
<p class="kt-ai-hero-sub reveal">Выберите тариф, скорость и оборудование — сумма обновится автоматически</p>
</div>
</section>
<section class="kt-ai-section" data-divider="true">
<div class="kt-ai-wrap" data-width="wide">
<div class="calc-grid">
<div class="reveal">
<h2 style="margin-bottom: var(--kt-ai-space-4)">Тарифный план</h2>
<div id="tariffs" class="kt-ai-v" data-gap="tight">
</div>
</div>
<div class="reveal">
<h2 style="margin-bottom: var(--kt-ai-space-4)">Скорость соединения</h2>
<div id="speeds" class="kt-ai-v" data-gap="tight">
</div>
</div>
<div class="reveal">
<h2 style="margin-bottom: var(--kt-ai-space-4)">Оборудование</h2>
<div id="equipment" class="kt-ai-v" data-gap="tight">
</div>
</div>
</div>
<div class="total-block reveal">
<span style="font-size: var(--kt-ai-text-lg); color: var(--kt-ai-fg-muted)">Итого в месяц</span>
<div class="total-amount" id="totalAmount">0 ₸</div>
<label class="kt-ai-card" style="display: inline-flex; align-items: center; gap: var(--kt-ai-space-3); padding: var(--kt-ai-space-4) var(--kt-ai-space-5); margin: var(--kt-ai-space-5) 0; cursor: pointer">
<input type="checkbox" id="discountCheck" style="width: 18px; height: 18px">
<span style="font-weight: var(--kt-ai-weight-medium)">Скидка 10% для сотрудников КТ</span>
</label>
<button class="kt-ai-btn" data-variant="primary" data-size="lg" id="copyBtn">
<svg class="kt-icon" style="margin-right: 8px"><use href="#copy"></use></svg>
Скопировать расчёт
</button>
<div id="copyStatus" style="margin-top: var(--kt-ai-space-3); font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted); min-height: 20px;"></div>
</div>
<div id="journal" class="journal reveal">
<h3 style="margin-bottom: var(--kt-ai-space-4)">Журнал расчётов</h3>
<div id="journalList">
<div class="journal-entry" style="color: var(--kt-ai-fg-muted)">Пока нет записей</div>
</div>
</div>
</div>
</section>
<footer class="kt-ai-wrap kt-ai-pagefoot">
<span>© 2026 Калькулятор подключения</span>
<span class="kt-ai-spacer"></span>
<span style="color: var(--kt-ai-fg-muted)">Для менеджеров и клиентов</span>
</footer>
<script>
// Демо-данные из brief.md
const TARIFFS = [
{ id: 'basic', name: 'Базовый', price: 5000 },
{ id: 'optimal', name: 'Оптимальный', price: 7500 },
{ id: 'premium', name: 'Премиум', price: 12000 }
];
const SPEEDS = [
{ id: '100', name: '100 Мбит/с', price: 0 },
{ id: '500', name: '500 Мбит/с', price: 1000 },
{ id: '1000', name: '1 Гбит/с', price: 2000 }
];
const EQUIPMENT = [
{ id: 'own', name: 'Свой роутер', price: 0 },
{ id: 'wifi5', name: 'Аренда Wi-Fi 5', price: 1500 },
{ id: 'wifi6', name: 'Аренда Wi-Fi 6', price: 2500 }
];
// Состояние
let state = {
tariff: null,
speed: null,
equipment: null,
discount: false
};
// Инициализация
function init() {
loadState();
renderOptions();
updateTotal();
renderJournal();
setupReveal();
setupDiscount();
}
// Рендер опций
function renderOptions() {
const tariffsEl = document.getElementById('tariffs');
const speedsEl = document.getElementById('speeds');
const equipmentEl = document.getElementById('equipment');
tariffsEl.innerHTML = TARIFFS.map(t => renderCard('tariff', t.id, t.name, t.price, state.tariff === t.id)).join('');
speedsEl.innerHTML = SPEEDS.map(s => renderCard('speed', s.id, s.name, s.price, state.speed === s.id)).join('');
equipmentEl.innerHTML = EQUIPMENT.map(e => renderCard('equipment', e.id, e.name, e.price, state.equipment === e.id)).join('');
// Клик по карточкам
document.querySelectorAll('.calc-card').forEach(card => {
card.addEventListener('click', () => {
const type = card.dataset.type;
const id = card.dataset.id;
selectOption(type, id);
});
});
}
function renderCard(type, id, name, price, selected) {
const priceText = price === 0 ? 'Бесплатно' : `+${price.toLocaleString()} ₸`;
return `
<label class="calc-card kt-ai-card ${selected ? 'selected' : ''}" data-type="${type}" data-id="${id}" style="padding: var(--kt-ai-space-5)">
<input type="radio" name="${type}" class="radio-hidden" ${selected ? 'checked' : ''}>
<div style="display: flex; justify-content: space-between; align-items: center">
<span style="font-weight: var(--kt-ai-weight-medium)">${name}</span>
<span class="price-badge">${priceText}</span>
</div>
</label>
`;
}
// Выбор опции
function selectOption(type, id) {
state[type] = id;
saveState();
renderOptions();
updateTotal();
logAction();
}
// Расчёт итога
function updateTotal() {
let total = 0;
if (state.tariff) {
const t = TARIFFS.find(x => x.id === state.tariff);
total += t.price;
}
if (state.speed) {
const s = SPEEDS.find(x => x.id === state.speed);
total += s.price;
}
if (state.equipment) {
const e = EQUIPMENT.find(x => x.id === state.equipment);
total += e.price;
}
if (state.discount) {
total = total * 0.9;
}
document.getElementById('totalAmount').textContent = Math.round(total).toLocaleString() + ' ₸';
}
// LocalStorage
function saveState() {
localStorage.setItem('calcState', JSON.stringify(state));
}
function loadState() {
try {
const saved = localStorage.getItem('calcState');
if (saved) {
state = JSON.parse(saved);
}
} catch (e) {}
}
// Скидка
function setupDiscount() {
const check = document.getElementById('discountCheck');
check.checked = state.discount;
check.addEventListener('change', () => {
state.discount = check.checked;
saveState();
updateTotal();
logAction();
});
}
// Журнал
function logAction() {
const entry = {
time: new Date().toISOString(),
tariff: state.tariff,
speed: state.speed,
equipment: state.equipment,
total: calculateTotal()
};
let journal = [];
try {
const saved = localStorage.getItem('calcJournal');
if (saved) journal = JSON.parse(saved);
} catch (e) {}
journal.unshift(entry);
if (journal.length > 10) journal = journal.slice(0, 10);
localStorage.setItem('calcJournal', JSON.stringify(journal));
renderJournal();
}
function calculateTotal() {
let total = 0;
if (state.tariff) total += TARIFFS.find(x => x.id === state.tariff).price;
if (state.speed) total += SPEEDS.find(x => x.id === state.speed).price;
if (state.equipment) total += EQUIPMENT.find(x => x.id === state.equipment).price;
if (state.discount) total = total * 0.9;
return Math.round(total);
}
function renderJournal() {
const list = document.getElementById('journalList');
let journal = [];
try {
const saved = localStorage.getItem('calcJournal');
if (saved) journal = JSON.parse(saved);
} catch (e) {}
if (journal.length === 0) {
list.innerHTML = '<div class="journal-entry" style="color: var(--kt-ai-fg-muted)">Пока нет записей</div>';
return;
}
list.innerHTML = journal.map(j => {
const time = new Date(j.time).toLocaleString('ru-RU', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
const tariffName = TARIFFS.find(t => t.id === j.tariff)?.name || '—';
const speedName = SPEEDS.find(s => s.id === j.speed)?.name || '—';
const equipName = EQUIPMENT.find(e => e.id === j.equipment)?.name || '—';
return `
<div class="journal-entry">
<div style="font-weight: var(--kt-ai-weight-medium)">${time} — <strong>${j.total.toLocaleString()} ₸</strong></div>
<div style="color: var(--kt-ai-fg-muted); font-size: var(--kt-ai-text-xs)">${tariffName} | ${speedName} | ${equipName}</div>
</div>
`;
}).join('');
}
// Копирование
document.getElementById('copyBtn').addEventListener('click', () => {
if (!state.tariff || !state.speed || !state.equipment) {
document.getElementById('copyStatus').textContent = 'Выберите все параметры';
document.getElementById('copyStatus').style.color = 'var(--kt-ai-danger)';
return;
}
const t = TARIFFS.find(x => x.id === state.tariff);
const s = SPEEDS.find(x => x.id === state.speed);
const e = EQUIPMENT.find(x => x.id === state.equipment);
const total = calculateTotal();
const discountText = state.discount ? '\n🎁 Скидка 10% (сотрудник КТ): применена' : '';
const text = `📊 Расчёт стоимости подключения:
🔹 Тариф: ${t.name} — ${t.price.toLocaleString()} ₸
🔹 Скорость: ${s.name} — ${s.price === 0 ? 'Бесплатно' : '+' + s.price.toLocaleString() + ' ₸'}
🔹 Оборудование: ${e.name} — ${e.price === 0 ? 'Бесплатно' : '+' + e.price.toLocaleString() + ' ₸/мес'}${discountText}
💰 Итого: ${total.toLocaleString()} ₸ в месяц`;
navigator.clipboard.writeText(text).then(() => {
document.getElementById('copyStatus').textContent = '✓ Скопировано в буфер обмена';
document.getElementById('copyStatus').style.color = 'var(--kt-ai-fg-muted)';
setTimeout(() => {
document.getElementById('copyStatus').textContent = '';
}, 3000);
}).catch(() => {
document.getElementById('copyStatus').textContent = 'Не удалось скопировать';
document.getElementById('copyStatus').style.color = 'var(--kt-ai-danger)';
});
});
// Анимация появления
function setupReveal() {
const io = new IntersectionObserver(function(entries) {
entries.forEach(function(e) {
if (e.isIntersecting) e.target.classList.add('in');
});
}, { threshold: 0.15 });
document.querySelectorAll('.reveal').forEach(function(el) { io.observe(el); });
}
// Старт
init();
</script>
</body>
</html>