|
187 | 187 | </tbody> |
188 | 188 | </table> |
189 | 189 |
|
| 190 | + <div class="ui pagination menu" style="display:flex; align-items:center; justify-content:space-between; margin-top: 12px;"> |
| 191 | + <div style="display:flex; align-items:center; gap:8px;"> |
| 192 | + <button class="ui button" onclick="{ go_to_page.bind(this, page - 1) }" disabled="{ page <= 1 }"> |
| 193 | + <i class="icon chevron left"></i> Previous |
| 194 | + </button> |
| 195 | + |
| 196 | + <div style="display:flex; align-items:center; gap:6px;"> |
| 197 | + <span>Page</span> |
| 198 | + <input type="number" min="1" value="{ page }" onkeydown="{ handle_page_enter }" style="width:70px; text-align:center;" /> |
| 199 | + <span> / { total_pages || 1 }</span> |
| 200 | + </div> |
| 201 | + |
| 202 | + <button class="ui button" onclick="{ go_to_page.bind(this, page + 1) }" disabled="{ !next }"> |
| 203 | + Next <i class="icon chevron right"></i> |
| 204 | + </button> |
| 205 | + </div> |
| 206 | + |
| 207 | + <div style="display:flex; align-items:center; gap:8px;"> |
| 208 | + <label>Per page</label> |
| 209 | + <select class="ui dropdown" value="{ page_size }" onchange="{ change_page_size.bind(this) }"> |
| 210 | + <option value="50">50</option> |
| 211 | + <option value="100">100</option> |
| 212 | + <option value="500">500</option> |
| 213 | + <option value="all">1000</option> |
| 214 | + </select> |
| 215 | + |
| 216 | + <div style="margin-right: 10px; color: #8c8c8c;"> |
| 217 | + <small>{ total_count || 0 } total</small> |
| 218 | + </div> |
| 219 | + </div> |
| 220 | + |
190 | 221 | <div class="ui large modal" ref="modal"> |
191 | 222 | <div class="content"> |
192 | 223 | <div if="{!!selected_submission && !_.get(selected_submission, 'has_children', false)}"> |
|
203 | 234 |
|
204 | 235 | <div if="{is_admin()}" data-tab="admin" class="parent-modal item">Admin</div> |
205 | 236 |
|
206 | | - <!-- Sometimes submissions end up in a bad state with no children.. --> |
207 | 237 | <div class="item" if="{_.get(selected_submission, 'children').length === 0}"> |
208 | 238 | <i style="padding: 5px;">ERROR: Submission is a parent, but has no children. There was an error |
209 | 239 | during creation.</i> |
|
216 | 246 | show_visualization="{opts.competition.enable_detailed_results}" |
217 | 247 | submission="{child}"></submission-modal> |
218 | 248 | </div> |
| 249 | + |
| 250 | + |
219 | 251 | <div class="ui tab" style="height: 565px; overflow: auto;" data-tab="admin" if="{is_admin()}"> |
220 | 252 | <submission-scores leaderboards="{leaderboards}"></submission-scores> |
221 | 253 | </div> |
|
234 | 266 | self.checked_submissions = [] |
235 | 267 | self.show_is_soft_deleted = false |
236 | 268 |
|
| 269 | + self.page = 1 |
| 270 | + self.page_size = 50 |
| 271 | + self.total_count = 0 |
| 272 | + self.total_pages = 1 |
| 273 | + self.next = null |
| 274 | + self.previous = null |
| 275 | + |
237 | 276 | self.on("mount", function () { |
238 | 277 | $(self.refs.search).dropdown() |
239 | 278 | $(self.refs.status).dropdown() |
|
269 | 308 | self.update() |
270 | 309 | } |
271 | 310 |
|
| 311 | + |
272 | 312 | self.update_submissions = function (filters) { |
273 | 313 | self.loading = true |
274 | 314 | self.update() |
275 | | - if (opts.admin) { |
276 | | - filters = filters || { phase__competition: opts.competition.id } |
277 | | - filters.show_is_soft_deleted = self.show_is_soft_deleted |
| 315 | + |
| 316 | + if (!filters) { |
| 317 | + if (opts.admin) { |
| 318 | + filters = { phase__competition: opts.competition.id } |
| 319 | + filters.show_is_soft_deleted = self.show_is_soft_deleted |
| 320 | + } else { |
| 321 | + filters = { phase: self.selected_phase ? self.selected_phase.id : undefined } |
| 322 | + } |
| 323 | + } |
| 324 | + |
| 325 | + filters.page = self.page |
| 326 | + if (String(self.page_size).toLowerCase() === 'all') { |
| 327 | + filters.page_size = 'all' |
278 | 328 | } else { |
279 | | - filters = filters || { phase: self.selected_phase.id } |
| 329 | + filters.page_size = self.page_size |
280 | 330 | } |
281 | | - filters = filters || { phase: self.selected_phase.id } |
| 331 | + |
282 | 332 | CODALAB.api.get_submissions(filters) |
283 | | - .done(function (submissions) { |
284 | | - // TODO: should be able to do this with a serializer? |
| 333 | + .done(function (response) { |
| 334 | + let data = response |
| 335 | + let results = response |
| 336 | + if (response && typeof response === 'object' && response.hasOwnProperty('results')) { |
| 337 | + results = response.results || [] |
| 338 | + self.next = response.next || null |
| 339 | + self.previous = response.previous || null |
| 340 | + self.total_count = response.count || 0 |
| 341 | + |
| 342 | + var effectivePageSize = 1 |
| 343 | + var serverPageSize = undefined |
| 344 | + var totalCount = (typeof response.count === 'number') ? response.count : self.total_count |
| 345 | + var isLastPage = (response.next === null) |
| 346 | + |
| 347 | + if (response && typeof response.page_size !== 'undefined') { |
| 348 | + serverPageSize = response.page_size |
| 349 | + } |
| 350 | + |
| 351 | + if (typeof serverPageSize !== 'undefined') { |
| 352 | + if (String(serverPageSize).toLowerCase() === 'all') { |
| 353 | + self.page_size = 'all' |
| 354 | + } else { |
| 355 | + var parsedServerPS = Number(serverPageSize) |
| 356 | + if (!isNaN(parsedServerPS) && parsedServerPS > 0) { |
| 357 | + self.page_size = parsedServerPS |
| 358 | + } |
| 359 | + } |
| 360 | + } |
| 361 | + |
| 362 | + if (String(self.page_size).toLowerCase() === 'all') { |
| 363 | + if (typeof response.page_size_numeric === 'number' && response.page_size_numeric > 0) { |
| 364 | + effectivePageSize = response.page_size_numeric |
| 365 | + } else if (typeof response.effective_page_size === 'number' && response.effective_page_size > 0) { |
| 366 | + effectivePageSize = response.effective_page_size |
| 367 | + } else { |
| 368 | + if (!isLastPage && Array.isArray(results) && results.length > 0) { |
| 369 | + effectivePageSize = results.length |
| 370 | + } else if (isLastPage && self.page > 1 && typeof totalCount === 'number' && totalCount > 0) { |
| 371 | + var itemsOnLastPage = Array.isArray(results) ? results.length : 0 |
| 372 | + var pagesBefore = self.page - 1 |
| 373 | + var calc = Math.floor((totalCount - itemsOnLastPage) / pagesBefore) |
| 374 | + if (calc > 0) { |
| 375 | + effectivePageSize = calc |
| 376 | + } else { |
| 377 | + effectivePageSize = 50 |
| 378 | + } |
| 379 | + } else if (Array.isArray(results) && results.length > 0) { |
| 380 | + effectivePageSize = results.length |
| 381 | + } else { |
| 382 | + effectivePageSize = 50 |
| 383 | + } |
| 384 | + } |
| 385 | + } else if (typeof self.page_size === 'number' && self.page_size > 0) { |
| 386 | + effectivePageSize = self.page_size |
| 387 | + } else { |
| 388 | + effectivePageSize = 50 |
| 389 | + } |
| 390 | + |
| 391 | + if (typeof totalCount !== 'number' || totalCount < 0) { |
| 392 | + self.total_pages = 1 |
| 393 | + } else { |
| 394 | + self.total_pages = Math.max(1, Math.ceil(totalCount / effectivePageSize)) |
| 395 | + } |
| 396 | + } else { |
| 397 | + results = response || [] |
| 398 | + self.next = null |
| 399 | + self.previous = null |
| 400 | + self.total_count = results.length |
| 401 | + self.total_pages = Math.max(1, Math.ceil(self.total_count / self.page_size)) |
| 402 | + } |
| 403 | + |
285 | 404 | if (opts.admin) { |
286 | | - self.submissions = submissions.map((item) => { |
| 405 | + self.submissions = results.map((item) => { |
287 | 406 | item.phase = opts.competition.phases.filter((phase) => { |
288 | 407 | return phase.id === item.phase |
289 | 408 | })[0] |
290 | 409 | return item |
291 | 410 | }) |
292 | 411 | } else { |
293 | | - self.submissions = _.filter(submissions, sub => sub.owner === CODALAB.state.user.username) |
| 412 | + self.submissions = _.filter(results, sub => sub.owner === CODALAB.state.user.username) |
294 | 413 | } |
| 414 | + |
295 | 415 | if (!opts.admin) { |
296 | 416 | CODALAB.events.trigger('submissions_loaded', self.submissions) |
297 | 417 | } |
| 418 | + |
298 | 419 | self.csv_link = CODALAB.api.get_submission_csv_URL(filters) |
| 420 | + |
299 | 421 | self.update() |
300 | 422 | self.submission_checked() |
301 | 423 |
|
302 | | - // Timeout here so loader doesn't flicker |
303 | 424 | _.delay(() => { |
304 | 425 | self.loading = false |
305 | 426 | self.update() |
|
310 | 431 | }) |
311 | 432 | } |
312 | 433 |
|
| 434 | + |
| 435 | + self.go_to_page = function (p) { |
| 436 | + let newPage = parseInt(p, 10) |
| 437 | + if (isNaN(newPage) || newPage < 1) newPage = 1 |
| 438 | + if (self.total_pages && newPage > self.total_pages) newPage = self.total_pages |
| 439 | + if (newPage === self.page) return |
| 440 | + self.page = newPage |
| 441 | + self.update_submissions() |
| 442 | + } |
| 443 | + |
| 444 | + self.change_page_size = function (e) { |
| 445 | + const raw = (e && e.target && typeof e.target.value !== 'undefined') ? String(e.target.value).toLowerCase() : String(self.page_size).toLowerCase() |
| 446 | + |
| 447 | + if (raw === 'all') { |
| 448 | + self.page_size = 'all' |
| 449 | + } else { |
| 450 | + const val = parseInt(raw, 10) |
| 451 | + if (isNaN(val) || val <= 0) return |
| 452 | + // n'autorise que 50,100,500 |
| 453 | + if (![50, 100, 500].includes(val)) return |
| 454 | + self.page_size = val |
| 455 | + } |
| 456 | + |
| 457 | + self.page = 1 // reset to first page when page size changes |
| 458 | + self.update_submissions() |
| 459 | + } |
| 460 | + |
| 461 | + self.handle_page_enter = function (ev) { |
| 462 | + if (ev.key === 'Enter') { |
| 463 | + // value du champ input |
| 464 | + let v = ev.target.value |
| 465 | + let requested = parseInt(v, 10) |
| 466 | + if (isNaN(requested)) return |
| 467 | + self.go_to_page(requested) |
| 468 | + } |
| 469 | + } |
| 470 | + |
313 | 471 | self.add_to_leaderboard = function (submission) { |
314 | 472 | CODALAB.api.add_submission_to_leaderboard(submission.id) |
315 | 473 | .done(function (data) { |
|
365 | 523 | filters['phase__competition'] = opts.competition.id |
366 | 524 | } |
367 | 525 | } |
| 526 | + self.page = 1 |
368 | 527 | self.update_submissions(filters) |
369 | 528 | }, 100) |
370 | 529 | } |
|
524 | 683 | // Set checkboxes to be equal to Select_All checkbox |
525 | 684 | check_boxes.prop('checked', check_boxes.first().is(':checked')) |
526 | 685 |
|
527 | | - |
528 | 686 | let inputs = $(self.refs.submission_table).find('input') |
529 | 687 | let checked_boxes = inputs.not(':first').filter('input:checked') |
530 | 688 | self.checked_submissions = checked_boxes.serializeArray().map((x) => { return x.name }) |
531 | 689 | } |
532 | 690 |
|
533 | | - |
534 | | - |
535 | | - |
536 | | - |
537 | 691 | self.submission_clicked = function (submission) { |
538 | 692 | // stupid workaround to not modify the original submission object |
539 | 693 | submission = _.defaultsDeep({}, submission) |
|
0 commit comments