diff --git a/frontend/admin.html b/frontend/admin.html index 91d8689..4f9ba24 100644 --- a/frontend/admin.html +++ b/frontend/admin.html @@ -1339,18 +1339,28 @@ .shop-stat-v { font-family:'Unbounded',sans-serif; font-size:1.2rem; font-weight:800; } .shop-stat-v.sm { font-size:0.92rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .shop-stat-l { font-size:0.73rem; color:var(--text-3); font-weight:600; } - .shop-cell { display:flex; align-items:center; gap:11px; } - .shop-ic { width:34px; height:34px; border-radius:10px; display:flex; align-items:center; justify-content:center; flex-shrink:0; } - .shop-ic svg { width:17px; height:17px; } - .shop-cell-txt { display:flex; flex-direction:column; line-height:1.25; min-width:0; } - .shop-cell-sub { font-size:0.76rem; color:var(--text-3); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:300px; } - .shop-type-badge { display:inline-block; padding:3px 11px; border-radius:var(--r-pill); font-size:0.76rem; font-weight:700; white-space:nowrap; } - .shop-price { white-space:nowrap; font-weight:600; } - .shop-ic.t-frame, .shop-type-badge.t-frame { background:rgba(155,93,229,.12); color:var(--violet); } - .shop-ic.t-title, .shop-type-badge.t-title { background:rgba(255,179,71,.16); color:#d98a17; } - .shop-ic.t-background, .shop-type-badge.t-background { background:rgba(6,214,224,.12); color:#0891a3; } - .shop-ic.t-effect, .shop-type-badge.t-effect { background:rgba(6,214,160,.13); color:#089f7a; } - .shop-ic.t-other, .shop-type-badge.t-other { background:rgba(120,130,150,.14); color:var(--text-3); } + + .sh-group { margin-bottom:26px; } + .sh-group-h { display:flex; align-items:center; gap:9px; margin:0 0 13px; font-family:'Unbounded',sans-serif; font-size:0.82rem; font-weight:700; text-transform:uppercase; letter-spacing:0.06em; color:var(--text-2); } + .sh-group-c { font-family:'Manrope',sans-serif; font-size:0.72rem; font-weight:700; color:var(--text-3); background:var(--border-h); padding:1px 9px; border-radius:var(--r-pill); letter-spacing:0; } + .sh-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(185px, 1fr)); gap:14px; } + .sh-card { background:var(--surface); border:1px solid var(--border); border-radius:14px; padding:10px; display:flex; flex-direction:column; gap:9px; transition:border-color var(--tr), box-shadow var(--tr); } + .sh-card:hover { border-color:var(--violet); box-shadow:var(--shadow); } + .sh-card.off { opacity:0.5; } + .sh-prev { width:100%; aspect-ratio:16/10; border-radius:10px; display:flex; align-items:center; justify-content:center; overflow:hidden; position:relative; background:#f5f7fb; } + .sh-av { width:46px; height:46px; border-radius:50%; box-sizing:border-box; background:linear-gradient(135deg, rgba(155,93,229,0.55), rgba(6,214,224,0.42)); } + .sh-prev-title { font-family:'Unbounded',sans-serif; font-weight:800; font-size:0.92rem; letter-spacing:0.5px; text-transform:uppercase; padding:0 8px; text-align:center; line-height:1.15; } + .sh-fx-ic { color:var(--text-3); } + .sh-fx-ic svg { width:20px; height:20px; } + @keyframes sh-pulse { 0%,100%{box-shadow:0 0 0 0 rgba(155,93,229,0.5)} 50%{box-shadow:0 0 0 8px rgba(155,93,229,0)} } + .sh-av.fx-pulse { animation:sh-pulse 2s infinite; } + .sh-card-name { font-weight:700; font-size:0.9rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } + .sh-card-row { display:flex; align-items:baseline; justify-content:space-between; gap:8px; } + .sh-price { white-space:nowrap; font-weight:700; font-size:0.9rem; } + .sh-sold { font-size:0.74rem; color:var(--text-3); white-space:nowrap; } + .sh-card-foot { display:flex; align-items:center; justify-content:space-between; gap:8px; border-top:1px solid var(--border); padding-top:9px; } + .sh-acts { display:flex; gap:4px; } + .sh-empty { color:var(--text-3); padding:30px; text-align:center; }
Магазин
@@ -1367,14 +1377,7 @@ -
- - - - - -
ТоварТипЦенаПроданоАктивенДействия
-
+
diff --git a/frontend/js/admin/sections/shop.js b/frontend/js/admin/sections/shop.js index 54486e3..7637f63 100644 --- a/frontend/js/admin/sections/shop.js +++ b/frontend/js/admin/sections/shop.js @@ -7,10 +7,64 @@ let _filterType = ''; let _search = ''; - const TYPE_LABELS = { frame:'Рамка', title:'Титул', theme:'Тема', effect:'Эффект', background:'Фон' }; - const TYPE_ICONS = { frame:'square', title:'award', background:'image', effect:'sparkles', theme:'palette' }; - const typeCls = t => ['frame','title','background','effect'].includes(t) ? 't-' + t : 't-other'; + const TYPE_ICONS = { frame:'square', title:'award', background:'image', effect:'sparkles', theme:'palette' }; const typeIcon = t => TYPE_ICONS[t] || 'tag'; + const GROUP_ORDER = [ + { type: 'frame', label: 'Рамки' }, + { type: 'title', label: 'Титулы' }, + { type: 'background', label: 'Фоны' }, + { type: 'effect', label: 'Эффекты' }, + ]; + + function parseData(it) { + if (!it || it.data == null) return {}; + if (typeof it.data === 'object') return it.data; + try { return JSON.parse(it.data); } catch { return {}; } + } + + /* Real visual preview by item type (uses the same data the client renders). */ + function itemPreview(it) { + const d = parseData(it); + if (it.type === 'background') { + const slug = String(d.slug || 'none').replace(/[^a-z0-9_-]/gi, ''); + return `
`; + } + if (it.type === 'title') { + const color = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(d.color || '') ? d.color : '#9B5DE5'; + return `
${esc(d.text || it.name || '')}
`; + } + if (it.type === 'frame') { + const css = String(d.css || '').replace(/"/g, '"'); + return `
`; + } + if (it.type === 'effect') { + const fx = d.effect === 'pulse' ? ' fx-pulse' : ''; + const ic = d.effect === 'pulse' ? '' : ``; + return `
${ic}
`; + } + return `
`; + } + + function cardHTML(it) { + return `
+ ${itemPreview(it)} +
${esc(it.name)}
+
+ ${it.price} + продано: ${it.sold_count || 0} +
+
+ +
+ + +
+
+
`; + } async function load() { try { @@ -47,7 +101,7 @@ } function renderShopItems() { - const body = document.getElementById('shop-items-body'); + const wrap = document.getElementById('shop-items-body'); const countEl = document.getElementById('shop-count'); const filtered = _shopItems.filter(it => (!_filterType || it.type === _filterType) && @@ -56,32 +110,23 @@ if (countEl) countEl.textContent = filtered.length === _shopItems.length ? `${_shopItems.length} товаров` : `${filtered.length} из ${_shopItems.length}`; - if (!_shopItems.length) { body.innerHTML = 'Нет товаров'; return; } - if (!filtered.length) { body.innerHTML = 'Ничего не найдено'; return; } - body.innerHTML = filtered.map(it => ` - -
- - - ${esc(it.name)} - ${it.description ? esc(it.description) : '#' + it.id} - -
- - ${TYPE_LABELS[it.type] || esc(it.type)} - ${it.price} - ${it.sold_count || 0} - - - - - - - - `).join(''); + if (!_shopItems.length) { wrap.innerHTML = '
Нет товаров
'; return; } + if (!filtered.length) { wrap.innerHTML = '
Ничего не найдено
'; return; } + + const groups = []; + const seen = new Set(); + for (const g of GROUP_ORDER) { + const items = filtered.filter(it => it.type === g.type); + if (items.length) { groups.push({ label: g.label, items }); seen.add(g.type); } + } + const other = filtered.filter(it => !seen.has(it.type)); + if (other.length) groups.push({ label: 'Прочее', items: other }); + + wrap.innerHTML = groups.map(g => ` +
+
${g.label}${g.items.length}
+
${g.items.map(cardHTML).join('')}
+
`).join(''); if (window.lucide) lucide.createIcons(); }