Compare commits

..

2 Commits

2 changed files with 15 additions and 12 deletions

View File

@ -181,10 +181,10 @@ priceComparisonRouter.get("/:sku", async (req: Request, res: Response) => {
po.price,
po.currency,
po.stock_level,
-- Prefer stock_observations for latest stock info
-- Prefer stock_observations for latest stock info (in_stock is boolean)
COALESCE(
(
SELECT so.stock_level
SELECT CASE WHEN so.in_stock THEN 'in_stock' ELSE 'out_of_stock' END
FROM stock_observations so
WHERE so.transceiver_id = po.transceiver_id
AND so.source_vendor_id = po.source_vendor_id
@ -193,10 +193,11 @@ priceComparisonRouter.get("/:sku", async (req: Request, res: Response) => {
),
po.stock_level
) AS stock_level,
-- Build product URL: use vendor search_url_template if no direct url
-- Use direct product URL from price observation, fall back to vendor shop/website
COALESCE(
v.search_url_template,
v.website_url
po.url,
v.shop_url,
v.website
) AS url,
po.time AS observed_at
FROM (
@ -206,6 +207,7 @@ priceComparisonRouter.get("/:sku", async (req: Request, res: Response) => {
po.price,
po.currency,
po.stock_level,
po.url,
po.time
FROM price_observations po
WHERE po.transceiver_id = $1

View File

@ -1786,16 +1786,17 @@
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1.5rem">
<!-- Top Sellers -->
<div class="card" style="overflow:hidden">
<div style="padding:0.75rem 1rem;border-bottom:1px solid var(--border);font-size:0.85rem;font-weight:600;color:var(--text-bright)">🔥 Top Sellers (by units sold)</div>
<div style="padding:0.75rem 1rem;border-bottom:1px solid var(--border);font-size:0.85rem;font-weight:600;color:var(--text-bright);display:flex;align-items:center;gap:0.5rem">🔥 Top Sellers (by units sold) <span style="font-size:0.65rem;font-weight:700;background:#f59e0b22;color:#f59e0b;border:1px solid #f59e0b66;border-radius:3px;padding:1px 6px;letter-spacing:0.05em">DEMO DATA</span></div>
<div style="padding:0.35rem 1rem;background:#f59e0b11;border-bottom:1px solid #f59e0b33;font-size:0.7rem;color:#f59e0b">⚠ Verkauft-Zahlen sind synthetische Seed-Daten — keine echten Flexoptix-Verkaufszahlen</div>
<div style="overflow-x:auto">
<table style="width:100%;border-collapse:collapse;font-size:0.75rem" id="stock-top-sellers">
<thead>
<tr style="background:var(--surface2)">
<th style="padding:6px 8px;text-align:left;color:var(--text-dim);font-weight:500">Part Number</th>
<th style="padding:6px 8px;text-align:left;color:var(--text-dim);font-weight:500">Form Factor</th>
<th style="padding:6px 8px;text-align:right;color:var(--text-dim);font-weight:500">Verkauft</th>
<th style="padding:6px 8px;text-align:right;color:var(--text-dim);font-weight:500">DE-Lager</th>
<th style="padding:6px 8px;text-align:right;color:var(--text-dim);font-weight:500">Global-Lager</th>
<th style="padding:6px 8px;text-align:right;color:var(--text-dim);font-weight:500">Verkauft <span style="font-size:0.6rem;color:#f59e0b;opacity:0.8">[DEMO]</span></th>
<th style="padding:6px 8px;text-align:right;color:var(--text-dim);font-weight:500">DE-Lager <span style="font-size:0.6rem;color:#f59e0b;opacity:0.8">[DEMO]</span></th>
<th style="padding:6px 8px;text-align:right;color:var(--text-dim);font-weight:500">Global-Lager <span style="font-size:0.6rem;color:#f59e0b;opacity:0.8">[DEMO]</span></th>
<th style="padding:6px 8px;text-align:right;color:var(--text-dim);font-weight:500">Preis (net)</th>
</tr>
</thead>
@ -6696,9 +6697,9 @@ async function loadStock() {
return '<tr style="border-bottom:1px solid var(--border)">'
+ '<td style="padding:5px 8px">' + pn + '</td>'
+ '<td style="padding:5px 8px;color:var(--text-dim)">' + esc(r.form_factor || '—') + '</td>'
+ '<td style="padding:5px 8px;text-align:right;color:#f59e0b;font-weight:600">' + Number(r.units_sold || 0).toLocaleString() + '</td>'
+ '<td style="padding:5px 8px;text-align:right;color:#6366f1">' + (r.warehouse_de_qty != null ? Number(r.warehouse_de_qty).toLocaleString() : '—') + '</td>'
+ '<td style="padding:5px 8px;text-align:right;color:#06b6d4">' + (r.warehouse_global_qty != null ? Number(r.warehouse_global_qty).toLocaleString() : '—') + '</td>'
+ '<td style="padding:5px 8px;text-align:right;color:#f59e0b;font-weight:600">' + Number(r.units_sold || 0).toLocaleString() + ' <span style="font-size:0.6rem;opacity:0.6;font-weight:400">demo</span></td>'
+ '<td style="padding:5px 8px;text-align:right;color:#6366f1">' + (r.warehouse_de_qty != null ? Number(r.warehouse_de_qty).toLocaleString() + ' <span style="font-size:0.6rem;opacity:0.5;font-weight:400">demo</span>' : '—') + '</td>'
+ '<td style="padding:5px 8px;text-align:right;color:#06b6d4">' + (r.warehouse_global_qty != null ? Number(r.warehouse_global_qty).toLocaleString() + ' <span style="font-size:0.6rem;opacity:0.5;font-weight:400">demo</span>' : '—') + '</td>'
+ '<td style="padding:5px 8px;text-align:right">' + fmtPrice(r.price_net, r.price_currency) + '</td>'
+ '</tr>';
}).join('');