Skip to content

Commit 2c8729b

Browse files
feat: integrate ROR data to display organization metadata and identifiers across resource pages and explore tables
1 parent 58e8557 commit 2c8729b

3 files changed

Lines changed: 97 additions & 17 deletions

File tree

‎src/app/explore/page.js‎

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ import {
1515
// --- Column Definitions ---
1616
const ALL_COLUMNS = [
1717
{ key: 'title', label: 'Resource', sticky: true, minWidth: '240px' },
18-
{ key: 'organizations', label: 'Organization', minWidth: '200px' },
18+
{ key: 'organizations', label: 'Organization', minWidth: '220px' },
19+
{ key: 'rorId', label: 'ROR ID', minWidth: '130px' },
1920
{ key: 'origin', label: 'HQ Location', minWidth: '140px' },
2021
{ key: 'entityCategory', label: 'Sector', minWidth: '120px' },
21-
{ key: 'entitySubType', label: 'Entity Type', minWidth: '140px' },
22+
{ key: 'entitySubType', label: 'Entity Type', minWidth: '160px' },
2223
{ key: 'dataTypes', label: 'Data Types', minWidth: '180px' },
2324
{ key: 'compensationType', label: 'Compensation', minWidth: '130px' },
2425
{ key: 'countries', label: 'Available In', minWidth: '160px' },
@@ -44,6 +45,8 @@ function getNestedValue(resource, key) {
4445
return (resource.compatibleSources || []).join(', ');
4546
case 'citations':
4647
return (resource.citations || []).length;
48+
case 'rorId':
49+
return (resource.organizations || []).map(o => o.rorId).filter(Boolean).join(', ');
4750
default:
4851
return resource[key] || '';
4952
}
@@ -507,9 +510,33 @@ export default function ExplorePage() {
507510
)}
508511
</div>
509512
) : col.key === 'organizations' ? (
510-
<span className="text-slate-600 text-xs">
511-
{(resource.organizations || []).map(o => o.name).join(', ') || '—'}
512-
</span>
513+
<div className="flex flex-col gap-1">
514+
{(resource.organizations || []).map((o, idx) => (
515+
<div key={idx} className="flex items-center gap-1.5 whitespace-nowrap overflow-hidden">
516+
<span className="text-slate-600 text-xs truncate">{o.name}</span>
517+
{o.rorId && (
518+
<a
519+
href={o.rorId}
520+
target="_blank"
521+
rel="noopener noreferrer"
522+
title="View on ROR"
523+
className="text-[9px] text-blue-500 hover:text-blue-700 bg-blue-50 px-1 py-0 rounded border border-blue-100 uppercase font-bold"
524+
>
525+
ROR
526+
</a>
527+
)}
528+
</div>
529+
))}
530+
{(!resource.organizations || resource.organizations.length === 0) && '—'}
531+
</div>
532+
) : col.key === 'rorId' ? (
533+
<div className="flex flex-col gap-1">
534+
{(resource.organizations || []).map((o, idx) => o.rorId ? (
535+
<a key={idx} href={o.rorId} target="_blank" rel="noopener noreferrer" className="text-[10px] font-mono text-blue-600 hover:underline">
536+
{o.rorId.split('/').pop()}
537+
</a>
538+
) : null)}
539+
</div>
513540
) : col.key === 'origin' ? (
514541
<span className="inline-flex items-center gap-1.5 text-xs text-slate-600">
515542
{resource.originCode && (
@@ -538,7 +565,18 @@ export default function ExplorePage() {
538565
{resource.entityCategory || '—'}
539566
</span>
540567
) : col.key === 'entitySubType' ? (
541-
<span className="text-xs text-slate-500">{resource.entitySubType || '—'}</span>
568+
<div className="flex flex-col gap-1">
569+
<span className="text-xs text-slate-500">{resource.entitySubType || '—'}</span>
570+
{resource.organizations?.some(o => o.rorTypes) && (
571+
<div className="flex flex-wrap gap-1">
572+
{Array.from(new Set(resource.organizations.flatMap(o => o.rorTypes || []))).map(t => (
573+
<span key={t} className="text-[9px] bg-indigo-50 text-indigo-600 border border-indigo-100 px-1 py-0 rounded font-medium">
574+
{t.charAt(0).toUpperCase() + t.slice(1)}
575+
</span>
576+
))}
577+
</div>
578+
)}
579+
</div>
542580
) : col.key === 'dataTypes' ? (
543581
<div className="flex flex-wrap gap-1">
544582
{(resource.dataTypes || []).map(d => (

‎src/app/resource/[slug]/page.js‎

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,10 @@ export default function ResourcePage({ params }) {
111111
'creator': resource.organizations ? resource.organizations.map(org => ({
112112
'@type': 'Organization',
113113
'name': org.name,
114-
...(org.wikidataId && { 'sameAs': `https://www.wikidata.org/wiki/${org.wikidataId}` })
114+
'sameAs': [
115+
...(org.wikidataId ? [`https://www.wikidata.org/wiki/${org.wikidataId}`] : []),
116+
...(org.rorId ? [org.rorId] : [])
117+
]
115118
})) : undefined,
116119
'isAccessibleForFree': true,
117120
...(resource.resourceWikidataId && { 'sameAs': `https://www.wikidata.org/wiki/${resource.resourceWikidataId}` })
@@ -175,10 +178,17 @@ export default function ResourcePage({ params }) {
175178
)}
176179
<p className="mt-2 text-lg text-gray-600">by {resource.organizations ? resource.organizations.map((o, i) => (
177180
<span key={i}>
178-
{o.wikidataId ? (
179-
<a href={`https://www.wikidata.org/wiki/${o.wikidataId}`} target="_blank" rel="noopener noreferrer" className="hover:text-blue-600 hover:underline">
180-
{o.name}
181-
</a>
181+
{o.wikidataId || o.rorId ? (
182+
<span className="inline-flex items-center gap-2">
183+
<a href={o.rorId || `https://www.wikidata.org/wiki/${o.wikidataId}`} target="_blank" rel="noopener noreferrer" className="hover:text-blue-600 hover:underline">
184+
{o.name}
185+
</a>
186+
{o.rorId && (
187+
<a href={o.rorId} target="_blank" rel="noopener noreferrer" title="View on Research Organization Registry (ROR)" className="text-[10px] text-blue-500 hover:text-blue-700 bg-blue-50 px-1.5 py-0.5 rounded border border-blue-100 uppercase font-bold tracking-tighter">
188+
ROR
189+
</a>
190+
)}
191+
</span>
182192
) : (
183193
o.name
184194
)}
@@ -245,17 +255,32 @@ export default function ResourcePage({ params }) {
245255
</div>
246256
<div>
247257
<h2 className="text-2xl font-bold text-gray-800 mb-4">Entity Type</h2>
248-
<span className="inline-flex items-center bg-purple-100 text-purple-800 text-md font-medium px-4 py-2 rounded-full">
249-
<FaInfoCircle className="mr-2" /> {resource.entityCategory} / {resource.entitySubType}
250-
</span>
258+
<div className="flex flex-wrap gap-2">
259+
<span className="inline-flex items-center bg-purple-100 text-purple-800 text-md font-medium px-4 py-2 rounded-full">
260+
<FaInfoCircle className="mr-2" /> {resource.entityCategory} / {resource.entitySubType}
261+
</span>
262+
{resource.organizations?.some(o => o.rorTypes) && (
263+
<span className="inline-flex items-center bg-indigo-50 text-indigo-700 text-md font-medium px-4 py-2 rounded-full border border-indigo-100">
264+
<FaUniversity className="mr-2" /> {Array.from(new Set(resource.organizations.flatMap(o => o.rorTypes || []))).map(t => t.charAt(0).toUpperCase() + t.slice(1)).join(', ')}
265+
</span>
266+
)}
267+
</div>
251268
</div>
252269

253270
{resource.origin && (
254271
<div>
255272
<h2 className="text-2xl font-bold text-gray-800 mb-4">Based In</h2>
256-
<span className="inline-flex items-center bg-teal-100 text-teal-800 text-md font-medium px-4 py-2 rounded-full">
257-
<FaBuilding className="mr-2" /> {resource.origin} {resource.originCode ? `(${resource.originCode})` : ''}
258-
</span>
273+
<div className="flex flex-col gap-2">
274+
<span className="inline-flex items-center bg-teal-100 text-teal-800 text-md font-medium px-4 py-2 rounded-full">
275+
<FaBuilding className="mr-2" /> {resource.origin} {resource.originCode ? `(${resource.originCode})` : ''}
276+
</span>
277+
{resource.organizations?.some(o => o.rorCity) && (
278+
<span className="text-sm text-gray-500 ml-4 flex items-center">
279+
<FaMapMarkerAlt className="mr-1.5 text-gray-400" />
280+
{Array.from(new Set(resource.organizations.map(o => `${o.rorCity}, ${o.rorCountry}`))).join('; ')} (via ROR)
281+
</span>
282+
)}
283+
</div>
259284
</div>
260285
)}
261286

‎src/data/resources.js‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,7 @@ const dataTypeToMacroCategory = {
18541854

18551855
import enrichedResources from '../../public/resources_wikidata.json' with { type: 'json' };
18561856
import wikidataStats from './wikidataStats.json' with { type: 'json' };
1857+
import rorData from './rorData.json' with { type: 'json' };
18571858

18581859
const citedQIDs = new Set(wikidataStats.items ? wikidataStats.items.map(item => item.id) : []);
18591860

@@ -1865,8 +1866,24 @@ export const enrichedResourcesWithMacro = enrichedResources.map(r => {
18651866

18661867
const isCitedOnWikidata = r.resourceWikidataId ? citedQIDs.has(r.resourceWikidataId) : false;
18671868

1869+
const organizations = (r.organizations || []).map(org => {
1870+
const rorEntry = rorData[org.name];
1871+
if (rorEntry && rorEntry.rorId) {
1872+
return {
1873+
...org,
1874+
rorId: rorEntry.rorId,
1875+
rorName: rorEntry.name,
1876+
rorTypes: rorEntry.types,
1877+
rorCountry: rorEntry.country,
1878+
rorCity: rorEntry.city,
1879+
};
1880+
}
1881+
return org;
1882+
});
1883+
18681884
return {
18691885
...r,
1886+
organizations,
18701887
macroCategories,
18711888
isCitedOnWikidata,
18721889
};

0 commit comments

Comments
 (0)