Skip to content

Commit 5bf7cb8

Browse files
Add Trends tab with 5 temporal visualizations: Stream Timeline, Cumulative Growth, Journal Distribution, Emerging Topics bubble chart, and Activity Heatmap
1 parent 0fddd6e commit 5bf7cb8

1 file changed

Lines changed: 311 additions & 1 deletion

File tree

ISR-submission/dashboard/dashboard.html

Lines changed: 311 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>IS Research Streams Explorer | 2000-2025</title>
7+
<!-- Chart.js for interactive charts -->
8+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
79
<style>
810
* {
911
margin: 0;
@@ -439,9 +441,10 @@ <h1>🔬 IS Research Streams Explorer</h1>
439441
<button class="tab active" onclick="showPanel('overview')">📊 Overview</button>
440442
<button class="tab" onclick="showPanel('explorer')">🔍 Explorer</button>
441443
<button class="tab" onclick="showPanel('streams')">🌊 Research Streams</button>
444+
<button class="tab" onclick="showPanel('trends')">📈 Trends</button>
442445
<button class="tab" onclick="showPanel('emerging')">🔥 Emerging Topics</button>
443446
<button class="tab" onclick="showPanel('impactful')">⭐ Most Impactful</button>
444-
<button class="tab" onclick="showPanel('visualizations')">📈 Visualizations</button>
447+
<button class="tab" onclick="showPanel('visualizations')"> Visualizations</button>
445448
<button class="tab" onclick="showPanel('top-papers')">📚 Top Papers</button>
446449
<button class="tab" onclick="showPanel('about')">ℹ️ About</button>
447450
</div>
@@ -529,6 +532,55 @@ <h2 style="margin-bottom: 10px;">🔍 Hierarchical Literature Explorer</h2>
529532
</div>
530533
</div>
531534

535+
<!-- Trends Panel -->
536+
<div id="trends" class="panel">
537+
<div class="viz-container">
538+
<h2 style="margin-bottom: 20px;">📈 Temporal Evolution & Trends</h2>
539+
<p style="color: #64748b; margin-bottom: 30px;">Explore how research streams have evolved over nearly 50 years of IS research</p>
540+
541+
<!-- Stream Timeline Chart -->
542+
<div style="margin-bottom: 40px;">
543+
<h3 style="color: var(--primary); margin-bottom: 15px;">📊 Research Stream Evolution (1977-2026)</h3>
544+
<p style="color: #64748b; font-size: 0.9rem; margin-bottom: 20px;">Papers published per year by research stream</p>
545+
<canvas id="streamTimelineChart" style="max-height: 400px;"></canvas>
546+
</div>
547+
548+
<!-- Cumulative Growth Chart -->
549+
<div style="margin-bottom: 40px;">
550+
<h3 style="color: var(--primary); margin-bottom: 15px;">📈 Cumulative Growth</h3>
551+
<p style="color: #64748b; font-size: 0.9rem; margin-bottom: 20px;">Total papers published over time across all journals</p>
552+
<canvas id="cumulativeGrowthChart" style="max-height: 300px;"></canvas>
553+
</div>
554+
555+
<!-- Journal Distribution by Decade -->
556+
<div style="margin-bottom: 40px;">
557+
<h3 style="color: var(--primary); margin-bottom: 15px;">📚 Journal Contribution by Decade</h3>
558+
<p style="color: #64748b; font-size: 0.9rem; margin-bottom: 20px;">Publication distribution across the AIS Basket of Eight</p>
559+
<canvas id="journalDecadeChart" style="max-height: 400px;"></canvas>
560+
</div>
561+
562+
<!-- Emerging Topics Bubble Chart -->
563+
<div style="margin-bottom: 40px;">
564+
<h3 style="color: var(--primary); margin-bottom: 15px;">🔥 Emerging vs. Established Topics</h3>
565+
<p style="color: #64748b; font-size: 0.9rem; margin-bottom: 20px;">
566+
Research streams by average publication year and size (bubble size = paper count)
567+
</p>
568+
<canvas id="emergingTopicsBubbleChart" style="max-height: 500px;"></canvas>
569+
</div>
570+
571+
<!-- Stream Activity Heatmap -->
572+
<div style="margin-bottom: 20px;">
573+
<h3 style="color: var(--primary); margin-bottom: 15px;">🔥 Research Activity Heatmap</h3>
574+
<p style="color: #64748b; font-size: 0.9rem; margin-bottom: 20px;">
575+
Publication intensity by stream and 5-year period
576+
</p>
577+
<div id="activityHeatmap" style="overflow-x: auto;">
578+
<!-- Will be populated by JavaScript -->
579+
</div>
580+
</div>
581+
</div>
582+
</div>
583+
532584
<!-- Research Streams Panel -->
533585
<div id="streams" class="panel">
534586
<div class="controls">
@@ -867,6 +919,8 @@ <h3 style="color: var(--primary); margin-bottom: 15px;">🙏 Acknowledgments</h3
867919
loadExplorer();
868920
} else if (panelId === 'streams' && !document.getElementById('streamsContainer').dataset.loaded) {
869921
loadAllStreams();
922+
} else if (panelId === 'trends') {
923+
initializeTrendsCharts();
870924
} else if (panelId === 'emerging') {
871925
loadEmergingStreams();
872926
} else if (panelId === 'impactful') {
@@ -1602,6 +1656,262 @@ <h4 style="margin-top: 0; margin-bottom: 15px; color: var(--primary);">📊 Filt
16021656
document.body.removeChild(a);
16031657
URL.revokeObjectURL(url);
16041658
}
1659+
1660+
// ===== TRENDS VISUALIZATION FUNCTIONS =====
1661+
1662+
let trendsChartsInitialized = false;
1663+
let streamTimelineChart, cumulativeGrowthChart, journalDecadeChart, emergingTopicsBubbleChart;
1664+
1665+
function initializeTrendsCharts() {
1666+
if (trendsChartsInitialized) return;
1667+
1668+
console.log('Initializing trends charts...');
1669+
1670+
// Prepare data from dashboardData
1671+
const papers = dashboardData.papers;
1672+
const streams = dashboardData.streams.filter(s => s.id >= 0); // Exclude unclassified
1673+
1674+
// 1. Stream Timeline Data (papers per year by stream)
1675+
const yearlyStreamData = {};
1676+
papers.forEach(paper => {
1677+
if (paper.l1 >= 0) {
1678+
const year = paper.year;
1679+
if (!yearlyStreamData[year]) yearlyStreamData[year] = {};
1680+
if (!yearlyStreamData[year][paper.l1]) yearlyStreamData[year][paper.l1] = 0;
1681+
yearlyStreamData[year][paper.l1]++;
1682+
}
1683+
});
1684+
1685+
createStreamTimelineChart(yearlyStreamData, streams);
1686+
createCumulativeGrowthChart(papers);
1687+
createJournalDecadeChart(papers);
1688+
createEmergingTopicsBubbleChart(streams);
1689+
createActivityHeatmap(yearlyStreamData, streams);
1690+
1691+
trendsChartsInitialized = true;
1692+
}
1693+
1694+
function createStreamTimelineChart(yearlyData, streams) {
1695+
const ctx = document.getElementById('streamTimelineChart');
1696+
if (!ctx) return;
1697+
1698+
const years = Object.keys(yearlyData).map(Number).sort((a, b) => a - b);
1699+
const streamColors = [
1700+
'#2563eb', '#7c3aed', '#db2777', '#059669',
1701+
'#d97706', '#dc2626', '#0891b2', '#4f46e5'
1702+
];
1703+
1704+
const datasets = streams.map((stream, idx) => ({
1705+
label: stream.title,
1706+
data: years.map(year => yearlyData[year]?.[stream.id] || 0),
1707+
borderColor: streamColors[idx % streamColors.length],
1708+
backgroundColor: streamColors[idx % streamColors.length] + '20',
1709+
fill: false,
1710+
tension: 0.3
1711+
}));
1712+
1713+
streamTimelineChart = new Chart(ctx, {
1714+
type: 'line',
1715+
data: { labels: years, datasets: datasets },
1716+
options: {
1717+
responsive: true,
1718+
maintainAspectRatio: true,
1719+
plugins: {
1720+
legend: { position: 'bottom', labels: { boxWidth: 12, font: { size: 10 } } },
1721+
tooltip: { mode: 'index', intersect: false }
1722+
},
1723+
scales: {
1724+
y: { beginAtZero: true, title: { display: true, text: 'Papers per Year' } },
1725+
x: { title: { display: true, text: 'Year' } }
1726+
}
1727+
}
1728+
});
1729+
}
1730+
1731+
function createCumulativeGrowthChart(papers) {
1732+
const ctx = document.getElementById('cumulativeGrowthChart');
1733+
if (!ctx) return;
1734+
1735+
const yearCounts = {};
1736+
papers.forEach(p => {
1737+
const year = p.year;
1738+
if (year) yearCounts[year] = (yearCounts[year] || 0) + 1;
1739+
});
1740+
1741+
const years = Object.keys(yearCounts).map(Number).sort((a, b) => a - b);
1742+
let cumulative = 0;
1743+
const cumulativeData = years.map(year => {
1744+
cumulative += yearCounts[year] || 0;
1745+
return cumulative;
1746+
});
1747+
1748+
cumulativeGrowthChart = new Chart(ctx, {
1749+
type: 'line',
1750+
data: {
1751+
labels: years,
1752+
datasets: [{
1753+
label: 'Total Papers',
1754+
data: cumulativeData,
1755+
borderColor: '#2563eb',
1756+
backgroundColor: 'rgba(37, 99, 235, 0.1)',
1757+
fill: true,
1758+
tension: 0.3
1759+
}]
1760+
},
1761+
options: {
1762+
responsive: true,
1763+
maintainAspectRatio: true,
1764+
plugins: {
1765+
legend: { display: false },
1766+
tooltip: { callbacks: { label: ctx => `Total: ${ctx.parsed.y.toLocaleString()} papers` } }
1767+
},
1768+
scales: {
1769+
y: { beginAtZero: true, title: { display: true, text: 'Cumulative Papers' } },
1770+
x: { title: { display: true, text: 'Year' } }
1771+
}
1772+
}
1773+
});
1774+
}
1775+
1776+
function createJournalDecadeChart(papers) {
1777+
const ctx = document.getElementById('journalDecadeChart');
1778+
if (!ctx) return;
1779+
1780+
const journals = [...new Set(papers.map(p => p.journal))].sort();
1781+
const decades = ['1977-1989', '1990-1999', '2000-2009', '2010-2019', '2020-2026'];
1782+
1783+
const decadeData = decades.map(() => ({}));
1784+
papers.forEach(p => {
1785+
const year = p.year;
1786+
let decadeIdx = -1;
1787+
if (year >= 1977 && year <= 1989) decadeIdx = 0;
1788+
else if (year >= 1990 && year <= 1999) decadeIdx = 1;
1789+
else if (year >= 2000 && year <= 2009) decadeIdx = 2;
1790+
else if (year >= 2010 && year <= 2019) decadeIdx = 3;
1791+
else if (year >= 2020 && year <= 2026) decadeIdx = 4;
1792+
1793+
if (decadeIdx >= 0) {
1794+
if (!decadeData[decadeIdx][p.journal]) decadeData[decadeIdx][p.journal] = 0;
1795+
decadeData[decadeIdx][p.journal]++;
1796+
}
1797+
});
1798+
1799+
const journalColors = [
1800+
'#2563eb', '#7c3aed', '#db2777', '#059669',
1801+
'#d97706', '#dc2626', '#0891b2', '#4f46e5'
1802+
];
1803+
1804+
const datasets = journals.map((journal, idx) => ({
1805+
label: journal,
1806+
data: decades.map((_, di) => decadeData[di][journal] || 0),
1807+
backgroundColor: journalColors[idx % journalColors.length]
1808+
}));
1809+
1810+
journalDecadeChart = new Chart(ctx, {
1811+
type: 'bar',
1812+
data: { labels: decades, datasets: datasets },
1813+
options: {
1814+
responsive: true,
1815+
maintainAspectRatio: true,
1816+
plugins: {
1817+
legend: { position: 'bottom', labels: { boxWidth: 12, font: { size: 10 } } }
1818+
},
1819+
scales: {
1820+
x: { stacked: true, title: { display: true, text: 'Decade' } },
1821+
y: { stacked: true, beginAtZero: true, title: { display: true, text: 'Papers' } }
1822+
}
1823+
}
1824+
});
1825+
}
1826+
1827+
function createEmergingTopicsBubbleChart(streams) {
1828+
const ctx = document.getElementById('emergingTopicsBubbleChart');
1829+
if (!ctx) return;
1830+
1831+
const data = streams.map(stream => ({
1832+
x: stream.avgYear || 2000,
1833+
y: stream.avgCitations || 0,
1834+
r: Math.sqrt(stream.size) / 3, // Bubble size proportional to paper count
1835+
label: stream.title,
1836+
papers: stream.size
1837+
}));
1838+
1839+
emergingTopicsBubbleChart = new Chart(ctx, {
1840+
type: 'bubble',
1841+
data: {
1842+
datasets: [{
1843+
label: 'Research Streams',
1844+
data: data,
1845+
backgroundColor: 'rgba(37, 99, 235, 0.6)',
1846+
borderColor: '#2563eb',
1847+
borderWidth: 1
1848+
}]
1849+
},
1850+
options: {
1851+
responsive: true,
1852+
maintainAspectRatio: true,
1853+
plugins: {
1854+
legend: { display: false },
1855+
tooltip: {
1856+
callbacks: {
1857+
label: ctx => {
1858+
const item = ctx.raw;
1859+
return [
1860+
item.label,
1861+
`Avg Year: ${item.x.toFixed(1)}`,
1862+
`Avg Citations: ${item.y.toFixed(1)}`,
1863+
`Papers: ${item.papers}`
1864+
];
1865+
}
1866+
}
1867+
}
1868+
},
1869+
scales: {
1870+
x: { title: { display: true, text: 'Average Publication Year' }, min: 1990, max: 2025 },
1871+
y: { title: { display: true, text: 'Average Citations' }, beginAtZero: true }
1872+
}
1873+
}
1874+
});
1875+
}
1876+
1877+
function createActivityHeatmap(yearlyData, streams) {
1878+
const container = document.getElementById('activityHeatmap');
1879+
if (!container) return;
1880+
1881+
const periods = ['1977-1984', '1985-1994', '1995-2004', '2005-2014', '2015-2026'];
1882+
1883+
const periodData = streams.map(stream => {
1884+
return periods.map((_, pidx) => {
1885+
const startYear = 1977 + (pidx * 10) - (pidx === 0 ? 0 : 2);
1886+
const endYear = pidx === periods.length - 1 ? 2026 : startYear + 9;
1887+
let count = 0;
1888+
for (let year = startYear; year <= endYear; year++) {
1889+
count += (yearlyData[year]?.[stream.id] || 0);
1890+
}
1891+
return count;
1892+
});
1893+
});
1894+
1895+
const maxCount = Math.max(...periodData.flat());
1896+
1897+
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 0.85rem;">';
1898+
html += '<tr><th style="text-align: left; padding: 8px; border: 1px solid #e2e8f0;">Stream</th>';
1899+
periods.forEach(p => html += `<th style="padding: 8px; border: 1px solid #e2e8f0;">${p}</th>`);
1900+
html += '</tr>';
1901+
1902+
streams.forEach((stream, sidx) => {
1903+
html += `<tr><td style="padding: 8px; border: 1px solid #e2e8f0; font-weight: 500;">${stream.title}</td>`;
1904+
periodData[sidx].forEach(count => {
1905+
const intensity = maxCount > 0 ? count / maxCount : 0;
1906+
const color = `rgba(37, 99, 235, ${0.1 + intensity * 0.9})`;
1907+
html += `<td style="padding: 8px; border: 1px solid #e2e8f0; background: ${color}; text-align: center;">${count}</td>`;
1908+
});
1909+
html += '</tr>';
1910+
});
1911+
html += '</table>';
1912+
1913+
container.innerHTML = html;
1914+
}
16051915

16061916
// Initialize
16071917
document.addEventListener('DOMContentLoaded', function() {

0 commit comments

Comments
 (0)