davidpomerenke commited on
Commit
53d2039
·
verified ·
1 Parent(s): 0fff076

Upload from GitHub Actions: Improve UX and style

Browse files
evals/backend.py CHANGED
@@ -54,6 +54,9 @@ def make_model_table(df, models):
54
  df["task_metric"] = df["task"] + "_" + df["metric"]
55
  df = df.drop(columns=["task", "metric"])
56
  df = df.pivot(index="model", columns="task_metric", values="score")
 
 
 
57
  df["average"] = compute_normalized_average(df, task_metrics_basic)
58
  df = df.sort_values(by="average", ascending=False).reset_index()
59
  df = pd.merge(df, models, left_on="model", right_on="id", how="left")
@@ -86,6 +89,9 @@ def make_language_table(df, languages):
86
  df["task_metric"] = df["task"] + "_" + df["metric"]
87
  df = df.drop(columns=["task", "metric"])
88
  df = df.pivot(index="bcp_47", columns="task_metric", values="score").reset_index()
 
 
 
89
  df["average"] = compute_normalized_average(df, task_metrics_basic)
90
  df = pd.merge(languages, df, on="bcp_47", how="outer")
91
  df = df.sort_values(by="speakers", ascending=False)
 
54
  df["task_metric"] = df["task"] + "_" + df["metric"]
55
  df = df.drop(columns=["task", "metric"])
56
  df = df.pivot(index="model", columns="task_metric", values="score")
57
+ for metric in task_metrics:
58
+ if metric not in df.columns:
59
+ df[metric] = np.nan
60
  df["average"] = compute_normalized_average(df, task_metrics_basic)
61
  df = df.sort_values(by="average", ascending=False).reset_index()
62
  df = pd.merge(df, models, left_on="model", right_on="id", how="left")
 
89
  df["task_metric"] = df["task"] + "_" + df["metric"]
90
  df = df.drop(columns=["task", "metric"])
91
  df = df.pivot(index="bcp_47", columns="task_metric", values="score").reset_index()
92
+ for metric in task_metrics:
93
+ if metric not in df.columns:
94
+ df[metric] = np.nan
95
  df["average"] = compute_normalized_average(df, task_metrics_basic)
96
  df = pd.merge(languages, df, on="bcp_47", how="outer")
97
  df = df.sort_values(by="speakers", ascending=False)
frontend/src/App.js CHANGED
@@ -94,10 +94,23 @@ function App () {
94
  🌍
95
  </span>
96
  </div>
97
- <h1 style={{ fontSize: '2.5rem', fontWeight: '700' }}>
 
 
 
 
 
 
98
  AI Language Proficiency Monitor
99
  </h1>
100
- <p style={{ fontSize: '1.10rem', color: '#555', marginTop: '0', marginBottom: '2rem' }}>
 
 
 
 
 
 
 
101
  Comprehensive multilingual evaluation results for AI language models
102
  </p>
103
 
@@ -155,13 +168,18 @@ function App () {
155
  {data && (
156
  <>
157
  <div style={{ width: '100%' }}>
158
- <ModelTable data={data.model_table} />
 
 
 
 
159
  </div>
160
  <div style={{ width: '100%' }}>
161
  <LanguageTable
162
  data={data.language_table}
163
  selectedLanguages={selectedLanguages}
164
  setSelectedLanguages={setSelectedLanguages}
 
165
  />
166
  </div>
167
  <div style={{ width: '100%' }}>
@@ -272,7 +290,7 @@ function App () {
272
  style={{ width: '90vw', height: '90vh' }}
273
  maximizable
274
  modal
275
- header="Interactive Visualizations"
276
  >
277
  {data && (
278
  <div style={{ width: '100%', height: '100%' }}>
 
94
  🌍
95
  </span>
96
  </div>
97
+ <h1 style={{
98
+ fontSize: '2.5rem',
99
+ fontWeight: '600',
100
+ margin: '1rem 0 0.5rem 0',
101
+ color: '#333',
102
+ letterSpacing: '-0.01em'
103
+ }}>
104
  AI Language Proficiency Monitor
105
  </h1>
106
+ <p style={{
107
+ fontSize: '1.1rem',
108
+ color: '#666',
109
+ margin: '0 0 2.5rem 0',
110
+ fontWeight: '400',
111
+ maxWidth: '700px',
112
+ lineHeight: '1.5'
113
+ }}>
114
  Comprehensive multilingual evaluation results for AI language models
115
  </p>
116
 
 
168
  {data && (
169
  <>
170
  <div style={{ width: '100%' }}>
171
+ <ModelTable
172
+ data={data.model_table}
173
+ selectedLanguages={selectedLanguages}
174
+ allLanguages={data.language_table || []}
175
+ />
176
  </div>
177
  <div style={{ width: '100%' }}>
178
  <LanguageTable
179
  data={data.language_table}
180
  selectedLanguages={selectedLanguages}
181
  setSelectedLanguages={setSelectedLanguages}
182
+ totalModels={data.model_table?.length || 0}
183
  />
184
  </div>
185
  <div style={{ width: '100%' }}>
 
290
  style={{ width: '90vw', height: '90vh' }}
291
  maximizable
292
  modal
293
+ header={null}
294
  >
295
  {data && (
296
  <div style={{ width: '100%', height: '100%' }}>
frontend/src/components/AutoComplete.js CHANGED
@@ -1,102 +1,255 @@
1
- import { AutoComplete as PrimeAutoComplete } from 'primereact/autocomplete'
2
- import { useState } from 'react'
3
- const AutoComplete = ({ languages, onComplete }) => {
4
- const [autoComplete, setAutoComplete] = useState('')
5
- const [suggestions, setSuggestions] = useState([])
6
 
7
- const exampleCodes = ['sw','ar', 'hi', 'en']
8
- // const exampleCodes = ['ar', 'hi', 'sw', 'fa']
9
- const exampleLanguages = exampleCodes.map(code =>
10
- languages?.find(item => item.bcp_47 === code)
11
- )
 
 
12
 
13
- const search = e => {
14
- const matches = languages.filter(language => {
15
- const query = e.query.toLowerCase()
16
- return (
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  language.language_name.toLowerCase().includes(query) ||
18
  language.autonym.toLowerCase().includes(query) ||
19
  language.bcp_47.toLowerCase().includes(query)
20
  )
21
- })
22
- setSuggestions(matches)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
 
25
- const itemTemplate = item => (
26
- <div
27
- style={{
28
- display: 'flex',
29
- flexDirection: 'row',
30
- justifyContent: 'space-between'
31
- }}
32
- >
33
- <span>
34
- {item.autonym}
35
- <span style={{ color: 'gray', marginLeft: '1rem' }}>
36
- {item.language_name}
37
- </span>
38
- </span>
39
- <span style={{ color: 'gray' }}>{item.bcp_47}</span>
40
- </div>
41
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  return (
44
- <>
45
- <PrimeAutoComplete
46
- placeholder='Search for language-specific leaderboards...'
47
- value={autoComplete}
48
- onChange={e => setAutoComplete(e.value)}
49
- onClick={() => {
50
- setAutoComplete('')
51
- setSuggestions(languages)
52
  }}
53
- onSelect={e => {
54
- setAutoComplete(e.value.language_name)
55
- onComplete([e.value])
56
- }}
57
- suggestions={suggestions}
58
- completeMethod={search}
59
- virtualScrollerOptions={{ itemSize: 50 }} // smaller values give layout problems
60
- delay={500}
61
- autoHighlight
62
- autoFocus
63
- itemTemplate={itemTemplate}
64
- field='language_name'
65
- minLength={0}
66
- />
67
- <span
68
- style={{
69
- display: 'flex',
70
- flexWrap: 'wrap',
71
- gap: '1rem',
72
- rowGap: '0.3rem',
73
- marginTop: '1rem',
74
- maxWidth: '600px',
75
- justifyContent: 'center',
76
- color: '#555',
77
- fontSize: '0.8rem'
78
  }}
79
  >
80
- <span>Examples:</span>
81
- {exampleLanguages?.map(language => (
82
- <a
83
- onClick={() => {
84
- onComplete([language])
85
- setAutoComplete(language.language_name)
86
- }}
87
- style={{ textDecoration: 'underline', cursor: 'pointer' }}
88
- >
89
- {language.language_name} Leaderboard
90
- </a>
91
- ))}
92
- {/* <li>African Leaderboard</li>
93
- <li>Indic Leaderboard</li>
94
- <li>Transcription Leaderboard</li>
95
- <li>Dataset Availability for African Languages</li>
96
- <li>GPT 4.5 Evaluation</li>
97
- <li>MMLU Evaluation of Open Models</li> */}
98
- </span>
99
- </>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  )
101
  }
102
 
 
1
+ import { useState, useEffect, useRef } from 'react'
 
 
 
 
2
 
3
+ const AutoComplete = ({ languages, onComplete }) => {
4
+ const [isOpen, setIsOpen] = useState(false)
5
+ const [searchTerm, setSearchTerm] = useState('')
6
+ const [selectedLanguage, setSelectedLanguage] = useState(null)
7
+ const [filteredLanguages, setFilteredLanguages] = useState([])
8
+ const dropdownRef = useRef(null)
9
+ const inputRef = useRef(null)
10
 
11
+ // Most spoken languages (by number of speakers) - you can adjust this list
12
+ const mostSpokenCodes = ['en', 'zh', 'hi', 'es', 'ar', 'bn', 'pt', 'ru', 'ja', 'pa', 'de', 'jv', 'ko', 'fr', 'te', 'mr', 'tr', 'ta', 'vi', 'ur']
13
+
14
+ useEffect(() => {
15
+ if (!languages) return
16
+
17
+ if (searchTerm.trim() === '') {
18
+ // Show most spoken languages first, then others
19
+ const mostSpoken = mostSpokenCodes
20
+ .map(code => languages.find(lang => lang.bcp_47 === code))
21
+ .filter(Boolean)
22
+
23
+ const others = languages
24
+ .filter(lang => !mostSpokenCodes.includes(lang.bcp_47))
25
+ .sort((a, b) => a.language_name.localeCompare(b.language_name))
26
+
27
+ setFilteredLanguages([...mostSpoken, ...others])
28
+ } else {
29
+ const query = searchTerm.toLowerCase()
30
+ const matches = languages.filter(language =>
31
  language.language_name.toLowerCase().includes(query) ||
32
  language.autonym.toLowerCase().includes(query) ||
33
  language.bcp_47.toLowerCase().includes(query)
34
  )
35
+ setFilteredLanguages(matches)
36
+ }
37
+ }, [searchTerm, languages, mostSpokenCodes])
38
+
39
+ useEffect(() => {
40
+ const handleClickOutside = (event) => {
41
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
42
+ setIsOpen(false)
43
+ setSearchTerm('')
44
+ }
45
+ }
46
+
47
+ document.addEventListener('mousedown', handleClickOutside)
48
+ return () => document.removeEventListener('mousedown', handleClickOutside)
49
+ }, [])
50
+
51
+ const handleSelect = (language) => {
52
+ setSelectedLanguage(language)
53
+ setIsOpen(false)
54
+ setSearchTerm('')
55
+ onComplete([language])
56
  }
57
 
58
+ const handleClear = (e) => {
59
+ e.stopPropagation()
60
+ setSelectedLanguage(null)
61
+ onComplete([])
62
+ }
63
+
64
+ const handleContainerClick = () => {
65
+ setIsOpen(true)
66
+ if (!selectedLanguage) {
67
+ setTimeout(() => inputRef.current?.focus(), 100)
68
+ }
69
+ }
70
+
71
+ const handleInputChange = (e) => {
72
+ // If user starts typing while a language is selected, clear the selection to enable search
73
+ if (selectedLanguage && e.target.value.length > 0) {
74
+ setSelectedLanguage(null)
75
+ onComplete([])
76
+ }
77
+ setSearchTerm(e.target.value)
78
+ }
79
+
80
+ const handleKeyDown = (e) => {
81
+ if (e.key === 'Escape') {
82
+ setIsOpen(false)
83
+ setSearchTerm('')
84
+ }
85
+ }
86
+
87
+ const containerStyle = {
88
+ position: 'relative',
89
+ display: 'inline-block',
90
+ minWidth: '400px',
91
+ maxWidth: '600px'
92
+ }
93
+
94
+ const buttonStyle = {
95
+ color: selectedLanguage ? '#333' : '#666',
96
+ border: '1px solid #ddd',
97
+ padding: '0.75rem 1rem',
98
+ borderRadius: '4px',
99
+ fontSize: '0.95rem',
100
+ backgroundColor: '#fff',
101
+ cursor: 'pointer',
102
+ display: 'flex',
103
+ alignItems: 'center',
104
+ justifyContent: 'space-between',
105
+ width: '100%',
106
+ minHeight: '44px',
107
+ transition: 'border-color 0.2s ease, box-shadow 0.2s ease'
108
+ }
109
+
110
+ const inputStyle = {
111
+ border: 'none',
112
+ outline: 'none',
113
+ fontSize: '0.95rem',
114
+ width: '100%',
115
+ backgroundColor: 'transparent',
116
+ color: '#333'
117
+ }
118
+
119
+ const dropdownStyle = {
120
+ position: 'absolute',
121
+ top: '100%',
122
+ left: 0,
123
+ right: 0,
124
+ backgroundColor: '#fff',
125
+ border: '1px solid #ddd',
126
+ borderTop: 'none',
127
+ borderRadius: '0 0 4px 4px',
128
+ maxHeight: '300px',
129
+ overflowY: 'auto',
130
+ zIndex: 1000,
131
+ boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
132
+ }
133
+
134
+ const itemStyle = {
135
+ padding: '0.75rem 1rem',
136
+ cursor: 'pointer',
137
+ borderBottom: '1px solid #f0f0f0',
138
+ display: 'flex',
139
+ justifyContent: 'space-between',
140
+ alignItems: 'center',
141
+ transition: 'background-color 0.2s ease'
142
+ }
143
+
144
+ const clearButtonStyle = {
145
+ background: 'none',
146
+ border: 'none',
147
+ color: '#999',
148
+ cursor: 'pointer',
149
+ padding: '0.25rem',
150
+ borderRadius: '50%',
151
+ display: 'flex',
152
+ alignItems: 'center',
153
+ justifyContent: 'center',
154
+ width: '20px',
155
+ height: '20px',
156
+ fontSize: '14px',
157
+ marginLeft: '0.5rem'
158
+ }
159
 
160
  return (
161
+ <div style={containerStyle} ref={dropdownRef}>
162
+ <div
163
+ style={buttonStyle}
164
+ onClick={handleContainerClick}
165
+ onMouseEnter={(e) => {
166
+ if (!selectedLanguage) {
167
+ e.target.style.borderColor = '#bbb'
168
+ }
169
  }}
170
+ onMouseLeave={(e) => {
171
+ e.target.style.borderColor = '#ddd'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  }}
173
  >
174
+ {selectedLanguage && !isOpen ? (
175
+ <>
176
+ <span style={{ fontWeight: '500' }}>
177
+ {selectedLanguage.language_name} Leaderboard
178
+ </span>
179
+ <button
180
+ style={clearButtonStyle}
181
+ onClick={handleClear}
182
+ onMouseEnter={(e) => {
183
+ e.target.style.backgroundColor = '#f0f0f0'
184
+ }}
185
+ onMouseLeave={(e) => {
186
+ e.target.style.backgroundColor = 'transparent'
187
+ }}
188
+ title="View overall leaderboard"
189
+ >
190
+ ×
191
+ </button>
192
+ </>
193
+ ) : isOpen ? (
194
+ <input
195
+ ref={inputRef}
196
+ style={inputStyle}
197
+ placeholder={selectedLanguage ? "Type to search other languages..." : "Type to search languages..."}
198
+ value={searchTerm}
199
+ onChange={handleInputChange}
200
+ onKeyDown={handleKeyDown}
201
+ />
202
+ ) : (
203
+ <span>Search for language-specific leaderboards...</span>
204
+ )}
205
+ {(!selectedLanguage || isOpen) && (
206
+ <span style={{ color: '#999', fontSize: '12px' }}>
207
+ {isOpen ? '▲' : '▼'}
208
+ </span>
209
+ )}
210
+ </div>
211
+
212
+ {isOpen && (
213
+ <div style={dropdownStyle}>
214
+ {filteredLanguages.length === 0 ? (
215
+ <div style={{ ...itemStyle, color: '#999', cursor: 'default' }}>
216
+ No languages found
217
+ </div>
218
+ ) : (
219
+ filteredLanguages.slice(0, 20).map((language, index) => (
220
+ <div
221
+ key={language.bcp_47}
222
+ style={itemStyle}
223
+ onClick={() => handleSelect(language)}
224
+ onMouseEnter={(e) => {
225
+ e.target.style.backgroundColor = '#f8f9fa'
226
+ }}
227
+ onMouseLeave={(e) => {
228
+ e.target.style.backgroundColor = 'transparent'
229
+ }}
230
+ >
231
+ <div>
232
+ <div style={{ fontWeight: '500', marginBottom: '2px' }}>
233
+ {language.language_name} Leaderboard
234
+ </div>
235
+ <div style={{ fontSize: '0.85rem', color: '#666' }}>
236
+ {language.autonym}
237
+ </div>
238
+ </div>
239
+ <div style={{ color: '#999', fontSize: '0.8rem' }}>
240
+ {language.bcp_47}
241
+ </div>
242
+ </div>
243
+ ))
244
+ )}
245
+ {filteredLanguages.length > 20 && (
246
+ <div style={{ ...itemStyle, color: '#999', cursor: 'default', fontStyle: 'italic' }}>
247
+ ... and {filteredLanguages.length - 20} more languages
248
+ </div>
249
+ )}
250
+ </div>
251
+ )}
252
+ </div>
253
  )
254
  }
255
 
frontend/src/components/DatasetTable.js CHANGED
@@ -103,7 +103,14 @@ const DatasetTable = ({ data }) => {
103
  return <div style={{ fontWeight: 'bold' }}>{rowData.group}</div>
104
  }}
105
  groupRowsBy='group'
106
- header={<>Datasets</>}
 
 
 
 
 
 
 
107
  removableSort
108
  filters={filters}
109
  filterDisplay='menu'
 
103
  return <div style={{ fontWeight: 'bold' }}>{rowData.group}</div>
104
  }}
105
  groupRowsBy='group'
106
+ header={
107
+ <span>
108
+ <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>Datasets</span>
109
+ <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
110
+ Multilingual datasets used for this evaluation, and other suitable datasets
111
+ </span>
112
+ </span>
113
+ }
114
  removableSort
115
  filters={filters}
116
  filterDisplay='menu'
frontend/src/components/LanguageTable.js CHANGED
@@ -6,7 +6,7 @@ import { useState, useEffect } from 'react'
6
  import { Slider } from 'primereact/slider'
7
  import ScoreColumns from './ScoreColumns'
8
 
9
- const LanguageTable = ({ data, selectedLanguages, setSelectedLanguages }) => {
10
  const [filters, setFilters] = useState({
11
  language_name: { value: null, matchMode: FilterMatchMode.EQUALS }, // via global search
12
  family: { value: null, matchMode: FilterMatchMode.IN },
@@ -122,7 +122,14 @@ const LanguageTable = ({ data, selectedLanguages, setSelectedLanguages }) => {
122
  value={data.filter(
123
  item => !selectedLanguages.some(l => l.bcp_47 === item.bcp_47)
124
  )}
125
- header={<>Languages</>}
 
 
 
 
 
 
 
126
  sortField='speakers'
127
  removableSort
128
  filters={filters}
 
6
  import { Slider } from 'primereact/slider'
7
  import ScoreColumns from './ScoreColumns'
8
 
9
+ const LanguageTable = ({ data, selectedLanguages, setSelectedLanguages, totalModels = 0 }) => {
10
  const [filters, setFilters] = useState({
11
  language_name: { value: null, matchMode: FilterMatchMode.EQUALS }, // via global search
12
  family: { value: null, matchMode: FilterMatchMode.IN },
 
122
  value={data.filter(
123
  item => !selectedLanguages.some(l => l.bcp_47 === item.bcp_47)
124
  )}
125
+ header={
126
+ <span>
127
+ <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>Languages</span>
128
+ <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
129
+ Average performance of {totalModels} evaluated AI models
130
+ </span>
131
+ </span>
132
+ }
133
  sortField='speakers'
134
  removableSort
135
  filters={filters}
frontend/src/components/ModelTable.js CHANGED
@@ -6,7 +6,7 @@ import { useState, useEffect } from 'react'
6
  import Medal from './Medal'
7
  import { Slider } from 'primereact/slider'
8
  import ScoreColumns from './ScoreColumns'
9
- const ModelTable = ({ data }) => {
10
  const [filters, setFilters] = useState({
11
  type: { value: null, matchMode: FilterMatchMode.IN },
12
  size: { value: null, matchMode: FilterMatchMode.BETWEEN },
@@ -150,10 +150,47 @@ const ModelTable = ({ data }) => {
150
  return <div style={{ textAlign: 'center' }}>${rowData.cost?.toFixed(2)}</div>
151
  }
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  return (
154
  <DataTable
155
  value={data}
156
- header={<>AI Models</>}
157
  sortField='average'
158
  removableSort
159
  filters={filters}
 
6
  import Medal from './Medal'
7
  import { Slider } from 'primereact/slider'
8
  import ScoreColumns from './ScoreColumns'
9
+ const ModelTable = ({ data, selectedLanguages = [], allLanguages = [] }) => {
10
  const [filters, setFilters] = useState({
11
  type: { value: null, matchMode: FilterMatchMode.IN },
12
  size: { value: null, matchMode: FilterMatchMode.BETWEEN },
 
150
  return <div style={{ textAlign: 'center' }}>${rowData.cost?.toFixed(2)}</div>
151
  }
152
 
153
+ const getHeaderText = () => {
154
+ // Count languages that have evaluation data (average score available)
155
+ const evaluatedLanguagesCount = allLanguages.filter(lang =>
156
+ lang.average !== null && lang.average !== undefined
157
+ ).length
158
+
159
+ if (selectedLanguages.length === 0) {
160
+ return (
161
+ <span>
162
+ <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>AI Models</span>
163
+ <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
164
+ Average performance across {evaluatedLanguagesCount} evaluated languages
165
+ </span>
166
+ </span>
167
+ )
168
+ } else if (selectedLanguages.length === 1) {
169
+ return (
170
+ <span>
171
+ <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>AI Models</span>
172
+ <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
173
+ {selectedLanguages[0].language_name} performance
174
+ </span>
175
+ </span>
176
+ )
177
+ } else {
178
+ const languageNames = selectedLanguages.map(lang => lang.language_name).join(', ')
179
+ return (
180
+ <span>
181
+ <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>AI Models</span>
182
+ <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
183
+ Performance for {languageNames}
184
+ </span>
185
+ </span>
186
+ )
187
+ }
188
+ }
189
+
190
  return (
191
  <DataTable
192
  value={data}
193
+ header={<>{getHeaderText()}</>}
194
  sortField='average'
195
  removableSort
196
  filters={filters}
frontend/src/index.css CHANGED
@@ -27,52 +27,87 @@ html, body, #root {
27
 
28
  .p-datatable-wrapper {
29
  scrollbar-color: #f0f0f0 rgba(0, 0, 0, 0);
30
- border-radius: 0 0 10px 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
 
33
  #model-table {
34
- border: 3px solid #5151ff;
35
- border-radius: 10px;
 
36
  }
37
 
38
  #model-table .p-datatable-header {
39
  border-top-width: 0px;
40
- border-radius: 5px 5px 0 0;
41
- background: linear-gradient(to bottom, #5151ff, #0000cd);
42
- color: white;
 
 
 
43
  }
44
 
45
  #language-table {
46
- border: 3px solid #00aa00;
47
- border-radius: 10px;
 
48
  }
49
 
50
  #language-table .p-datatable-header {
51
  border-top-width: 0px;
52
- border-radius: 5px 5px 0 0;
53
- background: linear-gradient(to bottom, #17b417, #008000);
54
- color: white;
 
 
 
55
  }
56
 
57
  #dataset-table {
58
- border: 3px solid #ffaa00;
59
- border-radius: 10px;
 
60
  }
61
 
62
  #dataset-table .p-datatable-header {
63
  border-top-width: 0px;
64
- border-radius: 5px 5px 0 0;
65
- background: linear-gradient(to bottom, #ffaa00, #ff8000);
66
- color: white;
 
 
 
67
  }
68
 
69
  #figure {
70
  width: 100%;
71
  min-width: 600px;
72
  height: 600px;
73
- border: 1px solid #bdbdbd;
74
- border-radius: 10px;
75
- padding: 10px;
 
 
76
  }
77
 
78
  .p-autocomplete-input {
 
27
 
28
  .p-datatable-wrapper {
29
  scrollbar-color: #f0f0f0 rgba(0, 0, 0, 0);
30
+ border-radius: 0 0 6px 6px;
31
+ }
32
+
33
+ .p-datatable .p-datatable-tbody > tr {
34
+ transition: background-color 0.2s ease;
35
+ }
36
+
37
+ .p-datatable .p-datatable-tbody > tr:hover {
38
+ background-color: #fafafa !important;
39
+ }
40
+
41
+ .p-datatable .p-datatable-thead > tr > th {
42
+ border-bottom: 1px solid #e0e0e0;
43
+ background-color: #fafafa;
44
+ font-weight: 500;
45
+ color: #555;
46
+ padding: 0.75rem 1rem;
47
+ }
48
+
49
+ .p-datatable .p-datatable-tbody > tr > td {
50
+ padding: 0.75rem 1rem;
51
+ border-bottom: 1px solid #f5f5f5;
52
  }
53
 
54
  #model-table {
55
+ border: 2px solid #d1d5ff;
56
+ border-radius: 8px;
57
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
58
  }
59
 
60
  #model-table .p-datatable-header {
61
  border-top-width: 0px;
62
+ border-radius: 6px 6px 0 0;
63
+ background: #f0f3ff;
64
+ color: #333;
65
+ border-bottom: 2px solid #d1d5ff;
66
+ padding: 1rem 1.25rem;
67
+ font-weight: 500;
68
  }
69
 
70
  #language-table {
71
+ border: 2px solid #c8f2c8;
72
+ border-radius: 8px;
73
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
74
  }
75
 
76
  #language-table .p-datatable-header {
77
  border-top-width: 0px;
78
+ border-radius: 6px 6px 0 0;
79
+ background: #f0fff0;
80
+ color: #333;
81
+ border-bottom: 2px solid #c8f2c8;
82
+ padding: 1rem 1.25rem;
83
+ font-weight: 500;
84
  }
85
 
86
  #dataset-table {
87
+ border: 2px solid #ffe4cc;
88
+ border-radius: 8px;
89
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
90
  }
91
 
92
  #dataset-table .p-datatable-header {
93
  border-top-width: 0px;
94
+ border-radius: 6px 6px 0 0;
95
+ background: #fff8f0;
96
+ color: #333;
97
+ border-bottom: 2px solid #ffe4cc;
98
+ padding: 1rem 1.25rem;
99
+ font-weight: 500;
100
  }
101
 
102
  #figure {
103
  width: 100%;
104
  min-width: 600px;
105
  height: 600px;
106
+ border: 2px solid #e0e0e0;
107
+ border-radius: 8px;
108
+ padding: 1rem;
109
+ background: #fafafa;
110
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
111
  }
112
 
113
  .p-autocomplete-input {