Skip to content
This repository was archived by the owner on Jan 28, 2026. It is now read-only.
Merged
28 changes: 19 additions & 9 deletions backend/routes/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,29 @@ router.get('/', async (req, res) => {
}
}

if (type === 'institution' && query.length >= 2) {
if (type === 'institution' && query.length >= 1) {
try {
const url = `${OPENALEX_API_BASE}/institutions`;
const params = { search: query, per_page: 10 };
// Use OpenAlex autocomplete endpoint for better partial matching
const url = `${OPENALEX_API_BASE}/autocomplete`;
const params = {
q: query,
mailto: 'team@ourresearch.org'
};
const response = await axios.get(url, { params, headers: OPENALEX_HEADERS });
const data = response.data;
const results = (data.results || []).map(inst => ({
id: inst.id,
display_name: inst.display_name
}));
return res.json({ results });

// Filter results to only include institutions
const institutionResults = (data.results || [])
.filter(item => item.entity_type === 'institution')
.map(inst => ({
id: inst.id,
display_name: inst.display_name
}));

return res.json({ results: institutionResults });
} catch (err) {
return res.status(500).json({ results: [], error: 'Failed to fetch from OpenAlex' });
console.error('OpenAlex autocomplete error:', err.message);
return res.status(500).json({ results: [], error: 'Failed to fetch from OpenAlex autocomplete' });
}
}

Expand Down
8 changes: 6 additions & 2 deletions backend/routes/publications.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,7 @@ router.get('/keyword_trends', async (req, res) => {
start_date,
end_date,
per_page = 200,
years,
limit
institution_id
} = req.query;

let filterParts = [];
Expand All @@ -159,6 +158,11 @@ router.get('/keyword_trends', async (req, res) => {
if (keyword) {
filterParts.push(`title_and_abstract.search:${keyword}`);
}
// Add institution filter if provided
if (institution_id) {
filterParts.push(`authorships.institutions.id:I${institution_id}`);
}

// Only add date filters if start_date or end_date is provided
if (start_date) {
filterParts.push(`from_publication_date:${start_date}`);
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/assets/icons/author.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions frontend/src/assets/icons/institution.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions frontend/src/assets/icons/search-publications.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
116 changes: 96 additions & 20 deletions frontend/src/assets/styles/institutionDropdown.module.css
Original file line number Diff line number Diff line change
@@ -1,34 +1,53 @@
.dropdownContainer {
position: relative;
width: 100%;
max-width: 400px;
margin-bottom: 1rem;
margin-bottom: 0;
}
.label {
font-weight: 600;
margin-bottom: 0.25rem;
display: block;
}
.inputWrapper {
position: relative;
display: flex;
align-items: center;
}
.input {
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px 0 0 4px;
padding: 0.75rem;
border: 1px solid #e5e7eb;
border-radius: 6px;
outline: none;
font-size: 1rem;
font-size: 0.875rem;
background-color: #fff;
transition: border-color 0.2s ease;
height: 48px;
box-sizing: border-box;
line-height: 1.5;
}

.input:focus {
border-color: #4F6AF6;
box-shadow: 0 0 0 2px rgba(79, 106, 246, 0.1);
}
.toggleBtn {
border: 1px solid #ccc;
border-left: none;
background: #f7f7f7;
padding: 0.5rem 0.75rem;
border-radius: 0 4px 4px 0;
position: absolute;
right: 32px;
top: 50%;
transform: translateY(-50%);
border: none;
background: transparent;
padding: 0.25rem;
cursor: pointer;
font-size: 1rem;
font-size: 0.75rem;
color: #6b7280;
z-index: 5;
height: 24px;
width: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.dropdownList {
position: absolute;
Expand All @@ -38,26 +57,83 @@
max-height: 220px;
overflow-y: auto;
background: #fff;
border: 1px solid #ccc;
border-top: none;
border: 1px solid #e5e7eb;
border-radius: 6px;
z-index: 10;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
margin-top: 4px;
}
.dropdownItem {
padding: 0.5rem 1rem;
padding: 0.75rem 1rem;
cursor: pointer;
transition: background 0.15s;
border-bottom: 1px solid #f3f4f6;
}

.dropdownItem:last-child {
border-bottom: none;
}

.dropdownItem:hover {
background: #f0f0f0;
background: #f9fafb;
}
.loading, .noResults {
padding: 0.75rem 1rem;
color: #888;
color: #6b7280;
text-align: center;
font-size: 0.875rem;
}
.selectedValue {
margin-top: 0.5rem;
font-size: 0.95rem;
color: #333;
font-size: 0.875rem;
color: #4F6AF6;
font-weight: 500;
}

/* Dark mode styles */
:global(.dark) .label {
color: #ccc;
}

:global(.dark) .input {
background-color: #1a1a1a;
border: 1px solid #404040;
color: #fff;
}

:global(.dark) .input:focus {
border-color: #4F6AF6;
box-shadow: 0 0 0 2px rgba(79, 106, 246, 0.2);
}

:global(.dark) .toggleBtn {
color: #ccc;
}

:global(.dark) .toggleBtn:hover {
color: #4F6AF6;
}

:global(.dark) .dropdownList {
background: #2a2a2a;
border: 1px solid #404040;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}

:global(.dark) .dropdownItem {
border-bottom: 1px solid #404040;
color: #fff;
}

:global(.dark) .dropdownItem:hover {
background: #404040;
}

:global(.dark) .loading,
:global(.dark) .noResults {
color: #ccc;
}

:global(.dark) .selectedValue {
color: #4F6AF6;
}
Loading