nado-sozdat-po-marketingovom/index.html

332 lines
14 KiB
HTML
Raw 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">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Маркетинговый анализ цен</title>
<style>
* { margin: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; background: #f5f5f7; color: #1d1d1f; line-height: 1.5; }
header { background: linear-gradient(135deg, #007AFF 0%, #00C7BE 100%); color: white; padding: 32px 24px; text-align: center; }
header h1 { font-size: clamp(24px, 4vw, 36px); margin-bottom: 8px; }
header p { opacity: 0.9; font-size: 16px; }
.container { max-width: 1200px; margin: 0 auto; padding: 24px; }
.tabs { display: flex; gap: 8px; margin-bottom: 24px; flex-wrap: wrap; }
.tab { padding: 10px 20px; border: none; background: white; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 500; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.tab.active { background: #007AFF; color: white; }
.section { display: none; }
.section.active { display: block; }
.card { background: white; border-radius: 16px; padding: 24px; margin-bottom: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
.card h2 { font-size: 20px; margin-bottom: 16px; color: #1d1d1f; }
.form-group { margin-bottom: 16px; }
.form-group label { display: block; margin-bottom: 6px; font-weight: 500; font-size: 14px; color: #6e6e73; }
.form-group input, .form-group select { width: 100%; padding: 12px; border: 1px solid #d2d2d7; border-radius: 10px; font-size: 15px; }
.form-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
.btn { padding: 12px 24px; border: none; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 600; transition: all 0.2s; }
.btn-primary { background: #007AFF; color: white; }
.btn-primary:hover { background: #0056b3; }
.btn-success { background: #34C759; color: white; }
.btn-danger { background: #FF3B30; color: white; padding: 8px 16px; font-size: 13px; }
table { width: 100%; border-collapse: collapse; margin-top: 16px; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #e5e5ea; }
th { background: #f5f5f7; font-weight: 600; font-size: 13px; color: #6e6e73; text-transform: uppercase; }
tr:hover { background: #f9f9fb; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px; margin-bottom: 24px; }
.stat { background: white; padding: 20px; border-radius: 12px; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
.stat-value { font-size: 32px; font-weight: 700; color: #007AFF; }
.stat-label { font-size: 13px; color: #6e6e73; margin-top: 4px; }
.price-highlight { background: #d4edda; font-weight: 600; }
.empty { text-align: center; padding: 40px; color: #86868b; }
</style>
</head>
<body>
<header>
<h1>📊 Маркетинговый анализ цен</h1>
<p>Сравнение цен казахстанских товаропроизводителей</p>
</header>
<div class="container">
<div class="tabs">
<button class="tab active" onclick="showTab('producers')">Производители</button>
<button class="tab" onclick="showTab('products')">Товары и цены</button>
<button class="tab" onclick="showTab('analysis')">Анализ</button>
</div>
<!-- Производители -->
<div id="producers" class="section active">
<div class="card">
<h2>Добавить производителя</h2>
<form id="producerForm">
<div class="form-row">
<div class="form-group">
<label>Название компании</label>
<input type="text" name="name" required placeholder="ТОО «Пример»">
</div>
<div class="form-group">
<label>Город</label>
<input type="text" name="city" required placeholder="Алматы">
</div>
<div class="form-group">
<label>Контакты</label>
<input type="text" name="contact" placeholder="Телефон / сайт">
</div>
</div>
<button type="submit" class="btn btn-primary">Добавить производителя</button>
</form>
</div>
<div class="card">
<h2>Список производителей</h2>
<table>
<thead>
<tr><th>Название</th><th>Город</th><th>Контакты</th><th>Действия</th></tr>
</thead>
<tbody id="producersTable"></tbody>
</table>
<div id="producersEmpty" class="empty">Нет производителей</div>
</div>
</div>
<!-- Товары -->
<div id="products" class="section">
<div class="card">
<h2>Добавить товар и цену</h2>
<form id="productForm">
<div class="form-row">
<div class="form-group">
<label>Производитель</label>
<select name="producerId" id="producerSelect" required></select>
</div>
<div class="form-group">
<label>Наименование товара</label>
<input type="text" name="productName" required placeholder="Например: Мука в/с, 50кг">
</div>
<div class="form-group">
<label>Цена (₸)</label>
<input type="number" name="price" required placeholder="15000">
</div>
<div class="form-group">
<label>Ед. измерения</label>
<input type="text" name="unit" placeholder="шт, кг, уп">
</div>
</div>
<button type="submit" class="btn btn-success">Добавить товар</button>
</form>
</div>
<div class="card">
<h2>Все товары</h2>
<table>
<thead>
<tr><th>Товар</th><th>Производитель</th><th>Цена</th><th>Ед.</th><th>Действия</th></tr>
</thead>
<tbody id="productsTable"></tbody>
</table>
<div id="productsEmpty" class="empty">Нет товаров</div>
</div>
</div>
<!-- Анализ -->
<div id="analysis" class="section">
<div class="stats">
<div class="stat">
<div class="stat-value" id="statProducers">0</div>
<div class="stat-label">Производителей</div>
</div>
<div class="stat">
<div class="stat-value" id="statProducts">0</div>
<div class="stat-label">Товаров</div>
</div>
<div class="stat">
<div class="stat-value" id="statAvgPrice">0</div>
<div class="stat-label">Средняя цена</div>
</div>
</div>
<div class="card">
<h2>Сравнение цен по товарам</h2>
<table>
<thead>
<tr><th>Товар</th><th>Мин. цена</th><th>Макс. цена</th><th>Разница</th><th>Лучшее предложение</th></tr>
</thead>
<tbody id="analysisTable"></tbody>
</table>
<div id="analysisEmpty" class="empty">Нет данных для анализа</div>
</div>
<div class="card">
<h2>Экспорт</h2>
<button class="btn btn-primary" onclick="exportToExcel()">📥 Выгрузить в Excel</button>
</div>
</div>
</div>
<script>
let data = { producers: [], products: [] };
async function loadData() {
const r = await fetch('/api/data');
data = await r.json();
renderProducers();
renderProducts();
renderAnalysis();
updateProducerSelect();
}
function renderProducers() {
const tbody = document.getElementById('producersTable');
const empty = document.getElementById('producersEmpty');
if (data.producers.length === 0) {
tbody.innerHTML = '';
empty.style.display = 'block';
return;
}
empty.style.display = 'none';
tbody.innerHTML = data.producers.map(p => `
<tr>
<td>${p.name}</td>
<td>${p.city}</td>
<td>${p.contact || '—'}</td>
<td><button class="btn btn-danger" onclick="deleteProducer(${p.id})">Удалить</button></td>
</tr>
`).join('');
}
function renderProducts() {
const tbody = document.getElementById('productsTable');
const empty = document.getElementById('productsEmpty');
if (data.products.length === 0) {
tbody.innerHTML = '';
empty.style.display = 'block';
return;
}
empty.style.display = 'none';
tbody.innerHTML = data.products.map(p => {
const producer = data.producers.find(pr => pr.id === p.producerId);
return `
<tr>
<td>${p.productName}</td>
<td>${producer ? producer.name : '—'}</td>
<td>${Number(p.price).toLocaleString()} ₸</td>
<td>${p.unit || '—'}</td>
<td><button class="btn btn-danger" onclick="deleteProduct(${p.id})">Удалить</button></td>
</tr>
`;
}).join('');
}
function renderAnalysis() {
const byProduct = {};
data.products.forEach(p => {
if (!byProduct[p.productName]) byProduct[p.productName] = [];
const producer = data.producers.find(pr => pr.id === p.producerId);
byProduct[p.productName].push({ price: Number(p.price), producer: producer ? producer.name : '—', unit: p.unit });
});
const tbody = document.getElementById('analysisTable');
const empty = document.getElementById('analysisEmpty');
document.getElementById('statProducers').textContent = data.producers.length;
document.getElementById('statProducts').textContent = data.products.length;
const allPrices = data.products.map(p => Number(p.price));
const avg = allPrices.length ? Math.round(allPrices.reduce((a,b) => a+b, 0) / allPrices.length) : 0;
document.getElementById('statAvgPrice').textContent = avg.toLocaleString() + ' ₸';
if (Object.keys(byProduct).length === 0) {
tbody.innerHTML = '';
empty.style.display = 'block';
return;
}
empty.style.display = 'none';
tbody.innerHTML = Object.entries(byProduct).map(([name, items]) => {
const prices = items.map(i => i.price);
const min = Math.min(...prices);
const max = Math.max(...prices);
const diff = max - min;
const best = items.find(i => i.price === min);
return `
<tr>
<td>${name}</td>
<td>${min.toLocaleString()} ₸</td>
<td>${max.toLocaleString()} ₸</td>
<td>${diff.toLocaleString()} ₸ (${diff > 0 ? Math.round(diff/min*100) : 0}%)</td>
<td class="price-highlight">${best.producer} — ${best.price.toLocaleString()} ₸/${best.unit || 'шт'}</td>
</tr>
`;
}).join('');
}
function updateProducerSelect() {
const sel = document.getElementById('producerSelect');
sel.innerHTML = '<option value="">Выберите производителя</option>' +
data.producers.map(p => `<option value="${p.id}">${p.name} (${p.city})</option>`).join('');
}
function showTab(id) {
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.getElementById(id).classList.add('active');
event.target.classList.add('active');
}
document.getElementById('producerForm').addEventListener('submit', async (e) => {
e.preventDefault();
const f = e.target;
await fetch('/api/producers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: f.name.value, city: f.city.value, contact: f.contact.value })
});
f.reset();
loadData();
});
document.getElementById('productForm').addEventListener('submit', async (e) => {
e.preventDefault();
const f = e.target;
await fetch('/api/products', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
producerId: Number(f.producerId.value),
productName: f.productName.value,
price: Number(f.price.value),
unit: f.unit.value
})
});
f.reset();
loadData();
});
async function deleteProducer(id) {
if (!confirm('Удалить производителя и все его товары?')) return;
await fetch('/api/producers/' + id, { method: 'DELETE' });
loadData();
}
async function deleteProduct(id) {
if (!confirm('Удалить товар?')) return;
await fetch('/api/products/' + id, { method: 'DELETE' });
loadData();
}
function exportToExcel() {
const head = ["Товар", "Производитель", "Город", "Цена", "Ед.изм."];
const body = data.products.map(p => {
const pr = data.producers.find(x => x.id === p.producerId);
return [p.productName, pr ? pr.name : '', pr ? pr.city : '', p.price, p.unit || ''];
});
const csv = '\uFEFF' + [head.join(';'), ...body.map(r => r.map(v => '"' + String(v).replace(/"/g, '""') + '"').join(';'))].join('\n');
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([csv], { type: 'text/csv' }));
a.download = 'analiz_cen.csv';
a.click();
}
loadData();
</script>
</body>
</html>