let appData = { presets: [], history: [] }; let selectedPresetId = null; async function loadData() { try { const res = await fetch('/api/data'); const data = await res.json(); appData = data; if (appData.presets.length === 0) { appData.presets.push({ id: Date.now(), name: 'Областной', variableCosts: 15921.2583, avgCapacity: 9.4317, fixedCosts: 8129.6707 }); } if (appData.history === undefined) { appData.history = []; } saveData(); } catch (e) { console.error('Ошибка загрузки:', e); if (appData.presets.length === 0) { appData.presets.push({ id: Date.now(), name: 'Областной', variableCosts: 15921.2583, avgCapacity: 9.4317, fixedCosts: 8129.6707 }); } } } async function saveData() { try { await fetch('/api/data', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(appData) }); } catch (e) { console.error('Ошибка сохранения:', e); } } function formatNumber(num) { return num.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } function formatNumberFull(num) { return num.toLocaleString('ru-RU', { minimumFractionDigits: 4, maximumFractionDigits: 4 }); } function renderPresetsSelect() { const select = document.getElementById('presetSelect'); select.innerHTML = ''; appData.presets.forEach(preset => { const option = document.createElement('option'); option.value = preset.id; option.textContent = preset.name; select.appendChild(option); }); if (selectedPresetId && appData.presets.find(p => p.id === selectedPresetId)) { select.value = selectedPresetId; } else if (appData.presets.length > 0) { selectedPresetId = appData.presets[0].id; select.value = selectedPresetId; } } function renderPresetsSettings() { const container = document.getElementById('presetsContainer'); container.innerHTML = ''; appData.presets.forEach(preset => { const card = document.createElement('div'); card.className = 'preset-card'; card.innerHTML = `

`; container.appendChild(card); }); container.querySelectorAll('.btn-delete').forEach(btn => { btn.addEventListener('click', (e) => { const id = parseInt(e.target.dataset.presetId); if (appData.presets.length > 1) { appData.presets = appData.presets.filter(p => p.id !== id); if (selectedPresetId === id) { selectedPresetId = appData.presets[0].id; } renderPresetsSettings(); } else { alert('Должен остаться хотя бы один тип расчета'); } }); }); container.querySelectorAll('input').forEach(input => { input.addEventListener('input', (e) => { const id = parseInt(e.target.dataset.presetId); const field = e.target.dataset.field; const preset = appData.presets.find(p => p.id === id); if (preset) { if (field === 'name') { preset.name = e.target.value; } else { preset[field] = parseFloat(e.target.value) || 0; } } }); }); } function renderHistory() { const tbody = document.querySelector('#historyTable tbody'); const noHistory = document.getElementById('noHistory'); if (appData.history.length === 0) { tbody.innerHTML = ''; noHistory.style.display = 'block'; return; } noHistory.style.display = 'none'; tbody.innerHTML = ''; const reversed = [...appData.history].reverse(); reversed.forEach(record => { const row = document.createElement('tr'); const date = new Date(record.date); const dateStr = date.toLocaleDateString('ru-RU'); row.innerHTML = ` ${dateStr} ${record.presetName} ${formatNumber(record.speed)} Мбит/с ${formatNumber(record.result)} ₸ `; tbody.appendChild(row); }); } function calculateCost() { const speedInput = document.getElementById('speedInput'); const speed = parseFloat(speedInput.value); const preset = appData.presets.find(p => p.id === selectedPresetId); if (!preset) { showError('Выберите тип расчета'); return null; } if (isNaN(speed) || speed <= 0) { showError('Введите корректное значение скорости (больше 0)'); return null; } if (preset.avgCapacity <= 0) { showError('Средняя пропускная способность должна быть больше 0. Проверьте настройки.'); return null; } const variablePart = (preset.variableCosts / preset.avgCapacity) * speed; const total = variablePart + preset.fixedCosts; return { variablePart, fixedPart: preset.fixedCosts, total, speed, preset }; } function showResult(calc) { const resultBlock = document.getElementById('resultBlock'); const resultValue = document.getElementById('resultValue'); const variablePart = document.getElementById('variablePart'); const fixedPart = document.getElementById('fixedPart'); const speedDisplay = document.getElementById('speedDisplay'); const formulaText = document.getElementById('formulaText'); resultValue.textContent = formatNumber(calc.total) + ' ₸'; variablePart.textContent = formatNumber(calc.variablePart) + ' ₸'; fixedPart.textContent = formatNumber(calc.fixedPart) + ' ₸'; speedDisplay.textContent = formatNumber(calc.speed) + ' Мбит/с'; formulaText.textContent = `(${formatNumberFull(calc.preset.variableCosts)} / ${formatNumberFull(calc.preset.avgCapacity)}) × ${formatNumber(calc.speed)} + ${formatNumberFull(calc.preset.fixedCosts)} = ${formatNumber(calc.total)}`; resultBlock.style.display = 'block'; } function showError(message) { const existingError = document.querySelector('.error-message'); if (existingError) { existingError.remove(); } const errorDiv = document.createElement('div'); errorDiv.className = 'error-message'; errorDiv.textContent = message; const calculatorCard = document.querySelector('.calculator'); calculatorCard.insertBefore(errorDiv, calculatorCard.firstChild); setTimeout(() => { errorDiv.remove(); }, 5000); } function addToHistory(calc) { const record = { date: new Date().toISOString(), presetId: calc.preset.id, presetName: calc.preset.name, speed: calc.speed, variableCosts: calc.preset.variableCosts, avgCapacity: calc.preset.avgCapacity, fixedCosts: calc.preset.fixedCosts, result: calc.total }; appData.history.push(record); if (appData.history.length > 100) { appData.history = appData.history.slice(-100); } saveData(); renderHistory(); } document.getElementById('calculateBtn').addEventListener('click', () => { const calc = calculateCost(); if (calc) { showResult(calc); addToHistory(calc); } }); document.getElementById('speedInput').addEventListener('keypress', (e) => { if (e.key === 'Enter') { document.getElementById('calculateBtn').click(); } }); document.getElementById('settingsBtn').addEventListener('click', () => { renderPresetsSettings(); document.getElementById('settingsModal').classList.add('on'); }); document.getElementById('closeModal').addEventListener('click', () => { document.getElementById('settingsModal').classList.remove('on'); }); document.getElementById('saveSettingsBtn').addEventListener('click', () => { saveData(); renderPresetsSelect(); document.getElementById('settingsModal').classList.remove('on'); }); document.getElementById('addPresetBtn').addEventListener('click', () => { const name = prompt('Введите название типа расчета:'); if (name && name.trim()) { appData.presets.push({ id: Date.now(), name: name.trim(), variableCosts: 0, avgCapacity: 1, fixedCosts: 0 }); renderPresetsSettings(); } }); document.getElementById('clearHistoryBtn').addEventListener('click', () => { if (confirm('Вы уверены, что хотите очистить историю расчетов?')) { appData.history = []; saveData(); renderHistory(); } }); document.getElementById('presetSelect').addEventListener('change', (e) => { selectedPresetId = parseInt(e.target.value); }); document.getElementById('settingsModal').addEventListener('click', (e) => { if (e.target === document.getElementById('settingsModal')) { document.getElementById('settingsModal').classList.remove('on'); } }); async function init() { await loadData(); renderPresetsSelect(); renderHistory(); const urlParams = new URLSearchParams(window.location.search); const speed = urlParams.get('speed'); if (speed) { document.getElementById('speedInput').value = speed; } } init();