Add AMD CPU support (CSV import + TechPowerUp scraper), fix alternatives accordion, untrack database.db
This commit is contained in:
@@ -162,6 +162,18 @@
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
||||
/* ── Alternatives ───────────────────────────────────────────── */
|
||||
.alt-accordion { border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 16px; overflow: hidden; transition: border-color .2s, box-shadow .2s; }
|
||||
.alt-accordion.has-deal { border-color: #f59e0b; box-shadow: 0 0 0 2px rgba(245,158,11,.18); }
|
||||
.alt-accordion-hdr { display: flex; align-items: center; gap: 8px; padding: 11px 14px; cursor: pointer; user-select: none; font-weight: 600; font-size: 13px; background: var(--surface); }
|
||||
.alt-accordion-hdr:hover { background: var(--card); }
|
||||
.alt-accordion.has-deal .alt-accordion-hdr { background: rgba(245,158,11,.08); }
|
||||
.alt-accordion-hdr .alt-hdr-icon { font-size: 14px; }
|
||||
.alt-accordion-hdr .alt-count { background: var(--accent); color: #fff; font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 10px; }
|
||||
.alt-accordion.has-deal .alt-accordion-hdr .alt-count { background: #f59e0b; }
|
||||
.alt-accordion-hdr .alt-chevron { margin-left: auto; font-size: 10px; color: var(--muted); transition: transform .2s; }
|
||||
.alt-accordion.open .alt-chevron { transform: rotate(180deg); }
|
||||
.alt-accordion-body { display: none; padding: 0 14px 12px; }
|
||||
.alt-accordion.open .alt-accordion-body { display: block; }
|
||||
.alt-group-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); margin: 14px 0 6px; }
|
||||
.alt-card { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); cursor: pointer; transition: border-color .15s; margin-bottom: 6px; }
|
||||
.alt-card:hover { border-color: var(--accent); }
|
||||
@@ -680,6 +692,8 @@ async function loadDetail(id) {
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="alt-section"></div>
|
||||
|
||||
<!-- Display -->
|
||||
<div class="detail-section">
|
||||
<h3>Display</h3>
|
||||
@@ -781,16 +795,15 @@ async function loadDetail(id) {
|
||||
${specItem('Eco Cert', l.eco_cert)}
|
||||
</div>
|
||||
</div>
|
||||
<div id="alt-section"></div>
|
||||
`;
|
||||
|
||||
$('detail-overlay').classList.add('open');
|
||||
$('detail-panel').classList.add('open');
|
||||
loadAlternatives(id);
|
||||
loadAlternatives(id, l.price_sek);
|
||||
}
|
||||
|
||||
// ── Alternatives panel ───────────────────────────────────────────────────
|
||||
async function loadAlternatives(id) {
|
||||
async function loadAlternatives(id, currentPrice) {
|
||||
const section = $('alt-section');
|
||||
if (!section) return;
|
||||
try {
|
||||
@@ -800,7 +813,13 @@ async function loadAlternatives(id) {
|
||||
const better = data.better_spec_same_price || [];
|
||||
if (!cheaper.length && !better.length) return;
|
||||
|
||||
const renderCard = (a, type) => {
|
||||
// Hot deal: any alternative is ≥15% cheaper, or any better-spec alternative exists
|
||||
const hasDeal = cheaper.some(a => currentPrice && a.price_sek <= currentPrice * 0.85)
|
||||
|| better.length > 0;
|
||||
const totalCount = cheaper.length + better.length;
|
||||
const icon = hasDeal ? '🔥 ' : '✦ ';
|
||||
|
||||
const renderAltCard = (a, type) => {
|
||||
const imgs = Array.isArray(a.image_urls) ? a.image_urls : [];
|
||||
const thumb = imgs[0]
|
||||
? `<img class="alt-card-thumb" src="${escHtml(imgs[0])}" loading="lazy">`
|
||||
@@ -829,17 +848,26 @@ async function loadAlternatives(id) {
|
||||
</div>`;
|
||||
};
|
||||
|
||||
let html = '<div class="detail-section"><h3>Alternatives</h3>';
|
||||
let cardsHtml = '';
|
||||
if (cheaper.length) {
|
||||
html += `<div class="alt-group-label">Same specs \u2014 lower price</div>`;
|
||||
html += cheaper.map(a => renderCard(a, 'cheaper')).join('');
|
||||
cardsHtml += `<div class="alt-group-label">Same specs \u2014 lower price</div>`;
|
||||
cardsHtml += cheaper.map(a => renderAltCard(a, 'cheaper')).join('');
|
||||
}
|
||||
if (better.length) {
|
||||
html += `<div class="alt-group-label">Same price \u2014 better specs</div>`;
|
||||
html += better.map(a => renderCard(a, 'better')).join('');
|
||||
cardsHtml += `<div class="alt-group-label">Same price \u2014 better specs</div>`;
|
||||
cardsHtml += better.map(a => renderAltCard(a, 'better')).join('');
|
||||
}
|
||||
html += '</div>';
|
||||
section.innerHTML = html;
|
||||
|
||||
section.className = 'alt-accordion' + (hasDeal ? ' has-deal open' : '');
|
||||
section.innerHTML = `
|
||||
<div class="alt-accordion-hdr" onclick="this.closest('.alt-accordion').classList.toggle('open')">
|
||||
<span class="alt-hdr-icon">${icon}</span>
|
||||
<span>Alternatives</span>
|
||||
<span class="alt-count">${totalCount}</span>
|
||||
<span class="alt-chevron">▼</span>
|
||||
</div>
|
||||
<div class="alt-accordion-body">${cardsHtml}</div>
|
||||
`;
|
||||
} catch (e) {
|
||||
console.error('Failed to load alternatives', e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user