<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Заказ</title>
<style>
:root{--bg:#0b1220;--card:#fff;--muted:#64748b;--text:#0f172a;--line:#e2e8f0;--soft:#f1f5f9;--soft2:#eef2f7;--brand:#2563eb;--dark:#111827}
*{box-sizing:border-box}
body{font-family:Arial,sans-serif;background:var(--bg);margin:0;color:var(--text)}
.wrap{max-width:1180px;margin:22px auto;padding:0 14px}
.card{background:var(--card);border-radius:18px;overflow:hidden;box-shadow:0 22px 70px rgba(0,0,0,.28)}
.hero{background:linear-gradient(135deg,#0b1220 0%,#111827 60%,#0b1220 100%);padding:22px 24px;display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap}
.hero h1{margin:0;color:#fff;font-size:22px}
.badges{display:flex;gap:10px;flex-wrap:wrap}
.badge{background:rgba(255,255,255,.1);color:#fff;padding:7px 10px;border-radius:999px;font-size:12px;border:1px solid rgba(255,255,255,.12)}
.grid{padding:18px 24px}
.row{display:flex;gap:12px;flex-wrap:wrap}
.field{flex:1;min-width:260px}
.label{font-size:12px;color:var(--muted);margin-bottom:6px}
input,textarea,select{width:100%;padding:11px 12px;border:1px solid #cbd5e1;border-radius:12px;font-size:14px;outline:none;background:#fff}
input:focus,textarea:focus,select:focus{border-color:#93c5fd;box-shadow:0 0 0 3px rgba(37,99,235,.12)}
textarea{min-height:78px;resize:vertical}
.hint{color:var(--muted);font-size:12px;margin-top:8px}
.tools{margin-top:10px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;justify-content:space-between}
.btn{border:none;border-radius:12px;padding:10px 14px;font-weight:700;cursor:pointer;font-size:14px}
.btnPrimary{background:var(--brand);color:#fff}
.btnGhost{background:#e2e8f0;color:#0f172a}
.status{font-weight:700}
.err{color:#b91c1c}
.ok{color:#047857}
.total{font-size:13px;font-weight:700}
.catalog{margin-top:16px}
details.cat{border:1px solid var(--line);border-radius:14px;overflow:hidden;background:#fff;margin-bottom:10px}
details.cat summary{cursor:pointer;list-style:none;padding:12px 14px;background:#f8fafc;display:flex;justify-content:space-between;align-items:center;gap:10px;font-weight:700}
details.cat summary::-webkit-details-marker{display:none}
.count{font-weight:700;color:#334155;background:#e2e8f0;padding:4px 8px;border-radius:999px;font-size:12px}
.tableWrap{padding:10px 12px}
table{width:100%;border-collapse:collapse;font-size:13px}
thead th{background:var(--dark);color:#fff;padding:10px;text-align:left}
tbody td{padding:10px;border-bottom:1px solid var(--line);vertical-align:middle}
tbody tr:nth-child(even){background:var(--soft2)}
.rightAlign{text-align:right}
.center{text-align:center}
.qty{width:90px}
.searchBox{border:1px solid var(--line);border-radius:14px;padding:12px 14px;background:#fff;margin-top:14px}
.searchHead{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px;font-weight:700}
.muted{color:var(--muted);font-size:12px}
</style>
</head>
<body>
<div class="wrap">
<div class="card">
<div class="hero">
<h1 id="title">Прайс на стандартную доставку</h1>
<div class="badges">
<div class="badge" id="dateLine"></div>
<div class="badge">Наш канал: @sonydysondubai</div>
</div>
</div>
<div class="grid">
<div class="row">
<div class="field">
<div class="label">Номер вашего предыдущего любого заказа</div>
<input id="prevOrder" placeholder="например: #1007853">
<div class="hint">По этому номеру мы определяем вас как клиента и создаём новый заказ в МойСклад.</div>
</div>
<div class="field" style="max-width:360px;">
<div class="label">Поиск по товарам</div>
<input id="search" placeholder="название / код / CUSA / категория">
<div class="hint">Поиск работает по всему каталогу.</div>
</div>
<div class="field" style="max-width:260px;">
<div class="label">Категория</div>
<select id="catSelect"></select>
<div class="hint">Каталог ниже по категориям (свернут).</div>
</div>
</div>
<div class="row" style="margin-top:12px;">
<div class="field">
<div class="label">Комментарий к заказу</div>
<textarea id="comment" placeholder="например: просьба упаковать отдельно, уточнение по доставке и т.д."></textarea>
</div>
</div>
<div class="tools">
<div class="total" id="totalLine">Итого: 0 ₽</div>
<div>
<button class="btn btnGhost" id="clearBtn">Очистить количества</button>
<button class="btn btnPrimary" id="sendBtn">Отправить заказ</button>
</div>
</div>
<div style="margin-top:10px;display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;">
<div class="muted">Если обновили цены — обновите страницу.</div>
<div id="status" class="status"></div>
</div>
<div id="searchResults"></div>
<div class="catalog" id="catalog"></div>
</div>
</div>
</div>
<script>
// set date always
const d = new Date();
const dd = String(d.getDate()).padStart(2,'0');
const mm = String(d.getMonth()+1).padStart(2,'0');
const yyyy = d.getFullYear();
document.getElementById('dateLine').textContent = 'Прайс от ' + `${dd}.${mm}.${yyyy}`;
// price mode from url
const url = new URL(location.href);
const priceMode = (url.searchParams.get('price') || 'basic').toLowerCase();
let CATALOG = null;
let FILTER = '';
let SELECTED_CAT = 'ALL';
const $ = (id)=>document.getElementById(id);
$('search').addEventListener('input', e => { FILTER = (e.target.value||'').toLowerCase().trim(); render(); });
$('catSelect').addEventListener('change', e => { SELECTED_CAT = e.target.value || 'ALL'; render(); });
$('clearBtn').addEventListener('click', () => {
if(!CATALOG) return;
(CATALOG.items||[]).forEach(it=>it.qty=0);
render();
setStatus('', '');
});
$('sendBtn').addEventListener('click', submitOrder);
loadCatalog();
async function loadCatalog(){
setStatus('Загружаю каталог…','');
const r = await fetch(`/api/catalog?price=${encodeURIComponent(priceMode)}`);
const j = await r.json();
if(!j.ok){ setStatus(j.error||'Ошибка каталога','err'); return; }
CATALOG = j;
buildCategorySelect();
setStatus('', '');
render();
}
function buildCategorySelect(){
const sel = $('catSelect');
sel.innerHTML = '';
const oAll = document.createElement('option');
oAll.value = 'ALL';
oAll.textContent = 'Все категории';
sel.appendChild(oAll);
(CATALOG.categories||[]).forEach(c=>{
const o = document.createElement('option');
o.value = c;
o.textContent = c;
sel.appendChild(o);
});
sel.value = 'ALL';
SELECTED_CAT = 'ALL';
}
function matchesFilter(it){
if(!FILTER) return true;
const s = FILTER;
return (it.name||'').toLowerCase().includes(s)
|| (it.code||'').toLowerCase().includes(s)
|| (it.cusa||'').toLowerCase().includes(s)
|| (it.category||'').toLowerCase().includes(s);
}
function render(){
if(!CATALOG) return;
renderSearchResults();
renderAccordion();
calcTotal();
}
function renderSearchResults(){
const box = $('searchResults');
box.innerHTML = '';
if(!FILTER) return;
const results = (CATALOG.items||[]).filter(matchesFilter);
const wrap = document.createElement('div');
wrap.className = 'searchBox';
wrap.innerHTML = `
<div class="searchHead">
<div>Результаты поиска: ${results.length}</div>
<div class="muted">Поиск по всему каталогу. Очистите поиск — вернётесь к категориям.</div>
</div>
<div class="tableWrap" style="padding:0;">
<table>
<thead>
<tr>
<th style="width:54px;">№</th>
<th style="width:140px;">Категория</th>
<th style="width:120px;">Код</th>
<th>Наименование</th>
<th style="width:110px;">CUSA/PPSA</th>
<th class="rightAlign" style="width:110px;">Цена</th>
<th class="rightAlign" style="width:120px;">Кол-во</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
`;
const tbody = wrap.querySelector('tbody');
results.forEach((it, idx)=>{
const tr = document.createElement('tr');
tr.innerHTML = `
<td class="center">${idx+1}</td>
<td>${esc(it.category||'')}</td>
<td>${esc(it.code||'')}</td>
<td>${esc(it.name||'')}</td>
<td class="center">${esc(it.cusa||'')}</td>
<td class="rightAlign">${fmt(it.price)} ₽</td>
<td class="rightAlign"><input class="qty" type="number" min="0" step="1" value="${it.qty||''}" data-id="${it.id}"></td>
`;
tr.querySelector('input').addEventListener('input', onQty);
tbody.appendChild(tr);
});
box.appendChild(wrap);
}
function renderAccordion(){
const root = $('catalog');
root.innerHTML = '';
let items = (CATALOG.items||[]);
if(SELECTED_CAT !== 'ALL') items = items.filter(it => (it.category||'') === SELECTED_CAT);
const map = new Map();
(CATALOG.categories||[]).forEach(c=>map.set(c, []));
items.forEach(it=>{
const c = it.category || '';
if(!map.has(c)) map.set(c, []);
map.get(c).push(it);
});
(CATALOG.categories||[]).forEach(cat=>{
const list = map.get(cat) || [];
if(!list.length) return;
const details = document.createElement('details');
details.className = 'cat';
details.open = false;
const summary = document.createElement('summary');
summary.innerHTML = `<span>${esc(cat)}</span><span class="count">${list.length}</span>`;
details.appendChild(summary);
const wrap = document.createElement('div');
wrap.className = 'tableWrap';
wrap.innerHTML = `
<table>
<thead>
<tr>
<th style="width:54px;">№</th>
<th style="width:120px;">Код</th>
<th>Наименование</th>
<th style="width:110px;">CUSA/PPSA</th>
<th class="rightAlign" style="width:110px;">Цена</th>
<th class="rightAlign" style="width:120px;">Кол-во</th>
</tr>
</thead>
<tbody></tbody>
</table>
`;
const tbody = wrap.querySelector('tbody');
list.forEach((it, idx)=>{
const tr = document.createElement('tr');
tr.innerHTML = `
<td class="center">${idx+1}</td>
<td>${esc(it.code||'')}</td>
<td>${esc(it.name||'')}</td>
<td class="center">${esc(it.cusa||'')}</td>
<td class="rightAlign">${fmt(it.price)} ₽</td>
<td class="rightAlign"><input class="qty" type="number" min="0" step="1" value="${it.qty||''}" data-id="${it.id}"></td>
`;
tr.querySelector('input').addEventListener('input', onQty);
tbody.appendChild(tr);
});
details.appendChild(wrap);
root.appendChild(details);
});
}
function onQty(e){
const id = e.target.getAttribute('data-id');
const val = Number(e.target.value || 0);
const it = (CATALOG.items||[]).find(x => x.id === id);
if(it) it.qty = (val > 0 ? val : 0);
calcTotal();
}
function calcTotal(){
let total = 0;
(CATALOG.items||[]).forEach(it=>{
const q = Number(it.qty||0);
if(q>0) total += Number(it.price||0) * q;
});
$('totalLine').textContent = 'Итого: ' + fmt(Math.round(total)) + ' ₽';
}
async function submitOrder(){
if(!CATALOG) return;
const prevOrderNumber = ($('prevOrder').value || '').trim();
const comment = ($('comment').value || '').trim();
const lines = (CATALOG.items||[])
.filter(it => Number(it.qty||0) > 0)
.map(it => ({ id: it.id, qty: Number(it.qty||0) }));
if(!prevOrderNumber){ setStatus('Укажи номер предыдущего заказа','err'); return; }
if(!lines.length){ setStatus('Выбери количество хотя бы по одной позиции','err'); return; }
setStatus('Создаю заказ…','');
const r = await fetch('/api/order', {
method: 'POST',
headers: { 'Content-Type':'application/json' },
body: JSON.stringify({ priceMode, prevOrderNumber, comment, lines })
});
const j = await r.json();
if(!j.ok){ setStatus(j.error||'Ошибка','err'); return; }
setStatus(`Заказ создан: ${j.orderName} | Сумма: ${fmt(j.totalRub)} ₽`,'ok');
}
function setStatus(text, cls){
const el = $('status');
el.className = 'status ' + (cls || '');
el.textContent = text || '';
}
function fmt(n){ return Number(n||0).toLocaleString('ru-RU'); }
function esc(s){ return String(s||'').replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); }
</script>
</body>
</html>