|
import React, { useState, useEffect, useMemo } from 'react'; |
|
|
|
const UrologyLeaderboard = () => { |
|
const [searchTerm, setSearchTerm] = useState(''); |
|
const [filterLicense, setFilterLicense] = useState('all'); |
|
const [sortBy, setSortBy] = useState('accuracy'); |
|
const [sortOrder, setSortOrder] = useState('desc'); |
|
const [data, setData] = useState([]); |
|
const [loading, setLoading] = useState(true); |
|
const [refreshing, setRefreshing] = useState(false); |
|
const [lastUpdated, setLastUpdated] = useState(null); |
|
const [error, setError] = useState(null); |
|
|
|
const loadData = async () => { |
|
try { |
|
const response = await fetch('https://datasets-server.huggingface.co/rows?dataset=SASLeaderboard/results&config=default&split=train'); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`); |
|
} |
|
|
|
const data = await response.json(); |
|
console.log('Raw HuggingFace data:', data); |
|
|
|
if (!data.rows || data.rows.length === 0) { |
|
throw new Error('No data found in the dataset'); |
|
} |
|
|
|
|
|
const modelStats = {}; |
|
|
|
data.rows.forEach(row => { |
|
const rowData = row.row; |
|
const model = rowData.model; |
|
const isCorrect = rowData.is_correct; |
|
const timestamp = rowData.timestamp; |
|
|
|
if (!modelStats[model]) { |
|
modelStats[model] = { |
|
model: model, |
|
baseModel: model.split('/')[1] || model, |
|
totalQuestions: 0, |
|
correctAnswers: 0, |
|
submittedTime: timestamp, |
|
license: "Unknown", |
|
submitType: "unknown", |
|
params: 0, |
|
precision: "unknown", |
|
status: "FINISHED" |
|
}; |
|
} |
|
|
|
modelStats[model].totalQuestions++; |
|
if (isCorrect) { |
|
modelStats[model].correctAnswers++; |
|
} |
|
|
|
|
|
if (new Date(timestamp) > new Date(modelStats[model].submittedTime)) { |
|
modelStats[model].submittedTime = timestamp; |
|
} |
|
}); |
|
|
|
|
|
const processedData = Object.values(modelStats).map(stats => ({ |
|
...stats, |
|
accuracy: stats.totalQuestions > 0 ? stats.correctAnswers / stats.totalQuestions : 0 |
|
})); |
|
|
|
console.log('Processed data:', processedData); |
|
return processedData; |
|
} catch (error) { |
|
console.error('Error loading data from HuggingFace:', error); |
|
throw error; |
|
} |
|
}; |
|
|
|
const refreshData = async () => { |
|
setRefreshing(true); |
|
setError(null); |
|
try { |
|
const newData = await loadData(); |
|
setData(newData); |
|
setLastUpdated(new Date()); |
|
} catch (error) { |
|
setError(`Failed to load data: ${error.message}`); |
|
setData([]); |
|
} finally { |
|
setRefreshing(false); |
|
} |
|
}; |
|
|
|
useEffect(() => { |
|
const initializeData = async () => { |
|
try { |
|
const initialData = await loadData(); |
|
setData(initialData); |
|
setLastUpdated(new Date()); |
|
} catch (error) { |
|
setError(`Failed to load data: ${error.message}`); |
|
setData([]); |
|
} finally { |
|
setLoading(false); |
|
} |
|
}; |
|
initializeData(); |
|
}, []); |
|
|
|
const filteredAndSortedData = useMemo(() => { |
|
|
|
if (error) { |
|
return []; |
|
} |
|
|
|
let filtered = data.filter(item => { |
|
const matchesSearch = item.model.toLowerCase().includes(searchTerm.toLowerCase()) || |
|
item.baseModel.toLowerCase().includes(searchTerm.toLowerCase()); |
|
const matchesFilter = filterLicense === 'all' || item.license === filterLicense; |
|
return matchesSearch && matchesFilter; |
|
}); |
|
|
|
return filtered.sort((a, b) => { |
|
let aValue = a[sortBy]; |
|
let bValue = b[sortBy]; |
|
|
|
if (sortBy === 'submittedTime') { |
|
aValue = new Date(aValue); |
|
bValue = new Date(bValue); |
|
} |
|
|
|
if (sortOrder === 'desc') { |
|
return bValue > aValue ? 1 : -1; |
|
} else { |
|
return aValue > bValue ? 1 : -1; |
|
} |
|
}); |
|
}, [data, searchTerm, filterLicense, sortBy, sortOrder, error]); |
|
|
|
const getRankIcon = (index) => { |
|
switch(index) { |
|
case 0: return React.createElement('span', { style: { fontSize: '24px' } }, '🏆'); |
|
case 1: return React.createElement('span', { style: { fontSize: '24px' } }, '🥈'); |
|
case 2: return React.createElement('span', { style: { fontSize: '24px' } }, '🥉'); |
|
default: return React.createElement('span', { |
|
style: { fontSize: '20px', fontWeight: 'bold', color: '#9ca3af' } |
|
}, `#${index + 1}`); |
|
} |
|
}; |
|
|
|
const formatAccuracy = (accuracy) => { |
|
return `${(accuracy * 100).toFixed(2)}%`; |
|
}; |
|
|
|
const formatParams = (params) => { |
|
if (params === 0) return 'API'; |
|
if (params >= 1e9) return `${(params / 1e9).toFixed(1)}B`; |
|
if (params >= 1e6) return `${(params / 1e6).toFixed(1)}M`; |
|
return params.toLocaleString(); |
|
}; |
|
|
|
const formatDate = (dateString) => { |
|
return new Date(dateString).toLocaleDateString('en-US', { |
|
day: '2-digit', |
|
month: '2-digit', |
|
year: 'numeric', |
|
hour: '2-digit', |
|
minute: '2-digit' |
|
}); |
|
}; |
|
|
|
const getSortIcon = (column) => { |
|
if (sortBy !== column) { |
|
return React.createElement('span', { style: { color: '#9ca3af' } }, '↕️'); |
|
} |
|
return sortOrder === 'desc' ? |
|
React.createElement('span', { style: { color: '#3b82f6' } }, '⬇️') : |
|
React.createElement('span', { style: { color: '#3b82f6' } }, '⬆️'); |
|
}; |
|
|
|
const styles = { |
|
container: { |
|
minHeight: '100vh', |
|
background: 'linear-gradient(135deg, #0f172a 0%, #1e3a8a 50%, #3730a3 100%)', |
|
padding: '32px 16px' |
|
}, |
|
innerContainer: { |
|
maxWidth: '1200px', |
|
margin: '0 auto' |
|
}, |
|
header: { |
|
textAlign: 'center', |
|
marginBottom: '32px' |
|
}, |
|
title: { |
|
fontSize: '48px', |
|
fontWeight: 'bold', |
|
color: 'white', |
|
marginBottom: '16px', |
|
background: 'linear-gradient(to right, #60a5fa, #a78bfa)', |
|
WebkitBackgroundClip: 'text', |
|
backgroundClip: 'text', |
|
color: 'transparent' |
|
}, |
|
subtitle: { |
|
fontSize: '20px', |
|
color: '#d1d5db', |
|
marginBottom: '24px' |
|
}, |
|
statsContainer: { |
|
display: 'flex', |
|
justifyContent: 'center', |
|
gap: '32px', |
|
textAlign: 'center', |
|
flexWrap: 'wrap' |
|
}, |
|
statCard: { |
|
background: 'rgba(255, 255, 255, 0.1)', |
|
backdropFilter: 'blur(10px)', |
|
border: '1px solid rgba(255, 255, 255, 0.2)', |
|
borderRadius: '12px', |
|
padding: '16px' |
|
}, |
|
statNumber: { |
|
fontSize: '24px', |
|
fontWeight: 'bold', |
|
color: 'white' |
|
}, |
|
statLabel: { |
|
fontSize: '14px', |
|
color: '#d1d5db' |
|
}, |
|
controlsContainer: { |
|
marginBottom: '24px', |
|
display: 'flex', |
|
flexWrap: 'wrap', |
|
gap: '16px', |
|
alignItems: 'center', |
|
justifyContent: 'space-between' |
|
}, |
|
inputGroup: { |
|
display: 'flex', |
|
flexWrap: 'wrap', |
|
gap: '16px', |
|
alignItems: 'center' |
|
}, |
|
inputContainer: { |
|
position: 'relative' |
|
}, |
|
inputIcon: { |
|
position: 'absolute', |
|
left: '12px', |
|
top: '50%', |
|
transform: 'translateY(-50%)', |
|
color: '#9ca3af' |
|
}, |
|
input: { |
|
background: 'rgba(255, 255, 255, 0.1)', |
|
backdropFilter: 'blur(10px)', |
|
border: '1px solid rgba(255, 255, 255, 0.2)', |
|
borderRadius: '8px', |
|
color: 'white', |
|
padding: '8px 12px 8px 40px', |
|
outline: 'none', |
|
fontSize: '14px' |
|
}, |
|
select: { |
|
background: 'rgba(255, 255, 255, 0.1)', |
|
backdropFilter: 'blur(10px)', |
|
border: '1px solid rgba(255, 255, 255, 0.2)', |
|
borderRadius: '8px', |
|
color: 'white', |
|
padding: '8px 32px 8px 40px', |
|
outline: 'none', |
|
fontSize: '14px' |
|
}, |
|
updateButton: { |
|
background: '#059669', |
|
color: 'white', |
|
border: 'none', |
|
borderRadius: '8px', |
|
padding: '8px 16px', |
|
cursor: 'pointer', |
|
display: 'flex', |
|
alignItems: 'center', |
|
gap: '8px', |
|
fontSize: '14px' |
|
}, |
|
tableCard: { |
|
background: 'rgba(255, 255, 255, 0.1)', |
|
backdropFilter: 'blur(10px)', |
|
border: '1px solid rgba(255, 255, 255, 0.2)', |
|
borderRadius: '12px', |
|
overflow: 'hidden' |
|
}, |
|
tableContainer: { |
|
overflowX: 'auto' |
|
}, |
|
scrollContainer: { |
|
maxHeight: '384px', |
|
overflowY: 'auto' |
|
}, |
|
table: { |
|
width: '100%', |
|
borderCollapse: 'collapse' |
|
}, |
|
tableHeader: { |
|
position: 'sticky', |
|
top: 0, |
|
background: 'rgba(255, 255, 255, 0.1)', |
|
backdropFilter: 'blur(10px)', |
|
borderBottom: '1px solid rgba(255, 255, 255, 0.1)' |
|
}, |
|
th: { |
|
padding: '16px 24px', |
|
textAlign: 'left', |
|
color: 'white', |
|
fontWeight: '600' |
|
}, |
|
thClickable: { |
|
padding: '16px 24px', |
|
textAlign: 'left', |
|
color: 'white', |
|
fontWeight: '600', |
|
cursor: 'pointer' |
|
}, |
|
tr: { |
|
borderBottom: '1px solid rgba(255, 255, 255, 0.05)' |
|
}, |
|
td: { |
|
padding: '16px 24px' |
|
}, |
|
progressBar: { |
|
width: '100%', |
|
background: '#374151', |
|
borderRadius: '4px', |
|
height: '8px' |
|
}, |
|
progressFill: { |
|
background: 'linear-gradient(to right, #3b82f6, #8b5cf6)', |
|
height: '8px', |
|
borderRadius: '4px', |
|
transition: 'width 0.5s ease' |
|
}, |
|
badge: { |
|
padding: '4px 8px', |
|
borderRadius: '12px', |
|
fontSize: '12px', |
|
fontWeight: '500' |
|
}, |
|
badgeBlue: { |
|
background: 'rgba(59, 130, 246, 0.2)', |
|
color: '#93c5fd' |
|
}, |
|
badgeGreen: { |
|
background: 'rgba(34, 197, 94, 0.2)', |
|
color: '#86efac' |
|
}, |
|
badgePurple: { |
|
background: 'rgba(147, 51, 234, 0.2)', |
|
color: '#c4b5fd' |
|
}, |
|
infoSection: { |
|
marginTop: '32px', |
|
display: 'flex', |
|
flexDirection: 'column', |
|
gap: '24px' |
|
}, |
|
infoCard: { |
|
background: 'rgba(255, 255, 255, 0.1)', |
|
backdropFilter: 'blur(10px)', |
|
border: '1px solid rgba(255, 255, 255, 0.2)', |
|
borderRadius: '12px', |
|
padding: '24px' |
|
}, |
|
academicCard: { |
|
background: 'rgba(255, 255, 255, 0.1)', |
|
backdropFilter: 'blur(10px)', |
|
border: '1px solid rgba(255, 255, 255, 0.2)', |
|
borderRadius: '12px', |
|
padding: '16px', |
|
textAlign: 'center' |
|
}, |
|
loadingContainer: { |
|
minHeight: '100vh', |
|
background: 'linear-gradient(135deg, #0f172a 0%, #1e3a8a 50%, #3730a3 100%)', |
|
display: 'flex', |
|
alignItems: 'center', |
|
justifyContent: 'center' |
|
}, |
|
spinner: { |
|
width: '64px', |
|
height: '64px', |
|
border: '2px solid transparent', |
|
borderBottom: '2px solid white', |
|
borderRadius: '50%', |
|
animation: 'spin 1s linear infinite', |
|
margin: '0 auto 16px' |
|
} |
|
}; |
|
|
|
if (loading) { |
|
return React.createElement('div', { style: styles.loadingContainer }, |
|
React.createElement('div', { style: { textAlign: 'center' } }, |
|
React.createElement('div', { style: styles.spinner }), |
|
React.createElement('p', { style: { color: 'white', fontSize: '20px' } }, 'Loading leaderboard...') |
|
) |
|
); |
|
} |
|
|
|
return React.createElement('div', null, |
|
React.createElement('style', null, ` |
|
@keyframes spin { |
|
0% { transform: rotate(0deg); } |
|
100% { transform: rotate(360deg); } |
|
} |
|
.rotate { |
|
animation: spin 1s linear infinite; |
|
} |
|
.hover-row:hover { |
|
background: rgba(255, 255, 255, 0.05); |
|
} |
|
input::placeholder { |
|
color: #9ca3af; |
|
} |
|
select option { |
|
background: #1f2937; |
|
color: white; |
|
} |
|
`), |
|
React.createElement('div', { style: styles.container }, |
|
React.createElement('div', { style: styles.innerContainer }, |
|
React.createElement('div', { style: styles.header }, |
|
React.createElement('h1', { style: styles.title }, '🏆 Urology AI Leaderboard'), |
|
React.createElement('p', { style: styles.subtitle }, 'Evaluating Natural Language Models on Urology Knowledge Assessment'), |
|
React.createElement('div', { style: styles.statsContainer }, |
|
React.createElement('div', { style: styles.statCard }, |
|
React.createElement('div', { style: styles.statNumber }, data.length), |
|
React.createElement('div', { style: styles.statLabel }, 'Models') |
|
), |
|
React.createElement('div', { style: styles.statCard }, |
|
React.createElement('div', { style: styles.statNumber }, '151'), |
|
React.createElement('div', { style: styles.statLabel }, 'Questions') |
|
), |
|
React.createElement('div', { style: styles.statCard }, |
|
React.createElement('div', { style: styles.statNumber }, 'SAS Urology'), |
|
React.createElement('div', { style: styles.statLabel }, 'Specialty') |
|
) |
|
) |
|
), |
|
React.createElement('div', { style: styles.controlsContainer }, |
|
React.createElement('div', { style: styles.inputGroup }, |
|
React.createElement('div', { style: styles.inputContainer }, |
|
React.createElement('span', { style: styles.inputIcon }, '🔍'), |
|
React.createElement('input', { |
|
type: 'text', |
|
placeholder: 'Search model...', |
|
value: searchTerm, |
|
onChange: (e) => setSearchTerm(e.target.value), |
|
style: styles.input |
|
}) |
|
), |
|
React.createElement('div', { style: styles.inputContainer }, |
|
React.createElement('span', { style: styles.inputIcon }, '📋'), |
|
React.createElement('select', { |
|
value: filterLicense, |
|
onChange: (e) => setFilterLicense(e.target.value), |
|
style: styles.select |
|
}, |
|
React.createElement('option', { value: 'all' }, 'All licenses'), |
|
React.createElement('option', { value: 'API Service' }, 'API Service'), |
|
React.createElement('option', { value: 'Apache 2.0' }, 'Apache 2.0'), |
|
React.createElement('option', { value: 'MIT' }, 'MIT') |
|
) |
|
) |
|
), |
|
React.createElement('button', { |
|
onClick: refreshData, |
|
disabled: refreshing, |
|
style: { ...styles.updateButton, opacity: refreshing ? 0.5 : 1 } |
|
}, |
|
React.createElement('span', { className: refreshing ? 'rotate' : '' }, '🔄'), |
|
React.createElement('span', null, refreshing ? 'Updating...' : 'Update') |
|
) |
|
), |
|
React.createElement('div', { style: styles.tableCard }, |
|
React.createElement('div', { style: styles.tableContainer }, |
|
React.createElement('div', { style: styles.scrollContainer }, |
|
React.createElement('table', { style: styles.table }, |
|
React.createElement('thead', { style: styles.tableHeader }, |
|
React.createElement('tr', null, |
|
React.createElement('th', { style: styles.th }, 'Rank'), |
|
React.createElement('th', { style: styles.th }, 'Model'), |
|
React.createElement('th', { |
|
style: styles.thClickable, |
|
onClick: () => { |
|
if (sortBy === 'accuracy') { |
|
setSortOrder(sortOrder === 'desc' ? 'asc' : 'desc'); |
|
} else { |
|
setSortBy('accuracy'); |
|
setSortOrder('desc'); |
|
} |
|
} |
|
}, |
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '4px' } }, |
|
React.createElement('span', null, '🎯'), |
|
React.createElement('span', null, 'Accuracy'), |
|
getSortIcon('accuracy') |
|
) |
|
), |
|
React.createElement('th', { style: styles.th }, 'Answers'), |
|
React.createElement('th', { style: styles.th }, 'Parameters'), |
|
React.createElement('th', { style: styles.th }, 'License'), |
|
React.createElement('th', { |
|
style: styles.thClickable, |
|
onClick: () => { |
|
if (sortBy === 'submittedTime') { |
|
setSortOrder(sortOrder === 'desc' ? 'asc' : 'desc'); |
|
} else { |
|
setSortBy('submittedTime'); |
|
setSortOrder('desc'); |
|
} |
|
} |
|
}, |
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '4px' } }, |
|
React.createElement('span', null, '🕒'), |
|
React.createElement('span', null, 'Date'), |
|
getSortIcon('submittedTime') |
|
) |
|
) |
|
) |
|
), |
|
React.createElement('tbody', null, |
|
filteredAndSortedData.map((item, index) => |
|
React.createElement('tr', { |
|
key: item.model, |
|
style: styles.tr, |
|
className: 'hover-row' |
|
}, |
|
React.createElement('td', { style: styles.td }, getRankIcon(index)), |
|
React.createElement('td', { style: styles.td }, |
|
React.createElement('div', { style: { display: 'flex', flexDirection: 'column' } }, |
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, |
|
React.createElement('span', { style: { color: 'white', fontWeight: '500' } }, item.model.split('/').pop()), |
|
React.createElement('span', { style: { color: '#9ca3af', cursor: 'pointer' } }, '🔗') |
|
), |
|
React.createElement('span', { style: { fontSize: '14px', color: '#9ca3af' } }, item.model.split('/')[0]) |
|
) |
|
), |
|
React.createElement('td', { style: styles.td }, |
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '12px' } }, |
|
React.createElement('div', { style: { flex: 1 } }, |
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '4px' } }, |
|
React.createElement('span', { style: { color: 'white', fontWeight: '600' } }, formatAccuracy(item.accuracy)) |
|
), |
|
React.createElement('div', { style: styles.progressBar }, |
|
React.createElement('div', { |
|
style: { ...styles.progressFill, width: `${item.accuracy * 100}%` } |
|
}) |
|
) |
|
) |
|
) |
|
), |
|
React.createElement('td', { style: styles.td }, |
|
React.createElement('div', { style: { textAlign: 'center' } }, |
|
React.createElement('div', { style: { color: 'white', fontWeight: '600' } }, `${item.correctAnswers}/${item.totalQuestions}`), |
|
React.createElement('div', { style: { fontSize: '14px', color: '#9ca3af' } }, 'correct') |
|
) |
|
), |
|
React.createElement('td', { style: styles.td }, |
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, |
|
React.createElement('span', null, '⚡'), |
|
React.createElement('span', { style: { color: 'white' } }, formatParams(item.params)) |
|
) |
|
), |
|
React.createElement('td', { style: styles.td }, |
|
React.createElement('span', { |
|
style: { |
|
...styles.badge, |
|
...(item.license === 'API Service' ? styles.badgeBlue : |
|
item.license === 'Apache 2.0' ? styles.badgeGreen : styles.badgePurple) |
|
} |
|
}, item.license) |
|
), |
|
React.createElement('td', { style: styles.td }, |
|
React.createElement('div', { style: { fontSize: '14px', color: '#d1d5db' } }, formatDate(item.submittedTime)) |
|
) |
|
) |
|
) |
|
) |
|
) |
|
) |
|
) |
|
), |
|
React.createElement('div', { style: styles.infoSection }, |
|
React.createElement('div', { style: styles.infoCard }, |
|
React.createElement('h3', { style: { fontSize: '20px', fontWeight: '600', color: 'white', marginBottom: '12px' } }, '📊 About This Evaluation'), |
|
React.createElement('p', { style: { color: '#d1d5db', marginBottom: '12px' } }, 'This leaderboard evaluates natural language models on their ability to answer urology questions. Models must respond to multiple-choice questions about urological knowledge, demonstrating their understanding and mastery of this medical specialty.'), |
|
React.createElement('p', { style: { color: '#d1d5db', marginBottom: '16px' } }, |
|
'Questions are from the ', |
|
React.createElement('a', { |
|
href: 'https://www.sspa.juntadeandalucia.es/servicioandaluzdesalud/', |
|
target: '_blank', |
|
rel: 'noopener noreferrer', |
|
style: { color: '#60a5fa', textDecoration: 'none', fontWeight: '600' } |
|
}, 'SAS (Servicio Andaluz de Salud)'), |
|
' for the ', |
|
React.createElement('a', { |
|
href: 'https://www.sspa.juntadeandalucia.es/servicioandaluzdesalud/profesionales/ofertas-de-empleo/oferta-de-empleo-publico-puestos-base/oep-extraordinaria-decreto-ley-122022-centros-sas/cuadro-de-evolucion-concurso-oposicion-centros-sas/fea-urologia', |
|
target: '_blank', |
|
rel: 'noopener noreferrer', |
|
style: { color: '#60a5fa', textDecoration: 'none', fontWeight: '600' } |
|
}, React.createElement('strong', null, 'Convocatoria Concurso Oposición')), |
|
' - specialized medical examination for urology residents.' |
|
), |
|
React.createElement('div', { style: { display: 'flex', justifyContent: 'center', color: '#d1d5db' } }, |
|
React.createElement('span', null, 'Dataset: ', React.createElement('a', { |
|
href: 'https://huggingface.co/datasets/SASLeaderboard/results', |
|
style: { color: '#60a5fa', textDecoration: 'none' }, |
|
target: '_blank', |
|
rel: 'noopener noreferrer' |
|
}, 'SASLeaderboard/results')) |
|
) |
|
), |
|
React.createElement('div', { style: styles.academicCard }, |
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '20px', flexWrap: 'wrap' } }, |
|
React.createElement('a', { |
|
href: 'https://iesrafaelalberti.es/', |
|
target: '_blank', |
|
rel: 'noopener noreferrer', |
|
style: { textDecoration: 'none', flexShrink: 0 } |
|
}, |
|
React.createElement('img', { |
|
src: 'https://avatars.githubusercontent.com/u/79144080?s=200&v=4', |
|
alt: 'IES Rafael Alberti Logo', |
|
style: { |
|
width: '80px', |
|
height: '80px', |
|
cursor: 'pointer', |
|
background: 'white', |
|
borderRadius: '8px', |
|
padding: '4px' |
|
} |
|
}) |
|
), |
|
React.createElement('div', { style: { textAlign: 'center', flex: 1, minWidth: '200px' } }, |
|
React.createElement('h4', { style: { fontSize: '18px', fontWeight: '600', color: 'white', marginBottom: '16px', margin: '0 0 16px 0' } }, '🎓 Academic Project'), |
|
React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: '8px' } }, |
|
React.createElement('p', { style: { color: '#d1d5db', fontSize: '14px', margin: 0 } }, |
|
'Final project for ', |
|
React.createElement('a', { |
|
href: 'https://iesrafaelalberti.es/', |
|
target: '_blank', |
|
rel: 'noopener noreferrer', |
|
style: { color: '#60a5fa', textDecoration: 'none', fontWeight: '600' } |
|
}, 'IES Rafael Alberti') |
|
), |
|
React.createElement('p', { style: { color: '#d1d5db', fontSize: '14px', margin: 0 } }, |
|
'Course: ', |
|
React.createElement('a', { |
|
href: 'https://iesrafaelalberti.es/curso-especializacion-inteligencia-artificial-y-big-datatarde/', |
|
target: '_blank', |
|
rel: 'noopener noreferrer', |
|
style: { color: '#60a5fa', textDecoration: 'none', fontWeight: '600' } |
|
}, 'Artificial Intelligence and Big Data') |
|
), |
|
React.createElement('p', { style: { color: '#d1d5db', fontSize: '14px', margin: 0 } }, |
|
'Subject: Artificial Intelligence Models' |
|
) |
|
) |
|
), |
|
React.createElement('a', { |
|
href: 'https://iesrafaelalberti.es/curso-especializacion-inteligencia-artificial-y-big-datatarde/', |
|
target: '_blank', |
|
rel: 'noopener noreferrer', |
|
style: { textDecoration: 'none', flexShrink: 0 } |
|
}, |
|
React.createElement('img', { |
|
src: '/IA-Big-Data-cuadrado.png', |
|
alt: 'AI and Big Data Course Logo', |
|
style: { |
|
width: '80px', |
|
height: '80px', |
|
cursor: 'pointer', |
|
background: 'white', |
|
borderRadius: '8px', |
|
padding: '4px' |
|
}, |
|
onError: (e) => { |
|
console.log('Error loading course image from public folder'); |
|
}, |
|
onLoad: () => { |
|
console.log('Course image loaded successfully from public folder'); |
|
} |
|
}) |
|
) |
|
) |
|
) |
|
) |
|
) |
|
) |
|
); |
|
}; |
|
|
|
export default UrologyLeaderboard; |