spravochnik-tarifov-kt/index.html

393 lines
16 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>
.kt-ai-btn { text-decoration: none; }
.table-container { overflow-x: auto; }
.kt-ai-table { width: 100%; border-collapse: collapse; font-size: var(--kt-ai-text-sm); }
.kt-ai-table th { text-align: left; font-weight: var(--kt-ai-weight-medium); color: var(--kt-ai-fg-muted); padding: var(--kt-ai-space-3) var(--kt-ai-space-4); border-bottom: 1px solid var(--kt-ai-border); white-space: nowrap; }
.kt-ai-table td { padding: var(--kt-ai-space-3) var(--kt-ai-space-4); border-bottom: 1px solid var(--kt-ai-border); color: var(--kt-ai-fg); }
.kt-ai-table tr:hover { background: var(--kt-ai-bg-soft); }
.kt-ai-table tr.clickable { cursor: pointer; }
.kt-ai-table .actions { opacity: 0; transition: opacity 0.2s; }
.kt-ai-table tr:hover .actions { opacity: 1; }
.kt-ai-chip { display: inline-flex; align-items: center; padding: 2px 10px; border-radius: var(--kt-ai-radius-full); font-size: var(--kt-ai-text-xs); font-weight: var(--kt-ai-weight-medium); }
.kt-ai-chip[data-region="Алматы"] { background: #dbeafe; color: #1e40af; }
.kt-ai-chip[data-region="Астана"] { background: #dcfce7; color: #166534; }
.kt-ai-chip[data-region="Шымкент"] { background: #fef3c7; color: #92400e; }
.kt-ai-chip[data-region="Регионы"] { background: #f3e8ff; color: #6b21a8; }
.kt-ai-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: none; align-items: center; justify-content: center; z-index: 100; }
.kt-ai-modal-overlay.on { display: flex; }
.kt-ai-modal { background: var(--kt-ai-bg); border-radius: var(--kt-ai-radius-2xl); padding: var(--kt-ai-space-6); width: 100%; max-width: 480px; box-shadow: 0 20px 40px rgba(0,0,0,0.2); }
.kt-ai-kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--kt-ai-space-4); margin-bottom: var(--kt-ai-space-6); }
.kt-ai-kpi-card { background: var(--kt-ai-card-bg); border: 1px solid var(--kt-ai-card-border); border-radius: var(--kt-ai-radius-xl); padding: var(--kt-ai-space-4); }
.kt-ai-kpi-card .value { font-size: var(--kt-ai-text-2xl); font-weight: var(--kt-ai-weight-bold); color: var(--kt-ai-fg); }
.kt-ai-kpi-card .label { font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted); margin-top: 4px; }
.search-row { display: flex; gap: var(--kt-ai-space-3); align-items: center; flex-wrap: wrap; margin-bottom: var(--kt-ai-space-4); }
.search-input { flex: 1; min-width: 200px; padding: 8px 12px; border: 1px solid var(--kt-ai-border); border-radius: var(--kt-ai-radius-md); background: var(--kt-ai-bg); color: var(--kt-ai-fg); font-size: var(--kt-ai-text-sm); }
.filter-chips { display: flex; gap: var(--kt-ai-space-2); flex-wrap: wrap; }
.filter-chip { padding: 6px 12px; border: 1px solid var(--kt-ai-border); border-radius: var(--kt-ai-radius-full); background: var(--kt-ai-bg); color: var(--kt-ai-fg); font-size: var(--kt-ai-text-sm); cursor: pointer; transition: all 0.2s; }
.filter-chip:hover { background: var(--kt-ai-bg-soft); }
.filter-chip.active { background: var(--kt-ai-primary); color: white; border-color: var(--kt-ai-primary); }
.log-entry { font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-muted); padding: var(--kt-ai-space-2) 0; border-bottom: 1px solid var(--kt-ai-border); }
.empty-state { text-align: center; padding: var(--kt-ai-space-12); color: var(--kt-ai-fg-muted); }
</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>
<button class="kt-ai-btn" data-variant="ghost" onclick="exportCsv()">Выгрузить в Excel</button>
<button class="kt-ai-btn" data-variant="primary" onclick="openModal()">+ Добавить тариф</button>
</div>
</header>
<main class="kt-ai-wrap" style="padding-top: var(--kt-ai-space-8); padding-bottom: var(--kt-ai-space-12);">
<section class="kt-ai-kpi-strip" data-cards="true">
<div class="kt-ai-kpi-card">
<div class="value" id="kpi-total">0</div>
<div class="label">Всего тарифов</div>
</div>
<div class="kt-ai-kpi-card">
<div class="value" id="kpi-almaty">0</div>
<div class="label">Алматы</div>
</div>
<div class="kt-ai-kpi-card">
<div class="value" id="kpi-astana">0</div>
<div class="label">Астана</div>
</div>
<div class="kt-ai-kpi-card">
<div class="value" id="kpi-shymkent">0</div>
<div class="label">Шымкент</div>
</div>
<div class="kt-ai-kpi-card">
<div class="value" id="kpi-regions">0</div>
<div class="label">Регионы</div>
</div>
</section>
<section class="kt-ai-section">
<div class="kt-ai-wrap">
<div class="search-row">
<input type="text" class="search-input" id="search-input" placeholder="Поиск по названию тарифа…">
<div class="filter-chips" id="filter-chips"></div>
</div>
<div class="table-container">
<table class="kt-ai-table">
<thead>
<tr>
<th>Название</th>
<th>Скорость</th>
<th>Цена</th>
<th>Регион</th>
<th style="text-align: right;">Действия</th>
</tr>
</thead>
<tbody id="tariff-table-body"></tbody>
</table>
</div>
<div id="empty-state" class="empty-state" style="display: none;">
<p>Тарифы не найдены</p>
</div>
</div>
</section>
<section class="kt-ai-section" data-divider="true">
<div class="kt-ai-wrap">
<h2>Журнал изменений</h2>
<div id="log-container" style="margin-top: var(--kt-ai-space-4);"></div>
</div>
</section>
</main>
<div class="kt-ai-modal-overlay" id="modal-overlay">
<div class="kt-ai-modal">
<h2 id="modal-title" style="margin-bottom: var(--kt-ai-space-4);">Добавить тариф</h2>
<form id="tariff-form">
<input type="hidden" id="edit-id">
<div class="kt-ai-field" style="margin-bottom: var(--kt-ai-space-4);">
<label>Название тарифа</label>
<input class="kt-ai-input" id="form-name" required placeholder="Например: Fiber Optic Pro">
</div>
<div class="kt-ai-field" style="margin-bottom: var(--kt-ai-space-4);">
<label>Скорость</label>
<input class="kt-ai-input" id="form-speed" required placeholder="Например: 100 Мбит/с">
</div>
<div class="kt-ai-field" style="margin-bottom: var(--kt-ai-space-4);">
<label>Цена (тенге)</label>
<input class="kt-ai-input" id="form-price" type="number" required placeholder="Например: 5990">
</div>
<div class="kt-ai-field" style="margin-bottom: var(--kt-ai-space-6);">
<label>Регион</label>
<select class="kt-ai-select" id="form-region" required>
<option value="Алматы">Алматы</option>
<option value="Астана">Астана</option>
<option value="Шымкент">Шымкент</option>
<option value="Регионы">Регионы</option>
</select>
</div>
<div style="display: flex; gap: var(--kt-ai-space-3); justify-content: flex-end;">
<button type="button" class="kt-ai-btn" data-variant="ghost" onclick="closeModal()">Отмена</button>
<button type="submit" class="kt-ai-btn" data-variant="primary">Сохранить</button>
</div>
</form>
</div>
</div>
<script>
const REGIONS = ['Все', 'Алматы', 'Астана', 'Шымкент', 'Регионы'];
const DEMO_DATA = [
{ id: 1, name: 'Tariff Home', speed: '100 Мбит/с', price: 3990, region: 'Алматы' },
{ id: 2, name: 'Fiber Optic', speed: '500 Мбит/с', price: 7990, region: 'Астана' },
{ id: 3, name: 'Mobile Pro', speed: '50 Мбит/с', price: 2990, region: 'Шымкент' },
{ id: 4, name: 'Business Start', speed: '200 Мбит/с', price: 9990, region: 'Алматы' },
{ id: 5, name: 'Premium Fiber', speed: '1 Гбит/с', price: 14990, region: 'Астана' },
{ id: 6, name: 'Economy', speed: '50 Мбит/с', price: 3000, region: 'Регионы' },
{ id: 7, name: 'Smart Home', speed: '150 Мбит/с', price: 5490, region: 'Алматы' },
{ id: 8, name: 'Gaming Pro', speed: '500 Мбит/с', price: 8990, region: 'Шымкент' },
{ id: 9, name: 'Office Basic', speed: '100 Мбит/с', price: 6990, region: 'Регионы' },
{ id: 10, name: 'Ultra Fast', speed: '1 Гбит/с', price: 13990, region: 'Алматы' },
{ id: 11, name: 'Family Pack', speed: '200 Мбит/с', price: 6490, region: 'Астана' },
{ id: 12, name: 'Student', speed: '50 Мбит/с', price: 2490, region: 'Регионы' },
{ id: 13, name: 'Corporate', speed: '500 Мбит/с', price: 11990, region: 'Алматы' },
{ id: 14, name: 'Light Speed', speed: '300 Мбит/с', price: 7490, region: 'Шымкент' },
{ id: 15, name: 'Regional Plus', speed: '100 Мбит/с', price: 4990, region: 'Регионы' }
];
let tariffs = [];
let log = [];
let currentFilter = 'Все';
let searchQuery = '';
function loadFromStorage() {
const stored = localStorage.getItem('tariffs');
const storedLog = localStorage.getItem('tariffsLog');
if (stored) {
tariffs = JSON.parse(stored);
} else {
tariffs = [...DEMO_DATA];
saveToStorage();
}
if (storedLog) {
log = JSON.parse(storedLog);
}
}
function saveToStorage() {
localStorage.setItem('tariffs', JSON.stringify(tariffs));
localStorage.setItem('tariffsLog', JSON.stringify(log));
}
function formatPrice(price) {
return price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ' ₸';
}
function formatDate(dateStr) {
const d = new Date(dateStr);
return d.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' });
}
function renderKPIs() {
const filtered = getFilteredData();
document.getElementById('kpi-total').textContent = filtered.length;
document.getElementById('kpi-almaty').textContent = filtered.filter(t => t.region === 'Алматы').length;
document.getElementById('kpi-astana').textContent = filtered.filter(t => t.region === 'Астана').length;
document.getElementById('kpi-shymkent').textContent = filtered.filter(t => t.region === 'Шымкент').length;
document.getElementById('kpi-regions').textContent = filtered.filter(t => t.region === 'Регионы').length;
}
function getFilteredData() {
return tariffs.filter(t => {
const matchesRegion = currentFilter === 'Все' || t.region === currentFilter;
const matchesSearch = searchQuery === '' || t.name.toLowerCase().includes(searchQuery.toLowerCase());
return matchesRegion && matchesSearch;
});
}
function renderTable() {
const tbody = document.getElementById('tariff-table-body');
const data = getFilteredData();
if (data.length === 0) {
document.getElementById('empty-state').style.display = 'block';
tbody.innerHTML = '';
return;
}
document.getElementById('empty-state').style.display = 'none';
tbody.innerHTML = data.map(t => `
<tr class="clickable" onclick="editTariff(${t.id})">
<td>${t.name}</td>
<td>${t.speed}</td>
<td>${formatPrice(t.price)}</td>
<td><span class="kt-ai-chip" data-region="${t.region}">${t.region}</span></td>
<td class="actions" style="text-align: right;">
<button class="kt-ai-btn" data-variant="danger" data-size="sm" onclick="event.stopPropagation(); deleteTariff(${t.id})">Удалить</button>
</td>
</tr>
`).join('');
}
function renderFilterChips() {
const container = document.getElementById('filter-chips');
container.innerHTML = REGIONS.map(r => `
<button class="filter-chip ${currentFilter === r ? 'active' : ''}" onclick="setFilter('${r}')">${r}</button>
`).join('');
}
function renderLog() {
const container = document.getElementById('log-container');
if (log.length === 0) {
container.innerHTML = '<p style="color: var(--kt-ai-fg-muted); font-size: var(--kt-ai-text-sm);">Журнал пуст</p>';
return;
}
container.innerHTML = log.slice().reverse().map(entry => `
<div class="log-entry">
<strong>${formatDate(entry.date)}</strong> — ${entry.action} (${entry.who})
</div>
`).join('');
}
function setFilter(region) {
currentFilter = region;
renderFilterChips();
renderTable();
renderKPIs();
}
function openModal() {
document.getElementById('modal-title').textContent = 'Добавить тариф';
document.getElementById('edit-id').value = '';
document.getElementById('form-name').value = '';
document.getElementById('form-speed').value = '';
document.getElementById('form-price').value = '';
document.getElementById('form-region').value = 'Алматы';
document.getElementById('modal-overlay').classList.add('on');
}
function closeModal() {
document.getElementById('modal-overlay').classList.remove('on');
}
function editTariff(id) {
const tariff = tariffs.find(t => t.id === id);
if (!tariff) return;
document.getElementById('modal-title').textContent = 'Редактировать тариф';
document.getElementById('edit-id').value = tariff.id;
document.getElementById('form-name').value = tariff.name;
document.getElementById('form-speed').value = tariff.speed;
document.getElementById('form-price').value = tariff.price;
document.getElementById('form-region').value = tariff.region;
document.getElementById('modal-overlay').classList.add('on');
}
function deleteTariff(id) {
if (!confirm('Вы уверены, что хотите удалить этот тариф?')) return;
const tariff = tariffs.find(t => t.id === id);
if (!tariff) return;
tariffs = tariffs.filter(t => t.id !== id);
log.push({
date: new Date().toISOString(),
action: `Удалён тариф "${tariff.name}"`,
who: 'Пользователь'
});
saveToStorage();
renderTable();
renderKPIs();
renderLog();
}
function exportCsv() {
const data = getFilteredData();
const headers = ['Название', 'Скорость', 'Цена', 'Регион'];
const rows = data.map(t => [
'"' + t.name.replace(/"/g, '""') + '"',
'"' + t.speed.replace(/"/g, '""') + '"',
t.price,
'"' + t.region + '"'
]);
const csv = '\uFEFF' + [headers.join(';'), ...rows.map(r => r.join(';'))].join('\n');
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'spravochnik-tarifov.csv';
link.click();
}
document.getElementById('tariff-form').addEventListener('submit', function(e) {
e.preventDefault();
const id = document.getElementById('edit-id').value;
const name = document.getElementById('form-name').value.trim();
const speed = document.getElementById('form-speed').value.trim();
const price = parseInt(document.getElementById('form-price').value);
const region = document.getElementById('form-region').value;
if (id) {
const index = tariffs.findIndex(t => t.id == id);
if (index !== -1) {
const old = tariffs[index];
tariffs[index] = { ...old, name, speed, price, region };
log.push({
date: new Date().toISOString(),
action: `Изменён тариф "${name}"`,
who: 'Пользователь'
});
}
} else {
const newTariff = {
id: Date.now(),
name,
speed,
price,
region
};
tariffs.push(newTariff);
log.push({
date: new Date().toISOString(),
action: `Добавлен тариф "${name}"`,
who: 'Пользователь'
});
}
saveToStorage();
closeModal();
renderTable();
renderKPIs();
renderLog();
});
document.getElementById('search-input').addEventListener('input', function(e) {
searchQuery = e.target.value.trim();
renderTable();
renderKPIs();
});
loadFromStorage();
renderFilterChips();
renderTable();
renderKPIs();
renderLog();
</script>
</body>
</html>