330 Commits

Author SHA1 Message Date
Rene Fichtmueller
5d69f0160c feat(procurement): data-grounded supply-availability signal per speed tier (1.6T scarce/800G moderate/400G abundant from real supplier+stock data) 2026-06-11 09:19:57 +00:00
Rene Fichtmueller
de40c65b01 feat(switch-compat): hard verification gate — only manufacturer-verified switches show compatibility
A switch presents transceiver compatibility ONLY if ports_verified=true (its port
config confirmed against the manufacturer datasheet). Unverified switches return
NO suggestions rather than potentially-wrong data. New columns: ports_verified,
ports_verified_source, ports_verified_at. Verified so far against datasheets:
HPE Aruba CX 10000-48Y6C, NVIDIA SN3700 (corrected 100G->200G QSFP56), SN4700,
SN5400, SN5600. 262 switches still pending datasheet verification.
2026-06-11 08:59:01 +00:00
Rene Fichtmueller
2dc21ee0aa fix(switch-compat): parse embedded port speed for chassis switches
Chassis switches use ports_config keys like 'max_per_slot_100G_QSFP28' (speed in
the middle), not just '100G_QSFP28'. The '^[0-9.]+G' anchor failed -> port_speed
NULL -> 0 suggestions (e.g. Cisco 8818 router). Now extracts the speed token
wherever it sits before the cage. Cisco 8818: 0 -> 55, all Cisco-codeable, max 400G.
2026-06-11 06:57:42 +00:00
Rene Fichtmueller
3bec30bad3 fix(switch-compat): require Flexoptix coding for the switch vendor (actually runs)
Physical fit alone is not real compatibility — a module that seats in the cage
still won't run unless Flexoptix can code it for the switch's platform. Added a
coding requirement: each suggestion must have an fx_compatibilities entry for the
switch's mapped vendor (HPE/Aruba->aruba, Cisco->cisco, NVIDIA->mellanox, etc.;
whitebox/unmapped -> MSA Standard fallback).

CX 10000-48Y6C: 629 physically-fitting -> 283 actually-codeable-for-Aruba. All
283 verified to carry an Aruba coding. Every suggestion is now catalog-confirmed,
datasheet-accurate, physically fitting AND guaranteed codeable to run in the switch.
2026-06-11 06:55:48 +00:00
Rene Fichtmueller
bd9d72d1bc fix(flexoptix-enricher): handle Terabit Ethernet rates in speed extraction
nominalSpeedFromSpecs missed '1.6T Ethernet' protocol entries (only matched 'G'),
returning 800 for twin-port 1.6T parts instead of 1600. Added Terabit handling +
guarded against 'Nx 800G' aggregate partial-matches. After re-deriving all 805
catalog-confirmed FX parts from their stored datasheet specs, DB now matches the
Flexoptix datasheet 100%: Form Factor 802/802, Speed 801/801, 0 deviations.
2026-06-11 06:49:49 +00:00
Rene Fichtmueller
7f13144c53 fix(switch-compat): only suggest catalog-confirmed Flexoptix parts (fx_specifications)
~107 parts were misattributed to the Flexoptix vendor but do not exist in the
live Flexoptix catalog (FX uses dot-SKUs like S.1606.28.KD; these use dash-SKUs
like FOT-AX-200G). They polluted 'BEI FLEXOPTIX BESTELLEN' with non-orderable
phantom parts carrying name-guessed specs. Now require fx_specifications IS NOT
NULL — every suggestion is confirmed in Flexoptix's API with authoritative
datasheet specs. CX 10000-48Y6C: 801 -> 630 suggestions, all catalog-confirmed,
0 physically impossible, 0 phantoms.
2026-06-11 05:36:44 +00:00
Rene Fichtmueller
436f62bc2b fix(flexoptix-enricher): source form_factor + speed_gbps from authoritative API specs
Root cause of unreliable speeds: the bulk Flexoptix products API returns NO speed
or form-factor field, so the catalog sync guessed them by parsing the product
NAME (e.g. 'FO-109010-CWDM' -> 100000G). Cryptic FX codes like 'O.164HG2.2.C:Sx'
are unparseable, producing garbage.

The detail enricher already pulls per-SKU specifications=1 (rate-limited) but only
wrote secondary fields. Now it also derives:
  form_factor <- structured 'Form Factor' spec (authoritative datasheet value)
  speed_gbps  <- highest Ethernet rate in 'Supported Protocols', fallback to the
                 'Bandwidth' line-rate upper bound mapped to nominal
Both OVERWRITE the corrupt bulk values (COALESCE(spec, existing)). Never derived
from the product name. Verified: 100/100 freshly-enriched FX parts now have
physically-consistent form_factor/speed (0 contradictions), incl. uncrackable
codes correctly resolved to OSFP/800G, QSFP-DD800/800G etc.
2026-06-11 05:19:48 +00:00
Rene Fichtmueller
2b7d5c7037 fix(switch-compat): physically validate Flexoptix suggestions by port speed + cage mechanics
getFlexoptixSuggestions matched ONLY by form factor, discarding the speed encoded
in each ports_config key (e.g. '100G_QSFP28'). Corrupt transceiver speed_gbps
values (400G/200G/128G/100000G mislabeled as QSFP28) leaked through, so a 100G
switch showed impossible '400G QSFP28' / '100T QSFP28' suggestions.

Now parses (speed, form_factor) from each port key and requires every suggested
module to (a) mechanically seat in the cage — precise port-FF -> accepted-module-FF
map, a QSFP28 cage takes QSFP+/28/56 but never QSFP-DD — and (b) have
speed_gbps <= the port's speed. CX 10000-48Y6C (25G SFP28 + 100G QSFP28) now
returns only valid <=25G SFP / <=100G QSFP modules; 0 physically impossible
entries (was 4 garbage groups). Belt-and-suspenders: even with corrupt speed data,
nothing oversized can reach a customer-facing suggestion.
2026-06-10 22:39:24 +00:00
Rene Fichtmueller
8c6df1028d merge: reconcile Erik-deployed fixes with Gitea feature branch (origin/main)
Merges 28 Gitea commits (Transceiver Academy, dashboard features A-P, equivalence
matchers OPN/spec, Abverkauf velocity engine, Flexoptix detail sync, SQL 111-118,
MCP equivalences tool, training data) with 22 Erik-local commits (price chart,
fmtSpd, supply-squeeze per-SKU fix, research-robot actions, NADDOD warehouse,
FS competitor stock, reorder bloat fix, flexoptix empty-token fix, vendor
reliability, blog WireGuard failover).

Only 2 files changed on both sides; ort auto-merged both cleanly. Manually
resolved a resulting duplicate GET /api/hype-cycle/market-signals: kept the
Erik handler (technologies+marketSignalScore+drivers+globalContext, matches the
merged dashboard), removed the Gitea Multi-source variant (preserved in history).

All 3 packages build with 0 TS errors. Safety: branch erik-pre-reconcile-20260607,
bundle _RECON_full-backup-20260607.bundle (also on Fearghas), bulk-price WIP in
stash@{0}, untracked SQL backed up to _RECON_untracked_sql_20260607/.
2026-06-07 05:01:32 +00:00
Rene Fichtmueller
87cd17808f fix(dashboard): Procurement-Pulse Buy-Signals card hit 404 endpoint
loadProcurementPulse called /api/procurement/reorder (does not exist) -> caught
-> always showed 0 Buy Signals. Correct route is /api/procurement/reorder-top,
which returns summary.buy_now. Fixed endpoint + count mapping (now shows 314).
2026-06-06 21:42:21 +00:00
Rene Fichtmueller
bb4046bb1e fix(reorder-signals): delete-before-insert prevents unbounded table growth
reorder_signals grew to 4.49M rows / 1.19GB — the compute job INSERTed a fresh
row per transceiver every 4h run but never deleted old ones (24h TTL filtered
them at read time via DISTINCT ON + expires_at, but they were never purged).
4.37M rows were already expired dead weight.

Fix: DELETE existing rows for a transceiver before inserting its new signal, so
the table holds exactly one (latest) row per transceiver. Cleaned up to 18,175
rows / 4.5MB (99.6% reclaimed, VACUUM FULL). Backup: reorder_signals_keep_bak_20260606.
Verified: re-running compute:reorder-signals keeps count stable at 18,175.
2026-06-06 21:39:57 +00:00
Rene Fichtmueller
2432c0ddfc fix(flexoptix-sync): empty FLEXOPTIX_API_TOKEN string short-circuited bearer login
Root cause of the persistent sync:flexoptix-catalog HTTP 401: line 397 used
'?? null' which only coerces null/undefined. With FLEXOPTIX_API_TOKEN='' (empty
string set in .env), token stayed '' and line 485's 'token ?? getBearerToken()'
returned '' instead of performing the username/password login — sending an empty
'Bearer ' header that the products endpoint rejected with 401.

Fix: '|| null' coerces empty string to null so the bearer-login fallback fires.
Verified: sync now completes (username/password -> customer token -> products 200,
3 products/price/stock writes on limit=50). Credentials were correct all along.
2026-06-06 17:06:22 +00:00
Rene Fichtmueller
a9f95fc552 feat(research-robot): actionable recommendations + dispatch/pause/resume buttons
The Research Robot panel showed only an LLM assessment (info, no action). Now:

API (research-robot.ts):
- GET enriches response with recommendations[] computed from live pgboss state:
  classifies each persistently-failing job (auth/401, network, no-handler, other)
  into severity + concrete advice + offered actions.
- POST /action {action,job}: dispatch (enqueue one run), pause (remove from
  schedule with backup to research_robot_paused_schedules), resume (restore).
  All validated against the pgboss.queue whitelist.

Dashboard:
- Renders each recommendation as a card with severity colour, cause, last error,
  and action buttons (Jetzt auslösen / Pausieren / Fortsetzen / Token-Anleitung).
- Verified: sync:flexoptix-catalog -> critical auth (HTTP 401), offers
  token-help + pause. Dispatch/pause/resume roundtrip tested green.
2026-06-06 16:53:54 +00:00
Rene Fichtmueller
0cf607040f fix(supply-squeeze): per-SKU paired price comparison eliminates catalog-composition bias
The 30d-vs-60d price momentum aggregated AVG/median across whatever SKUs
happened to be in a speed/form-factor bucket each period. New expensive SKUs
entering the catalog (NVIDIA switches at 30k USD, AOC cables) faked huge jumps
— 400G OSFP showed +151% when matched-SKU reality was 0%.

Now: compute per-transceiver median price in each period, keep only SKUs present
in BOTH periods (>=2 obs each), report the median of per-SKU pct deltas. Also
excludes non-transceiver form factors, AOC/DAC cables, switch SKUs, price>15k,
and anomalous observations. Result: 400G OSFP +151%->0%, signals 21->8, and the
ones that remain (NVIDIA MFA7U10 +84% same-SKU) are genuine price moves.
2026-06-06 16:48:15 +00:00
Rene Fichtmueller
03fdfa7d51 feat(naddod-scraper): extract per-warehouse stock breakdown + write warehouse_global_qty
Adds parseWarehouseStock() to decode the HTML-entity-encoded warehouse_stock JSON
(us/nl/sg/cn per-region array). When the static page has warehouse data, writes:
  warehouse_de_qty   ← nl (EU-closest warehouse)
  warehouse_global_qty ← sum(us+nl+sg+cn), or falls back to quantity_available
  stock_confidence   ← 3 (L3) when warehouse breakdown available, else 2

Note: per-warehouse quantities require JS execution to populate (API-loaded);
static HTML has [0,0] placeholders. The fallback ensures NADDOD global totals
appear in the competitor-by-tech dashboard comparison.
2026-06-06 16:34:02 +00:00
Rene Fichtmueller
9bf7da3fda feat(stock): FS.COM competitor stock by technology in Sales Velocity table
Adds /api/stock/competitor-by-tech endpoint aggregating warehouse_de_qty +
warehouse_global_qty from stock_observations for public competitors (FS.COM
etc.) per technology class. Dashboard velocity table gets two new columns
FS.COM DE + FS.COM Global with traffic-light coloring vs. monthly demand.
2026-06-05 23:06:13 +00:00
Rene Fichtmueller
261135c544 fix(detail-panel): show Flexoptix price in competitor comparison + fix 800.00G->800G 2026-06-05 22:55:15 +00:00
Rene Fichtmueller
0d3edd6ea9 feat+fix(dashboard): price chart + fmtSpd speed display (stable) 2026-06-05 22:49:09 +00:00
Rene Fichtmueller
8b232d942d feat+fix(dashboard): price chart on signal-card click + fmtSpd speed display cleanup 2026-06-05 22:48:13 +00:00
Rene Fichtmueller
8432a44574 fix(dashboard): fmtSpd() all speed displays -- 1.00G->1G, 400.00G->400G, 1600->1.6T 2026-06-05 22:38:52 +00:00
Rene Fichtmueller
f2b26e3286 fix(dashboard): clean speed display via fmtSpd() — 1.00G→1G, 400.00G→400G, 1600→1.6T 2026-06-05 22:38:13 +00:00
Rene Fichtmueller
e103a99822 feat(dashboard): price history chart on signal-card click
Clicking any signal card opens a modal with a 180-day SVG line chart
per source vendor (multi-line, colour-coded), x-axis dates, y-axis price,
current best price summary. Uses existing /api/price-history/:id endpoint.
No external chart library — pure inline SVG.
2026-06-05 22:36:31 +00:00
Rene Fichtmueller
842a85120b fix(price-movers): dedup by part_number, median price, CV-filter for tier noise
Group by part_number instead of transceiver_id (eliminates OEM duplicate rows).
Use PERCENTILE_CONT median instead of AVG to reduce single-outlier impact.
Add CV-filter (stddev/avg <= 0.35 over 2x window) to exclude high-variance
sources like Mouser quantity-tier pricing that produces artificial swings.
2026-06-05 21:23:20 +00:00
Rene Fichtmueller
c6e79e9967 feat(vendors): add /reliability endpoint (per-vendor freshness/frequency/coverage scores from real data) 2026-06-04 21:08:08 +00:00
Rene Fichtmueller
f067c0999d fix(vendors): reach /market-share + /intelligence (next() fallthrough past /:id) + fix SQL (numeric casts for ROUND division, COUNT(*) not non-existent po.id) 2026-06-04 20:58:12 +00:00
Rene Fichtmueller
b720afc92c feat(hype-cycle): add /market-signals endpoint (data-driven per-tech signal score + drivers + recommendation) 2026-06-04 20:47:30 +00:00
Rene Fichtmueller
b5a961c3bd feat(blog): automatic primary->fallback failover for Ollama endpoint
Blog LLM client probes BLOG_OLLAMA_URL (primary, WireGuard tunnel to Mac
Studio loopback Ollama) and falls back to BLOG_OLLAMA_URL_FALLBACK
(Cloudflare tunnel) when the primary transport is unreachable. Re-probed
at startup and every 60s; prefers primary when available. Both tunnels
terminate on the Mac loopback over independent transports, so the blog
keeps reaching fo-blog regardless of which transport drops.
2026-06-04 18:28:45 +00:00
Rene Fichtmueller
3577668cf3 feat(blog): route fo-blog LLM to Mac Studio via WireGuard
Blog auto-discovery + generation now use BLOG_OLLAMA_URL (-> Mac Studio
192.168.178.213:11434 over the Erik<->home WireGuard tunnel), falling
back to OLLAMA_URL. Search/embeddings stay on Erik-local OLLAMA_URL
(nomic-embed-text). Fixes blog model not-found after OLLAMA_URL was
repointed to Erik-local for the search fix.
2026-06-04 18:02:01 +00:00
Rene Fichtmueller
b5925bc264 Merge remote-tracking branch 'origin/erik-live-2026-06-04' into reconcile-2026-06-04
# Conflicts:
#	CHANGELOG_PENDING.md
#	packages/api/src/index.ts
#	packages/api/src/llm/client.ts
#	packages/api/src/routes/blog.ts
#	packages/api/src/routes/bulk-price.ts
#	packages/api/src/routes/kb.ts
#	packages/api/src/routes/price-matrix.ts
#	packages/api/src/routes/procurement.ts
#	packages/api/src/routes/stock.ts
#	packages/api/src/routes/vendor-reliability.ts
#	packages/api/src/routes/vendors.ts
#	packages/dashboard/index.html
2026-06-04 13:56:42 +00:00
Rene Fichtmueller
172ec324f2 snapshot: Erik live-deployed state 2026-06-04 (capture for source reconciliation) 2026-06-04 12:46:49 +00:00
Rene Fichtmueller
aa6ce9cc26 fix(api): switch-compat vendor join + min_price aggregate + win-loss form_factor ambiguity 2026-06-04 10:38:15 +00:00
Rene Fichtmueller
f2dad45c7c fix(api): part-number ILIKE search + verified-first catalog ordering + FTS-primary product search 2026-06-04 10:14:19 +00:00
Rene Fichtmueller
d6da7aa94c fix(api): part-number ILIKE search + verified-first catalog ordering + FTS-primary product search 2026-06-04 10:11:37 +00:00
Rene Fichtmueller
f81b67860b fix: buildDOM calls in Academy use el() wrapper instead of raw string IDs 2026-05-14 23:06:49 +02:00
Rene Fichtmueller
9b563b0378 fix: remove Selflearning tab from nav 2026-05-14 22:53:12 +02:00
Rene Fichtmueller
eb954aab2e fix: move Academy tab after Standards for visibility 2026-05-14 22:51:54 +02:00
Rene Fichtmueller
91b96a1e03 feat: promote Transceiver Academy to own main nav tab
Move Academy from hidden Standards sub-tab to a dedicated
top-level tab '🎓 Academy' in the main navigation bar.

- Add <div class="tab" data-tab="training"> to nav
- Create standalone <div id="tab-training"> with full Academy HTML
- Wire initTraining() into goToTab() handler
- Remove std-subtab-training skeleton from Standards section
- Remove training button from Standards sub-tab bar
- Update switchStdSubtab() to only handle standards/formfaktoren
2026-05-14 22:38:42 +02:00
Rene Fichtmueller
adfb590ad2 feat: Transceiver Academy — full API-backed customer & employee training platform
Replaces the old LLM-training inline data module with a proper interactive
training platform for annual employee onboarding and customer education.

Content (236 KB of structured training data):
- 5 categories: Standards, Form Factors, Switches & Compatibility,
  Fiber & Infrastructure, Testing & Buying
- 22 detailed lessons with bilingual content (EN + DE)
- 74 quiz questions with explanations in both languages
- Lesson types: beginner / intermediate / advanced
- Content blocks: paragraphs, tables, callouts, code blocks, formulas, lists

API route (GET /api/training/*):
- /categories — all 5 categories with lesson/quiz counts
- /lessons?category= — lesson metadata for category
- /lessons/:id — full lesson content (sections + blocks)
- /quiz?lesson=&category= — quiz questions with bilingual answers
- /stats — aggregate stats
- Public route (no auth token required)

Dashboard (Transceiver Academy UI):
- Language toggle EN/DE (persisted in localStorage)
- Category selector tabs with lesson counts
- Lesson cards with level badge, summary, duration, completion indicator
- Full lesson viewer: renders all block types with bilingual support
- Per-lesson quiz and per-category quiz
- Question-by-question quiz engine with auto-advance, dots progress indicator
- Results screen with grade (A-F), wrong answers + explanations
- Progress tracking in localStorage, global progress bar
- Reset progress button
2026-05-14 22:31:43 +02:00
Rene Fichtmueller
ae94fc8f47 feat: Training Module im Standards-Tab (Lektionen, Quiz, Lernpfade)
- 3. Subtab "Trainings" im Standards-Bereich mit Fortschrittsbalken
- 13 Lektionen: Form Factors, Glasfaser, IEEE 802.3, WDM, PAM4/NRZ,
  Link Budget (Live-Rechner), Coherent Optics, MSA/DOM, Vendor Locking,
  Temperature Classes, Selection Guide, 400G/800G, Troubleshooting
- 40 Quiz-Fragen mit Erklaerungen, Shuffle, Feedback pro Antwort,
  Falsch-Antworten-Review und Note (A-F)
- 4 Lernpfade: Einsteiger (5), Netzwerk-Engineer (9), Einkaeufer (6),
  Expert (alle 13)
- Lernfortschritt in localStorage (tip_training_progress)
- Live Link-Budget-Rechner eingebettet in Lektion linkbudget
2026-05-14 21:52:23 +02:00
Rene Fichtmueller
e71b985c52 feat: 10 weitere Dashboard-Features (G–P)
G) SKU Bulk Pricer — POST /api/bulk-price (bis 100 Part Numbers),
   Preistabelle je Vendor, CSV-Export, Not-Found-Liste

H) Side-by-side Comparison — Checkboxes in TX-Tabelle,
   Floating Comparison-Tray (max 4 SKUs), Modal mit Specs +
   Best-Price 7d nebeneinander

I) Vendor Reliability Score — GET /api/vendors/reliability,
   Freshness(40) + Frequency(30) + Coverage(30) = 0–100,
   Progress-Bar-Badge auf Vendor-Cards

J) Price Heat Map — GET /api/price-matrix?ids=,
   Row-normalisierte Farbmatrix SKU×Vendor (grün=günstig/rot=teuer),
   Sticky SKU-Spalte, Best-Price-Spalte

K) Watchlist — localStorage-basiert (/☆ in TX-Tabelle),
   Floating Drawer, live Preis-Update via Einzelabruf

L) PDF / Print Report — window.print() + dediziertes @media print CSS
   (blendet UI-Chrome aus, behält Overview-Content)

M) Global Search Overlay — Cmd+K / Ctrl+K, durchsucht Transceivers
   + KB + News + Documents gleichzeitig, clickbare Direktlinks

N) Saved Filter Presets — localStorage tip_presets, Dropdown +
   💾-Button in TX-Filterzeile, Save/Load/Delete

O) Price Forecast — GET /api/price-forecast/:id (lineare Regression
   90d → 30d Forecast), gestricheltes Overlay auf Price-History-Chart,
   Trend-Label (rising/stable/declining)

P) Technology Radar — SVG Bull's-Eye (Adopt/Trial/Assess/Hold),
   Hype-Cycle-Phasen → Ringe gemappt, Bubbles mit Market-Signal-Score,
   Quadrant-Labels, interaktive Tooltips
2026-05-14 21:39:17 +02:00
Rene Fichtmueller
fb060ee40a feat: 6 neue Dashboard-Features (A–F)
A) Price Movers Alert
   - GET /api/procurement/price-movers?days=N&limit=N
   - CTE cur vs prior period avg, |delta_pct| >= 2%, gainers+losers
   - Procurement tab: period toggle 7d/14d/30d, stats bar, Export CSV

B) Executive Overview Pulse
   - 5 KPI cards in Overview (Buy Signals, Arbitrage Ops, Supply Alerts,
     Price Gainers, Losers) via loadProcurementPulse()
   - Top-Movers mini-table in overview card, all → Procurement tab

C) CSV Export
   - exportMoversCSV() downloads gainers+losers as CSV

D) Vendor Intelligence
   - GET /api/vendors/intelligence: per-vendor 30d stats
     (sku_count, price_obs, avg/min/max price, last_seen)
   - Top-6 banner in Vendors tab

E) Advanced Transceiver Search
   - Speed filter (1G/10G/25G/40G/100G/200G/400G/800G)
   - Fiber type filter (SMF / MMF)
   - Verified-only checkbox
   - All params forwarded to GET /api/transceivers

F) Knowledge Base Browser
   - New KB tab with full-text search (ILIKE question/answer/subcategory)
   - GET /api/kb?q=&category=&limit= (packages/api/src/routes/kb.ts)
   - Category pills, entry cards with severity badge + FF/speed tags
2026-05-14 20:54:40 +02:00
Rene Fichtmueller
d7c1c351fe feat(dashboard): interactive price history chart with hover tooltip
Replace 260×60 sparkline with full 520×200 SVG line chart:
- Multi-vendor colored polylines (up to 8, MAGATAMA indigo palette)
- USD-normalized prices (EUR×1.08, GBP×1.27)
- Y/X axes with grid lines, date labels, price labels
- Hover vertical cursor + floating tooltip per-day vendor prices
- Click-to-toggle vendor legend
- 7d / 14d / 30d time range selector with live API re-fetch
- Current best prices table below the chart
- End-point dots per vendor line
2026-05-14 20:26:01 +02:00
Rene Fichtmueller
bcab2b97af feat: procurement — 5 intelligence sections (A-E)
E  Buy-Now Intel    211k precomputed reorder signals surfaced,
                    filterable by form factor, signal strength bars
A  Arbitrage        59k equivalence pairs + price data, FX vs comp
                    normalized to USD, sorted by savings %
B  Switch Compat    search 429 switches → compatible transceivers
                    with prices; 58k compatibility rows
C  Supply Squeeze   4-signal detector: price momentum (30d vs 60d),
                    hype phase, AI cluster demand, stock pressure
D  Dead Stock       7,297 dead-stock SKUs matched against ascending
                    hype phases (revival candidates)

5 new API endpoints: /api/procurement/reorder-top, /arbitrage,
/switch-compat, /supply-squeeze, /dead-stock-revival
2026-05-14 18:31:07 +02:00
Rene Fichtmueller
4bd16af9a5 feat: data quality panel in Crawler Intelligence tab
GET /api/scrapers/data-quality — 4 parallel queries across 200k+
transceiver_verification_evidence rows. Returns: coverage percentages
(price 62%, image 68%, details 94%, competitor 2%), all 10 evidence
types with counts + avg confidence, 17 robot/scraper contributions,
14-day daily activity time series.

Dashboard: coverage progress bars (color-coded thresholds), evidence
type table, SVG activity sparkline, robot contributions table.
2026-05-14 16:22:25 +02:00
Rene Fichtmueller
10d13633fb feat: dynamic hype cycle + market signal engine + eBay/CapEx panels
6-source composite Market Signal Score (0-100) per transceiver technology.
New GET /api/hype-cycle/market-signals blends: Norton-Bass hype_score,
hyperscaler CapEx YoY (MSFT +68.8%, GOOG +107%, META +46.8%), price
observation activity ratio 30d vs prior 30d, AI cluster transceiver demand,
eBay secondary market sell-through velocity, internal fast-mover trend.
All 6 queries run in parallel via Promise.all().

Recommendation engine maps hype phase × capex boom × speed class →
Buy/Hold/Watch labels with tooltips. Dashboard Hype Cycle table now shows
Market Signal ● LIVE column + Recommendation column. Hyperscaler CapEx
panel + eBay panel added to hype tab. Procurement: new eBay Market section.
Sourcing Hype Cycle replaced hardcoded seed with live price observation data.
2026-05-14 16:17:52 +02:00
Rene Fichtmueller
13fe33eceb feat: procurement — Internal Demand + AI Clusters sections with real data
Two new procurement sub-tabs backed by live database tables:

📦 Internal Demand (flexoptix_internal_demand, 8,585 SKUs):
- Velocity cards: fast_mover (70 SKUs, 53k units/12M), regular, slow, dead stock
- Filterable table with demand_12m, demand_3m, trend %, form factor
- GET /api/procurement/internal-demand — summary + paginated rows

🤖 AI Clusters (ai_cluster_announcements, 396 rows last 30d):
- Live datacenter build announcements with estimated transceiver demand
- Stats: total announcements, MW sum, distinct companies, total ~transceivers
- Filter for entries with transceiver estimates; time range selector
- GET /api/procurement/ai-clusters — data + period stats

Also: replaced misleading DEMO DATA banners on Reorder Signals and ABC
Classification sections with informational notes pointing to real data.
2026-05-14 16:04:11 +02:00
Rene Fichtmueller
ea8be4aea3 feat(tip): equivalences explorer + price history charts + linkedin status + MCP tools
Equivalences Explorer:
- GET /api/equivalences — search 63k cross-brand mappings by part number/vendor
- GET /api/equivalences/transceiver/:id — all equivalences for a specific product
- GET /api/equivalences/stats — active count, unique products, avg confidence (93.9%)
- GET /api/equivalences/top-vendors — top 20 competitor vendors by coverage
- New "Equivalences" tab in dashboard with part-number search, vendor filter,
  quick-click vendor chips, and results table with confidence coloring
- Transceiver detail modal: equivalences panel (Flexoptix alternatives or competitor
  products), clickable rows, confidence percentage, orange highlight for FX products

Price History Charts:
- GET /api/price-history/:id?days=90 — daily min/max/avg per source vendor (392k obs)
- Transceiver detail modal: SVG sparkline chart per vendor, legend with latest prices,
  range summary — loads async without blocking the modal

LinkedIn Distribution Status:
- GET /api/blog/linkedin/history — from blog_linkedin_distribution table
- Blog tab: LinkedIn status panel showing DRY_RUN badge, posted/dry_run/skipped/failed
  stats, distribution history table with URN link to live posts

MCP Server — 2 new tools:
- find_equivalences: search 63k+ verified cross-brand mappings with confidence filter
- get_price_history: 392k+ observations, daily series, per-vendor analysis, cheapest source
2026-05-14 15:54:01 +02:00
Rene Fichtmueller
67310c8fe7 fix(blog): SPA-aware URL blog generation + dynamic generated_by
- fetchUrlContent() now extracts OG/meta tags (og:title, og:description,
  name="description", og:site_name) as fallback content for JS-rendered SPAs
- Returns spaDetected=true when body text < 300 chars after stripping scripts
- from-url endpoint skips gatherBlogData() product injection when SPA detected,
  preventing fo-blog-v10 from defaulting to optical networking domain
- additionalContext now includes SPA warning instructing LLM not to default
  to optical transceiver topics unless the page is actually about that
- generated_by in pipeline UPDATE query now uses active model name instead of
  hardcoded 'fo-blog-engine-v7' (reads getLlmProvider().ollamaModel)
- Dashboard shows SPA warning toast when spa_detected=true in response
- Response now includes spa_detected field for client awareness
2026-05-14 12:29:17 +02:00
Rene Fichtmueller
e0f9656684 feat: Blog Engine — generate from URL (link → BlogLLM → article)
New POST /api/blog/from-url endpoint:
- Accepts url + topic in request body
- Fetches page server-side (no CORS, 20s timeout, redirect-follow)
- Strips script/style/nav/footer/svg; extracts readable text (~5000 chars)
- Extracts page title from <title> or <h1>
- Passes extracted content as structured additional_context to the
  existing 16-step FO blog pipeline (same as manual generation)
- Returns immediately; LLM pipeline runs async
- Validated: smoke test fetched flexoptix.net/en/blog/, 5040 chars,
  pipeline launched with llm_enhancing=true

New "🔗 Blog aus URL generieren" panel in dashboard:
- URL input (Enter key triggers generation)
- Blog-Typ dropdown (same 8 types as manual panel)
- Button shows loading state " Fetching…" during API call
- Status line shows extracted char count after success
- Reuses pollBlogLlm() for step-by-step progress polling
- Inline status field for error display without toast spam
2026-05-14 00:55:35 +02:00
Rene Fichtmueller
9b8b03e783 feat: Flexoptix section — speed formatting + Lagerbestand display
Speed display: fix raw Gbps decimals → formatted labels
- 1600.00G → 1.6T (≥1000 Gbps converted to T)
- 400G → 400G (clean integer, no trailing .00)
- Helper function fmtSpeed() added in dashboard JS

Lagerbestand: add stock availability per transceiver
- getFlexoptixSuggestions() extended with LEFT JOIN LATERAL on
  stock_observations (latest row per transceiver)
- Returns warehouse_de_qty, warehouse_global_qty, backorder_qty,
  backorder_estimated_date
- Dashboard renders color-coded badges per row:
    green  = DE-Lager quantity
    blue   = Global-Lager quantity
    yellow = Zulauf with estimated delivery date if available
- Badges hidden when all quantities are null/zero (graceful fallback)
2026-05-14 00:52:21 +02:00