diff --git a/packages/api/src/db/queries.ts b/packages/api/src/db/queries.ts index b0cf7e8..7c8481c 100644 --- a/packages/api/src/db/queries.ts +++ b/packages/api/src/db/queries.ts @@ -352,9 +352,20 @@ export async function getFlexoptixSuggestions(switchId: string) { CASE WHEN t.price_verified_eur IS NOT NULL THEN 'EUR' ELSE (SELECT po.currency FROM price_observations po WHERE po.transceiver_id = t.id ORDER BY po.time DESC LIMIT 1) - END AS latest_currency + END AS latest_currency, + so.warehouse_de_qty, + so.warehouse_global_qty, + so.backorder_qty, + so.backorder_estimated_date FROM transceivers t JOIN vendors v ON t.vendor_id = v.id + LEFT JOIN LATERAL ( + SELECT warehouse_de_qty, warehouse_global_qty, backorder_qty, backorder_estimated_date + FROM stock_observations + WHERE transceiver_id = t.id + ORDER BY time DESC + LIMIT 1 + ) so ON true WHERE LOWER(v.name) = 'flexoptix' AND t.form_factor IN ( SELECT form_factor FROM switch_form_factors WHERE form_factor IS NOT NULL diff --git a/packages/dashboard/index.html b/packages/dashboard/index.html index 1a89e0d..3bb04b1 100644 --- a/packages/dashboard/index.html +++ b/packages/dashboard/index.html @@ -4310,10 +4310,18 @@ async function openSwitchDetail(id) { + ''; fch += '
Passend für diesen Switch — FlexBox-Codierung möglich
'; + // Format speed_gbps → "1.6T", "400G", "100G" etc. + function fmtSpeed(gbps) { + if (!gbps) return '?'; + var n = parseFloat(gbps); + if (n >= 1000) return (n / 1000) + 'T'; + return Math.round(n) + 'G'; + } + // Group by speed class var foGroups = {}; foAll.forEach(function(t) { - var key = (t.speed_gbps ? t.speed_gbps + 'G' : (t.speed || '?')) + ' ' + (t.form_factor || '?'); + var key = fmtSpeed(t.speed_gbps) + ' ' + (t.form_factor || '?'); if (!foGroups[key]) foGroups[key] = []; foGroups[key].push(t); }); @@ -4342,10 +4350,27 @@ async function openSwitchDetail(id) { var shopHref = t.product_page_url || ('https://www.flexoptix.net/en/search/ajax/suggest/?q=' + encodeURIComponent(t.part_number || t.standard_name || '')); var reach = t.reach_label ? '' + esc(t.reach_label) + '' : ''; + // Stock badges + var stockHtml = ''; + var deQty = parseInt(t.warehouse_de_qty) || 0; + var glQty = parseInt(t.warehouse_global_qty) || 0; + var boQty = parseInt(t.backorder_qty) || 0; + if (deQty > 0 || glQty > 0 || boQty > 0) { + stockHtml += '
'; + if (deQty > 0) stockHtml += 'DE ' + deQty + ' Stk'; + if (glQty > 0) stockHtml += 'Global ' + glQty + ' Stk'; + if (boQty > 0) { + var boDate = t.backorder_estimated_date ? ' bis ' + new Date(t.backorder_estimated_date).toLocaleDateString('de-DE', {day:'2-digit',month:'2-digit'}) : ''; + stockHtml += 'Zulauf ' + boQty + boDate + ''; + } + stockHtml += '
'; + } + fch += '
' + '
' - + '' + esc(t.part_number || t.standard_name || t.slug) + '' - + reach + + '
' + esc(t.part_number || t.standard_name || t.slug) + '' + + reach + '
' + + stockHtml + '
' + (priceStr ? '' + priceStr + ''