Skip to content

Commit d066ce9

Browse files
committed
Dynamic tabs instead of always 4
1 parent d676d1d commit d066ce9

1 file changed

Lines changed: 100 additions & 30 deletions

File tree

src/static/riot/competitions/detail/submission_modal.tag

Lines changed: 100 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<div class="submission-modal item" data-tab="admin" if="{submission.admin}">ADMIN</div>
77
<div class="submission-modal item" data-tab="{admin_: submission.admin}fact_sheet">FACT SHEET ANSWERS</div>
88
</div>
9+
<!-- Downloads -->
910
<div class="ui tab active modal-tab" data-tab="{admin_: submission.admin}downloads">
1011
<div class="ui relaxed centered grid">
1112
<div class="ui fifteen wide column">
@@ -36,61 +37,119 @@
3637
</div>
3738
</div>
3839
</div>
40+
<!-- Logs -->
3941
<div class="ui tab modal-tab" data-tab="{admin_: submission.admin}logs" hide="{opts.hide_output}">
40-
<div class="ui top attached inverted pointing menu">
41-
<div class="active submission-modal item" data-tab="{admin_: submission.admin}log_ing_out">
42-
Ingestion output
43-
</div>
44-
<div class="submission-modal item" data-tab="{admin_: submission.admin}log_ing_err">
45-
Ingestion errors
46-
</div>
47-
<div class="submission-modal item" data-tab="{admin_: submission.admin}log_score_out">
48-
Scoring output
49-
</div>
50-
<div class="submission-modal item" data-tab="{admin_: submission.admin}log_score_err">
51-
Scoring errors
52-
</div>
53-
</div>
54-
<div class="ui active bottom attached inverted segment tab log"
55-
data-tab="{admin_: submission.admin}log_ing_out">
56-
<pre class="{empty: isEmpty(logs.prediction_ingestion_stdout)}">{ showLog(logs.prediction_ingestion_stdout) }</pre>
57-
</div>
58-
<div class="ui bottom attached inverted segment tab log"
59-
data-tab="{admin_: submission.admin}log_ing_err">
60-
<pre class="{empty: isEmpty(logs.prediction_ingestion_stderr)}">{ showLog(logs.prediction_ingestion_stderr) }</pre>
42+
<div class="ui top attached inverted pointing menu" if="{logTabs.length > 0}">
43+
<div each="{tab in logTabs}"
44+
class="submission-modal item {active: tab.fullTabId === activeLogTabId}"
45+
data-tab="{tab.fullTabId}">
46+
{tab.label}
47+
</div>
6148
</div>
62-
<div class="ui bottom attached inverted segment tab log"
63-
data-tab="{admin_: submission.admin}log_score_out">
64-
<pre class="{empty: isEmpty(logs.scoring_stdout)}">{ showLog(logs.scoring_stdout) }</pre>
49+
<!-- If no logs -->
50+
<div class="ui bottom attached inverted segment tab log active" if="{logTabs.length === 0}">
51+
<pre class="empty">No logs available for this submission.</pre>
6552
</div>
66-
<div class="ui bottom attached inverted segment tab log"
67-
data-tab="{admin_: submission.admin}log_score_err">
68-
<pre class="{empty: isEmpty(logs.scoring_stderr)}">{ showLog(logs.scoring_stderr) }</pre>
53+
<!-- Dynamic tabs -->
54+
<div each="{tab in logTabs}"
55+
class="ui bottom attached inverted segment tab log {active: tab.fullTabId === activeLogTabId}"
56+
data-tab="{tab.fullTabId}">
57+
<pre class="{empty: isEmpty(tab.content)}">{ showLog(tab.content) }</pre>
6958
</div>
7059
</div>
60+
<!-- Fact sheet -->
7161
<div class="ui tab modal-tab" data-tab="{admin_: submission.admin}fact_sheet">
7262
<div class="ui inverted segment log">
7363
<textarea name="fact-sheet" id="fact_sheet" ref="fact_sheet_text_area">{ JSON.stringify(fact_sheet_answers, null, 2) }</textarea>
7464
</div>
7565
<div class="ui button green" onclick="{update_fact_sheet.bind(this)}">Save</div>
7666
</div>
67+
<!-- Visualization -->
7768
<div class="ui tab modal-tab" data-tab="{admin_: submission.admin}graph" show="{opts.show_visualization && (!opts.hide_output || submission.admin)}">
7869
<iframe src="{detailed_result}" class="graph-frame" show="{detailed_result}"></iframe>
7970
</div>
71+
<!-- Admin -->
8072
<div class="ui tab leaderboard-tab" data-tab="admin" if="{submission.admin}">
8173
<submission-scores leaderboards="{leaderboards}"></submission-scores>
8274
</div>
75+
8376
<script>
8477
var self = this
8578
self.submission = {}
8679
self.logs = {}
8780
self.leaderboards = []
8881
self.columns = []
8982

90-
// Check if logs are empty
91-
self.isEmpty = (v) => v == null || (typeof v === "string" && v.trim().length === 0)
83+
// OLD helpers - Check if logs are empty
84+
//self.isEmpty = (v) => v == null || (typeof v === "string" && v.trim().length === 0)
85+
//self.nonEmpty = (v) => !self.isEmpty(v)
86+
//self.showLog = (v) => self.nonEmpty(v) ? v : "No logs for this tab."
87+
88+
// Logs helpers
89+
self.normalizeLog = (v) => {
90+
if (v == null) return v
91+
if (Array.isArray(v)) return v.join('\n')
92+
if (typeof v === "object") {
93+
try { return JSON.stringify(v, null, 2) } catch { return String(v) }
94+
}
95+
return String(v)
96+
}
97+
self.isEmpty = (v) => {
98+
v = self.normalizeLog(v)
99+
return v == null || (typeof v === "string" && v.trim().length === 0)
100+
}
92101
self.nonEmpty = (v) => !self.isEmpty(v)
93-
self.showLog = (v) => self.nonEmpty(v) ? v : "No logs for this tab."
102+
self.showLog = (v) => self.nonEmpty(v) ? self.normalizeLog(v) : "No logs for this tab."
103+
self.getLog = (...keys) => {
104+
for (const k of keys) {
105+
const v = self.normalizeLog(self.logs[k])
106+
if (!self.isEmpty(v)) return v
107+
}
108+
return null
109+
}
110+
111+
// Dynamic tabs state
112+
self.logTabs = []
113+
self.activeLogTabId = null
114+
115+
self.rebuildLogTabs = () => {
116+
const prefix = self.submission && self.submission.admin ? 'admin_' : ''
117+
const candidates = [
118+
{
119+
key: 'log_ing_out',
120+
label: 'Ingestion output',
121+
content: self.getLog('prediction_ingestion_stdout', 'prediction_stdout')
122+
},
123+
{
124+
key: 'log_ing_err',
125+
label: 'Ingestion errors',
126+
content: self.getLog('prediction_ingestion_stderr', 'prediction_stderr')
127+
},
128+
{
129+
key: 'log_score_out',
130+
label: 'Scoring output',
131+
content: self.getLog('scoring_stdout', 'scoring_ingestion_stdout')
132+
},
133+
{
134+
key: 'log_score_err',
135+
label: 'Scoring errors',
136+
content: self.getLog('scoring_stderr', 'scoring_ingestion_stderr')
137+
},
138+
]
139+
140+
// Keep only non empty tabs
141+
self.logTabs = candidates
142+
.filter(t => !self.isEmpty(t.content))
143+
.map(t => ({
144+
...t,
145+
fullTabId: `${prefix}${t.key}`
146+
}))
147+
// Select a valid tab as active
148+
const stillValid = self.logTabs.find(t => t.fullTabId === self.activeLogTabId)
149+
if (!stillValid) {
150+
self.activeLogTabId = self.logTabs.length ? self.logTabs[0].fullTabId : null
151+
}
152+
}
94153

95154
self.get_score_details = function (column) {
96155
try {
@@ -116,9 +175,20 @@
116175
$.get(item.data_file)
117176
.done(function (content) {
118177
self.logs[item.name] = content
178+
self.rebuildLogTabs()
119179
self.update()
180+
// Rebind Semantic UI tabs after DOM update
181+
setTimeout(() => {
182+
// init tabs inside the LOGS menu
183+
$('.ui.top.attached.menu .item').tab()
184+
if (self.activeLogTabId) {
185+
$('.ui.top.attached.menu .item').tab('change tab', self.activeLogTabId)
186+
}
187+
}, 0)
120188
})
121189
})
190+
self.rebuildLogTabs()
191+
self.update()
122192
if (self.submission.admin) {
123193
_.forEach(data.leaderboards, (leaderboard) => {
124194
_.map(leaderboard.columns, (column) => {

0 commit comments

Comments
 (0)