-
+
+
+
@@ -493,20 +600,182 @@ el('search-btn').addEventListener('click', doSearch);
el('search-input').addEventListener('keydown', function(e) { if (e.key === 'Enter') doSearch(); });
// Hype Cycle
+var PHASE_COLORS = {
+ 'Innovation Trigger': '#3b82f6',
+ 'Peak of Inflated Expectations': '#f59e0b',
+ 'Trough of Disillusionment': '#ef4444',
+ 'Slope of Enlightenment': '#8b5cf6',
+ 'Plateau of Productivity': '#10b981'
+};
+
+function hypeCurveY(x, w, h) {
+ // Classic Gartner hype curve shape: rise, peak, trough, slope, plateau
+ var t = x / w;
+ if (t < 0.15) return h - (t / 0.15) * h * 0.85; // rise to peak
+ if (t < 0.22) return h * 0.15 + ((t - 0.15) / 0.07) * h * 0.02; // peak plateau
+ if (t < 0.42) return h * 0.17 + ((t - 0.22) / 0.20) * h * 0.55; // drop to trough
+ if (t < 0.48) return h * 0.72 - ((t - 0.42) / 0.06) * h * 0.02; // trough bottom
+ if (t < 0.80) return h * 0.70 - ((t - 0.48) / 0.32) * h * 0.35; // slope up
+ return h * 0.35 - ((t - 0.80) / 0.20) * h * 0.02; // plateau
+}
+
+function positionPctToX(pct, w) {
+ // Map positionPct (0-100) to x on the curve
+ return (pct / 100) * w;
+}
+
+function renderHypeSvg(techs) {
+ var W = 960, H = 320, PAD = 40;
+ var cw = W - PAD * 2, ch = H - PAD * 2;
+
+ // Build curve path
+ var pts = [];
+ for (var i = 0; i <= cw; i += 2) {
+ pts.push(i + PAD + ',' + (hypeCurveY(i, cw, ch) + PAD));
+ }
+
+ // Phase boundary x positions on the curve
+ var phases = [
+ { label: 'Innovation\\nTrigger', x: 0.07 },
+ { label: 'Peak of Inflated\\nExpectations', x: 0.18 },
+ { label: 'Trough of\\nDisillusionment', x: 0.42 },
+ { label: 'Slope of\\nEnlightenment', x: 0.64 },
+ { label: 'Plateau of\\nProductivity', x: 0.90 }
+ ];
+
+ var svg = '
';
+ return svg;
+}
+
+async function openHypeDetail(techName) {
+ var panel = el('hype-detail');
+ var content = el('hype-detail-content');
+ content.innerHTML = '';
+ content.textContent = 'Loading...';
+ panel.classList.add('open');
+
+ try {
+ var data = await api('/api/hype-cycle/' + encodeURIComponent(techName));
+ var color = PHASE_COLORS[data.phaseLabel] || '#6b7280';
+
+ var html = '
' + esc(data.technology) + '
';
+ html += '
' + esc(data.phaseLabel) + '
';
+
+ html += '
';
+ html += '
Adoption
' + (data.adoptionPct != null ? (data.adoptionPct * 100).toFixed(0) + '%' : '—') + '
';
+ html += '
Position
' + (data.positionPct || 0) + '%
';
+ html += '
Peak Year
' + esc(data.forecast && data.forecast.peakShipmentYear ? data.forecast.peakShipmentYear : '—') + '
';
+ html += '
Years to Plateau
' + (data.forecast && data.forecast.yearsToPlateauFromNow != null ? data.forecast.yearsToPlateauFromNow + 'y' : '—') + '
';
+ html += '
';
+
+ // Forecast projection bars
+ if (data.forecast && data.forecast.fiveYearProjection && data.forecast.fiveYearProjection.length > 0) {
+ html += '
5-Year Adoption Forecast
';
+ for (var fi = 0; fi < data.forecast.fiveYearProjection.length; fi++) {
+ var f = data.forecast.fiveYearProjection[fi];
+ var fColor = PHASE_COLORS[{
+ 'INNOVATION_TRIGGER': 'Innovation Trigger',
+ 'PEAK_OF_INFLATED_EXPECTATIONS': 'Peak of Inflated Expectations',
+ 'TROUGH_OF_DISILLUSIONMENT': 'Trough of Disillusionment',
+ 'SLOPE_OF_ENLIGHTENMENT': 'Slope of Enlightenment',
+ 'PLATEAU_OF_PRODUCTIVITY': 'Plateau of Productivity'
+ }[f.phase] || ''] || '#6b7280';
+ html += '
';
+ html += '
' + f.year + '';
+ html += '
';
+ html += '
' + (f.adoptionPct || 0) + '%';
+ html += '
';
+ }
+ }
+
+ // Composite score
+ html += '
';
+ html += '
Composite Score
';
+ html += '
' + (data.compositeScore || 0) + ' / 100
';
+ html += '
';
+
+ buildDOM(content, html);
+ } catch (err) {
+ content.textContent = 'Failed to load: ' + err.message;
+ }
+}
+
+el('hype-detail-close').addEventListener('click', function() {
+ el('hype-detail').classList.remove('open');
+});
+
async function loadHypeCycle() {
var data = await api('/api/hype-cycle');
- var phaseColors = {
- 'Innovation Trigger': '#3b82f6',
- 'Peak of Inflated Expectations': '#f59e0b',
- 'Trough of Disillusionment': '#ef4444',
- 'Slope of Enlightenment': '#8b5cf6',
- 'Plateau of Productivity': '#10b981'
- };
+ var techs = data.technologies || [];
el('hype-year').textContent = data.year;
- buildDOM(el('hype-table'), (data.technologies || []).map(function(t) {
- var color = phaseColors[t.phase] || '#374151';
- return '
'
+
+ // Render SVG
+ var container = el('hype-svg-container');
+ buildDOM(container, renderHypeSvg(techs));
+
+ // Attach click handlers to dots
+ var dots = container.querySelectorAll('.hype-dot');
+ for (var di = 0; di < dots.length; di++) {
+ dots[di].addEventListener('click', function() {
+ openHypeDetail(this.getAttribute('data-tech'));
+ });
+ }
+
+ // Also render table (rows are clickable too)
+ buildDOM(el('hype-table'), techs.map(function(t) {
+ var color = PHASE_COLORS[t.phase] || '#374151';
+ return '
'
+ '| ' + esc(t.technology) + ' | '
+ '' + esc(t.phase) + ' | '
+ ' | '
@@ -515,6 +784,14 @@ async function loadHypeCycle() {
+ '' + (t.yearsToPlateauFromNow != null ? t.yearsToPlateauFromNow + 'y' : '—') + ' | '
+ '
';
}).join(''));
+
+ // Click handlers on table rows
+ var rows = el('hype-table').querySelectorAll('tr[data-tech]');
+ for (var ri = 0; ri < rows.length; ri++) {
+ rows[ri].addEventListener('click', function() {
+ openHypeDetail(this.getAttribute('data-tech'));
+ });
+ }
}
// Transceivers