Skip to content

Commit 7a57be2

Browse files
feat: Add search and export functionality to dashboard
- Added full-text search across titles, abstracts, and labels with instant filtering - Implemented export functionality: CSV, BibTeX, and JSON formats - Enhanced sort buttons with emoji icons for better UX - Added results count display - Improved button layout with export options - All exports include complete metadata for citation management Features match Appendix D requirements for dynamic filtering and data export.
1 parent 5fc3c26 commit 7a57be2

1 file changed

Lines changed: 138 additions & 6 deletions

File tree

ISR-submission/dashboard/dashboard.html

Lines changed: 138 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,18 +1239,47 @@ <h4 style="margin-bottom: 10px; color: var(--dark); font-size: 0.95rem;">${micro
12391239
let html = `
12401240
<div style="margin-bottom: 20px;">
12411241
<h3 style="color: var(--primary); margin-bottom: 10px;">${context.title}</h3>
1242-
<p style="color: #64748b; font-size: 0.9rem; margin-bottom: 10px;">${context.subtitle}</p>
1242+
<p style="color: #64748b; font-size: 0.9rem; margin-bottom: 15px;">${context.subtitle}</p>
1243+
1244+
<!-- Search Bar -->
1245+
<div style="margin-bottom: 15px;">
1246+
<input type="text"
1247+
id="paperSearch"
1248+
placeholder="🔍 Search papers by title, abstract, or keywords..."
1249+
class="search-box"
1250+
oninput="filterPapers()"
1251+
style="width: 100%; padding: 12px 20px; border: 2px solid var(--border); border-radius: 10px; font-size: 1rem;">
1252+
</div>
1253+
1254+
<!-- Filters and Sort -->
12431255
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 15px;">
1256+
<!-- Sort Options -->
12441257
<button onclick="sortPapers('citations')" class="filter-btn" style="padding: 6px 12px; font-size: 0.9rem;">
1245-
Sort by Citations
1258+
📊 Sort by Citations
12461259
</button>
12471260
<button onclick="sortPapers('year-desc')" class="filter-btn" style="padding: 6px 12px; font-size: 0.9rem;">
1248-
Sort by Year (Newest)
1261+
📅 Newest First
12491262
</button>
12501263
<button onclick="sortPapers('year-asc')" class="filter-btn" style="padding: 6px 12px; font-size: 0.9rem;">
1251-
Sort by Year (Oldest)
1264+
📅 Oldest First
1265+
</button>
1266+
1267+
<!-- Export Options -->
1268+
<button onclick="exportPapers('csv')" class="filter-btn" style="padding: 6px 12px; font-size: 0.9rem; margin-left: auto;">
1269+
💾 Export CSV
1270+
</button>
1271+
<button onclick="exportPapers('bibtex')" class="filter-btn" style="padding: 6px 12px; font-size: 0.9rem;">
1272+
📚 Export BibTeX
1273+
</button>
1274+
<button onclick="exportPapers('json')" class="filter-btn" style="padding: 6px 12px; font-size: 0.9rem;">
1275+
📋 Export JSON
12521276
</button>
12531277
</div>
1278+
1279+
<!-- Results Count -->
1280+
<p id="resultsCount" style="color: #64748b; font-size: 0.9rem; margin-bottom: 10px;">
1281+
Showing ${papers.length} paper${papers.length !== 1 ? 's' : ''}
1282+
</p>
12541283
</div>
12551284
<div id="papersList">
12561285
`;
@@ -1261,8 +1290,15 @@ <h3 style="color: var(--primary); margin-bottom: 10px;">${context.title}</h3>
12611290
? `<a href="${doiLink}" target="_blank" rel="noopener noreferrer" style="color: var(--dark); text-decoration: none;">${paper.title}</a>`
12621291
: paper.title;
12631292

1293+
const searchableText = `${paper.title} ${paper.abstract || ''} ${paper.l1Label || ''} ${paper.l3Label || ''}`.toLowerCase();
1294+
12641295
html += `
1265-
<div class="paper-item" style="margin-bottom: 15px; padding: 15px; background: #f8fafc; border-radius: 8px; border-left: 3px solid var(--primary);" data-year="${paper.year}" data-citations="${paper.citations}">
1296+
<div class="paper-item"
1297+
style="margin-bottom: 15px; padding: 15px; background: #f8fafc; border-radius: 8px; border-left: 3px solid var(--primary);"
1298+
data-year="${paper.year}"
1299+
data-citations="${paper.citations}"
1300+
data-journal="${paper.journal}"
1301+
data-searchable="${searchableText}">
12661302
<div style="font-weight: 600; margin-bottom: 8px; font-size: 1.05rem;">
12671303
${idx + 1}. ${titleHtml}
12681304
</div>
@@ -1284,9 +1320,10 @@ <h3 style="color: var(--primary); margin-bottom: 10px;">${context.title}</h3>
12841320
html += '</div>';
12851321
container.innerHTML = html;
12861322

1287-
// Store current papers and context for sorting
1323+
// Store current papers and context for sorting/filtering
12881324
window.currentPapers = papers;
12891325
window.currentContext = context;
1326+
window.allCurrentPapers = papers; // Keep original unfiltered list
12901327
}
12911328

12921329
// Sort papers in the current view
@@ -1308,6 +1345,101 @@ <h3 style="color: var(--primary); margin-bottom: 10px;">${context.title}</h3>
13081345
// Reload with new sort
13091346
displayPaperList(papers, window.currentContext);
13101347
}
1348+
1349+
// Filter papers based on search query
1350+
function filterPapers() {
1351+
const searchInput = document.getElementById('paperSearch');
1352+
if (!searchInput || !window.allCurrentPapers) return;
1353+
1354+
const query = searchInput.value.toLowerCase().trim();
1355+
1356+
if (!query) {
1357+
// Show all papers if search is empty
1358+
window.currentPapers = window.allCurrentPapers;
1359+
displayPaperList(window.allCurrentPapers, window.currentContext);
1360+
return;
1361+
}
1362+
1363+
// Filter papers by search query
1364+
const filtered = window.allCurrentPapers.filter(paper => {
1365+
const searchableText = `${paper.title} ${paper.abstract || ''} ${paper.l1Label || ''} ${paper.l3Label || ''}`.toLowerCase();
1366+
return searchableText.includes(query);
1367+
});
1368+
1369+
window.currentPapers = filtered;
1370+
displayPaperList(filtered, window.currentContext);
1371+
}
1372+
1373+
// Export papers in different formats
1374+
function exportPapers(format) {
1375+
if (!window.currentPapers || window.currentPapers.length === 0) {
1376+
alert('No papers to export!');
1377+
return;
1378+
}
1379+
1380+
const papers = window.currentPapers;
1381+
const context = window.currentContext || {title: 'Papers'};
1382+
const filename = `papers_${context.title.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_${new Date().toISOString().split('T')[0]}`;
1383+
1384+
if (format === 'csv') {
1385+
exportCSV(papers, filename);
1386+
} else if (format === 'bibtex') {
1387+
exportBibTeX(papers, filename);
1388+
} else if (format === 'json') {
1389+
exportJSON(papers, filename);
1390+
}
1391+
}
1392+
1393+
function exportCSV(papers, filename) {
1394+
const headers = ['Title', 'Authors', 'Year', 'Journal', 'Citations', 'DOI', 'Abstract', 'Stream', 'Subtopic'];
1395+
const rows = papers.map(p => [
1396+
`"${(p.title || '').replace(/"/g, '""')}"`,
1397+
`"${(p.authors || '').replace(/"/g, '""')}"`,
1398+
p.year || '',
1399+
`"${(p.journal || '').replace(/"/g, '""')}"`,
1400+
p.citations || 0,
1401+
p.doi || '',
1402+
`"${(p.abstract || '').replace(/"/g, '""')}"`,
1403+
`"${(p.l1Label || '').replace(/"/g, '""')}"`,
1404+
`"${(p.l3Label || '').replace(/"/g, '""')}"`
1405+
].join(','));
1406+
1407+
const csv = [headers.join(','), ...rows].join('\\n');
1408+
downloadFile(csv, `${filename}.csv`, 'text/csv');
1409+
}
1410+
1411+
function exportBibTeX(papers, filename) {
1412+
const bibtex = papers.map((paper, idx) => {
1413+
const key = `${(paper.authors || 'unknown').split(',')[0].toLowerCase().replace(/[^a-z]/g, '')}${paper.year || ''}${paper.title.split(' ')[0].toLowerCase()}`;
1414+
return `@article{${key},
1415+
title = {${paper.title}},
1416+
author = {${paper.authors || 'Unknown'}},
1417+
journal = {${paper.journal}},
1418+
year = {${paper.year}},
1419+
doi = {${paper.doi || ''}},
1420+
note = {Citations: ${paper.citations}}
1421+
}`;
1422+
}).join('\\n\\n');
1423+
1424+
downloadFile(bibtex, `${filename}.bib`, 'text/plain');
1425+
}
1426+
1427+
function exportJSON(papers, filename) {
1428+
const json = JSON.stringify(papers, null, 2);
1429+
downloadFile(json, `${filename}.json`, 'application/json');
1430+
}
1431+
1432+
function downloadFile(content, filename, mimeType) {
1433+
const blob = new Blob([content], { type: mimeType });
1434+
const url = URL.createObjectURL(blob);
1435+
const a = document.createElement('a');
1436+
a.href = url;
1437+
a.download = filename;
1438+
document.body.appendChild(a);
1439+
a.click();
1440+
document.body.removeChild(a);
1441+
URL.revokeObjectURL(url);
1442+
}
13111443

13121444
// Initialize
13131445
document.addEventListener('DOMContentLoaded', function() {

0 commit comments

Comments
 (0)