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();