From 1ba598e7ae8a27170eec453feda55bff4ae4db82 Mon Sep 17 00:00:00 2001 From: Rene Fichtmueller Date: Thu, 11 Jun 2026 10:35:37 +0000 Subject: [PATCH] feat(dashboard): availability-by-speed panel in Supply Squeeze (class + price trend + why per tier) --- packages/dashboard/index.html | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/dashboard/index.html b/packages/dashboard/index.html index 5cf9187..8287f5c 100644 --- a/packages/dashboard/index.html +++ b/packages/dashboard/index.html @@ -8129,12 +8129,51 @@ async function loadSwitchCompat() { } /* ── C: Supply Squeeze Detector ────────────────────────────────────────── */ + +/* ── Verfügbarkeit pro Speed-Tier (mit Warum-Begründung) ────────────────── */ +async function renderAvailabilityPanel() { + var host = el('proc-squeeze-summary'); + if (!host) return; + try { + var d = await api('/api/procurement/availability'); + var tiers = (d && d.tiers) || []; + if (!tiers.length) return; + var meta = { + scarce: { c:'#ef4444', bg:'rgba(239,68,68,0.10)', lbl:'KNAPP' }, + constrained: { c:'#f97316', bg:'rgba(249,115,22,0.10)', lbl:'eng' }, + moderate: { c:'#f59e0b', bg:'rgba(245,158,11,0.10)', lbl:'moderat' }, + abundant: { c:'#22c55e', bg:'rgba(34,197,94,0.10)', lbl:'breit' } + }; + function spd(g){ return g>=1000 ? (g/1000)+'T' : g+'G'; } + var html = '
' + + '
📦 Verfügbarkeit nach Geschwindigkeit — aus Anbieter-Diversität, Stock-Coverage & Preis-Momentum (echte Daten)
' + + '
'; + tiers.forEach(function(t) { + var m = meta[t.availability] || meta.moderate; + var pt = t.price_trend, pd = t.price_delta_pct; + var priceTag = pt==='rising' ? '▲ +'+pd+'%' : pt==='falling' ? '▼ '+pd+'%' : '→ stabil'; + html += '
' + + '
' + + ''+spd(t.speed_gbps)+'' + + ''+m.lbl+'' + + '
' + + '
'+t.suppliers+' Anbieter · '+(t.in_stock_pct!=null?t.in_stock_pct+'% stock':'k.A.')+' · '+priceTag+'
' + + '
'+esc((t.why||'').charAt(0).toUpperCase()+(t.why||'').slice(1))+'
' + + '
'; + }); + html += '
'; + // prepend to summary (before the squeeze severity badges, which get set after) + host.insertAdjacentHTML('afterbegin', html); + } catch(e) { /* silent */ } +} + async function loadSupplySqueeze() { var token = (window.loadToken ? window.loadToken() : '') || ''; var summEl = el('proc-squeeze-summary'); var listEl = el('proc-squeeze-list'); if (listEl) listEl.innerHTML = '
Analysiere Preis- & Nachfragesignale…
'; try { + renderAvailabilityPanel(); var r = await fetch('/api/procurement/supply-squeeze', { headers: { 'Authorization': 'Bearer ' + token } }); var d = await r.json(); if (!d.success) throw new Error(d.error);