From 817857ffd8233b54c065a37cf5c3d2b49bf56668 Mon Sep 17 00:00:00 2001 From: sto Date: Thu, 6 Aug 2026 11:26:51 +0200 Subject: [PATCH] Events: remaining fixes --- app/controllers/contests_controller.rb | 6 ++ app/controllers/events_controller.rb | 6 +- app/views/application/_event_nav.html.slim | 2 +- .../contests/scoreboard_not_started.html.slim | 2 + app/views/layouts/authenticated.html.slim | 85 ++++++++++--------- config/locales/en.yml | 1 + config/locales/fr.yml | 3 +- 7 files changed, 58 insertions(+), 47 deletions(-) create mode 100644 app/views/contests/scoreboard_not_started.html.slim diff --git a/app/controllers/contests_controller.rb b/app/controllers/contests_controller.rb index d362040..bc98fa6 100644 --- a/app/controllers/contests_controller.rb +++ b/app/controllers/contests_controller.rb @@ -169,6 +169,7 @@ class ContestsController < ApplicationController end def scoreboard + @is_scoreboard = true if active_page("/public/event/") == "active" @event = Event.find_by(slug: params[:id]) unless @event && @event.shared_scoreboard && !@event.contests.where(public: true).empty? @@ -187,6 +188,10 @@ class ContestsController < ApplicationController else @contest = @event.contests.where(public: true).first end + if @contest.completions.empty? && @contest.start_time == nil + authorize @contest + render :scoreboard_not_started and return + end else @contest = Contest.find_by(slug: params[:id]) unless @contest && @contest.public @@ -195,6 +200,7 @@ class ContestsController < ApplicationController end end authorize @contest + @is_scoreboard_started = true I18n.locale = @contest.lang diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 530be03..b458f95 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -35,14 +35,14 @@ class EventsController < ApplicationController def show authorize @event - @contests = @event.contests.includes([ :contestants, :puzzles ]) + @contests = @event.contests.includes([ :contestants, puzzles: [ :image_attachment ] ]) @title = @event.name end def edit authorize @event - @title = I18n.t("events.edit.title") + @title = @event.name @nonav = true end @@ -52,7 +52,7 @@ class EventsController < ApplicationController if @event.update(event_params) redirect_to event_path(@event), notice: t("events.edit.notice") else - @title = I18n.t("events.edit.title") + @title = @event.name @nonav = true render :edit, status: :unprocessable_entity end diff --git a/app/views/application/_event_nav.html.slim b/app/views/application/_event_nav.html.slim index c49ad8e..d66e07f 100644 --- a/app/views/application/_event_nav.html.slim +++ b/app/views/application/_event_nav.html.slim @@ -1,4 +1,4 @@ -.mt-2.row style="font-size: 16px;" +.mt-3.row style="font-size: 16px;" .col ul.nav.nav-tabs.mb-4 - @contest.event.contests.where(public: true).each do |contest| diff --git a/app/views/contests/scoreboard_not_started.html.slim b/app/views/contests/scoreboard_not_started.html.slim new file mode 100644 index 0000000..a54900c --- /dev/null +++ b/app/views/contests/scoreboard_not_started.html.slim @@ -0,0 +1,2 @@ +.mt-3 + = t("contests.scoreboard.not_started_message") \ No newline at end of file diff --git a/app/views/layouts/authenticated.html.slim b/app/views/layouts/authenticated.html.slim index ab173ff..7990889 100644 --- a/app/views/layouts/authenticated.html.slim +++ b/app/views/layouts/authenticated.html.slim @@ -45,54 +45,55 @@ html h1.mb-4 - if @contest && @contest.id.present? - - if active_page("/public") == "active" && @action_path + - if active_page("/public") == "active" && @is_scoreboard - if active_page("/public/event/") == "active" = @contest.event.name = render "event_nav" - else = @contest.name - .float-end style="margin-top: #{active_page("/public/event/") == "active" ? "-18px" : "-5px"};" id="scoreboard-switches" - .d-inline-flex.align-items-center - .ms-4.form-check.form-switch style="font-size: 16px; font-weight: 300;" - input.form-check-input type="checkbox" id="refresh-checkbox" - label.ms-1 style="font-size: 16px; font-weight: 300;" - = t("contests.scoreboard.auto_refresh") - .js data-turbo="false" - javascript: - function refresh() { - if (document.getElementById('refresh-checkbox').checked) { - addParam('autorefresh', 1); - setTimeout(refresh, 30000); + - if @is_scoreboard_started + .float-end style="margin-top: #{active_page("/public/event/") == "active" ? "-18px" : "-5px"};" id="scoreboard-switches" + .d-inline-flex.align-items-center + .ms-4.form-check.form-switch style="font-size: 16px; font-weight: 300;" + input.form-check-input type="checkbox" id="refresh-checkbox" + label.ms-1 style="font-size: 16px; font-weight: 300;" + = t("contests.scoreboard.auto_refresh") + .js data-turbo="false" + javascript: + function refresh() { + if (document.getElementById('refresh-checkbox').checked) { + addParam('autorefresh', 1); + setTimeout(refresh, 30000); + } } - } - function addParam(key, value) { - const urlParams = new URLSearchParams(window.location.search); - urlParams.delete(key); - urlParams.append(key, value); - const refreshBtn = document.getElementById('refresh-button') - refreshBtn.href = `/public/#{@contest.friendly_id}?${urlParams.toString()}`; - refreshBtn.click(); - } - function removeParam(key) { - const urlParams = new URLSearchParams(window.location.search); - urlParams.delete(key); - const refreshBtn = document.getElementById('refresh-button') - refreshBtn.href = `/public/#{@contest.friendly_id}?${urlParams.toString()}`; - refreshBtn.click(); - } - function autoRefresh() { - if (document.getElementById('refresh-checkbox').checked) setTimeout(refresh, 30000); - document.getElementById('refresh-checkbox').addEventListener('change', (e) => { - if (e.target.checked) refresh(); - else removeParam('autorefresh'); - }); - } - async function startAutoRefresh(count) { - if (count == 0) return; - if (document.getElementById('refresh-button') && document.getElementById('refresh-checkbox')) autoRefresh(); - else setTimeout(() => startAutoRefresh(count - 1), 10); - } - startAutoRefresh(200); + function addParam(key, value) { + const urlParams = new URLSearchParams(window.location.search); + urlParams.delete(key); + urlParams.append(key, value); + const refreshBtn = document.getElementById('refresh-button') + refreshBtn.href = `/public/#{@contest.friendly_id}?${urlParams.toString()}`; + refreshBtn.click(); + } + function removeParam(key) { + const urlParams = new URLSearchParams(window.location.search); + urlParams.delete(key); + const refreshBtn = document.getElementById('refresh-button') + refreshBtn.href = `/public/#{@contest.friendly_id}?${urlParams.toString()}`; + refreshBtn.click(); + } + function autoRefresh() { + if (document.getElementById('refresh-checkbox').checked) setTimeout(refresh, 30000); + document.getElementById('refresh-checkbox').addEventListener('change', (e) => { + if (e.target.checked) refresh(); + else removeParam('autorefresh'); + }); + } + async function startAutoRefresh(count) { + if (count == 0) return; + if (document.getElementById('refresh-button') && document.getElementById('refresh-checkbox')) autoRefresh(); + else setTimeout(() => startAutoRefresh(count - 1), 10); + } + startAutoRefresh(200); - elsif active_page("/contests") == "active" = @contest.name - if @contest.event && @contest.event.shared_scoreboard diff --git a/config/locales/en.yml b/config/locales/en.yml index 60fb398..aa9bd3e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -236,6 +236,7 @@ en: all_categories: All categories auto_refresh: Auto refresh (30s) hide_offline: Hide offline participants + not_started_message: This contest isn't started yet. refresh: Activate auto-refresh (every 5s) title: "%{name}" show: diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 42f6fe4..a576cd7 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -207,6 +207,7 @@ fr: all_categories: Toutes les catégories auto_refresh: Auto-rafraichissement (30s) hide_offline: Cacher les participant.e.s hors-ligne + not_started_message: Cette épreuve n'a pas encore commencé. refresh: Activer le rafraichissement automatique de la page (toutes les 5s) title: "%{name}" show: @@ -270,7 +271,7 @@ fr: index: contest: épreuve contests: épreuves - message: Nouvelle fonctionalité ! Les événements permettent de regrouper plusieurs concours/épreuves en un seul espace de gestion. Cela permet aussi, si souhaité, d'avoir un lien de classement commun à toutes les épreuces d'un même événement. + message: Nouvelle fonctionalité ! Les événements permettent de regrouper plusieurs concours/épreuves en un seul espace de gestion. Cela permet aussi, si souhaité, d'avoir un lien de classement commun à toutes les épreuves d'un même événement. my_contests_outside_events: Mes concours non associés à un événement new: Créer un nouvel événement other_contest: concours