Compare commits
2 Commits
97ea17b7c2
...
f91145637f
| Author | SHA1 | Date | |
|---|---|---|---|
| f91145637f | |||
| cdf87e48f2 |
@@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base
|
|||||||
include Authentication
|
include Authentication
|
||||||
include Pundit::Authorization
|
include Pundit::Authorization
|
||||||
|
|
||||||
before_action :set_title, :set_current_user, :set_lang
|
before_action :set_current_user, :set_lang
|
||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
|
|
||||||
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
||||||
@@ -12,13 +12,6 @@ class ApplicationController < ActionController::Base
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_title
|
|
||||||
t_action_name = action_name
|
|
||||||
t_action_name = "new" if action_name == "create"
|
|
||||||
t_action_name = "edit" if action_name == "update"
|
|
||||||
@title = I18n.t("#{controller_name}.#{t_action_name}.title")
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_current_user
|
def set_current_user
|
||||||
@current_user = current_user
|
@current_user = current_user
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,21 +8,11 @@ class CompletionsController < ApplicationController
|
|||||||
|
|
||||||
def edit
|
def edit
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
if @contestant
|
|
||||||
@action_name = t("helpers.buttons.back_to_contestant")
|
|
||||||
@action_path = edit_contest_contestant_path(@contest, @contestant)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
if @contestant
|
|
||||||
@action_name = t("helpers.buttons.back_to_contestant")
|
|
||||||
@action_path = edit_contest_contestant_path(@contest, @contestant)
|
|
||||||
end
|
|
||||||
|
|
||||||
@completion = Completion.new
|
@completion = Completion.new
|
||||||
@completion.completed = true
|
@completion.completed = true
|
||||||
if params[:contestant_id]
|
if params[:contestant_id]
|
||||||
@@ -47,11 +37,6 @@ class CompletionsController < ApplicationController
|
|||||||
else
|
else
|
||||||
if params[:completion].key?(:message_id)
|
if params[:completion].key?(:message_id)
|
||||||
@message = Message.find(params[:completion][:message_id])
|
@message = Message.find(params[:completion][:message_id])
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
elsif @contestant
|
|
||||||
@action_name = t("helpers.buttons.back_to_contestant")
|
|
||||||
@action_path = edit_contest_contestant_path(@contest, @contestant)
|
|
||||||
end
|
end
|
||||||
render :new, status: :unprocessable_entity
|
render :new, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
@@ -69,10 +54,6 @@ class CompletionsController < ApplicationController
|
|||||||
redirect_to @contest, notice: t("completions.edit.notice")
|
redirect_to @contest, notice: t("completions.edit.notice")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if @contestant
|
|
||||||
@action_name = t("helpers.buttons.back_to_contestant")
|
|
||||||
@action_path = edit_contest_contestant_path(@contest, @contestant)
|
|
||||||
end
|
|
||||||
render :edit, status: :unprocessable_entity
|
render :edit, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
12
app/controllers/concerns/contestants_concern.rb
Normal file
12
app/controllers/concerns/contestants_concern.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module ContestantsConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def ranked_contestants(contest)
|
||||||
|
contest.contestants.sort_by { |contestant| [
|
||||||
|
-contestant.completions.where(remaining_pieces: nil).size,
|
||||||
|
(contestant.completions.where(remaining_pieces: nil).size == @contest.puzzles.length ? 1 : 0) * contestant.time_seconds,
|
||||||
|
contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? contestant.completions[-1].remaining_pieces : 1000000,
|
||||||
|
contestant.time_seconds
|
||||||
|
] }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
class ContestantsController < ApplicationController
|
class ContestantsController < ApplicationController
|
||||||
|
include ContestantsConcern
|
||||||
|
|
||||||
before_action :set_contest
|
before_action :set_contest
|
||||||
before_action :set_contestant, only: %i[ destroy edit update]
|
before_action :set_contestant, only: %i[ destroy edit update]
|
||||||
before_action :set_completions, only: %i[edit update ]
|
before_action :set_completions, only: %i[edit update ]
|
||||||
@@ -6,29 +8,17 @@ class ContestantsController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@title = @contest.name
|
@contestants = @contest.contestants.sort_by { |contestant| contestant.name }
|
||||||
@contestants = @contest.contestants.sort_by { |contestant| [
|
|
||||||
-contestant.completions.where(remaining_pieces: nil).size,
|
|
||||||
(contestant.completions.where(remaining_pieces: nil).size == @contest.puzzles.length ? 1 : 0) * contestant.time_seconds,
|
|
||||||
contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? contestant.completions[-1].remaining_pieces : 1000000,
|
|
||||||
contestant.time_seconds
|
|
||||||
] }
|
|
||||||
filter_contestants_per_category
|
filter_contestants_per_category
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@title = @contestant.name
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@contestant = Contestant.new
|
@contestant = Contestant.new
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -41,8 +31,6 @@ class ContestantsController < ApplicationController
|
|||||||
update_contestant_categories
|
update_contestant_categories
|
||||||
redirect_to contest_path(@contest), notice: t("contestants.new.notice")
|
redirect_to contest_path(@contest), notice: t("contestants.new.notice")
|
||||||
else
|
else
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
render :new, status: :unprocessable_entity
|
render :new, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -54,8 +42,6 @@ class ContestantsController < ApplicationController
|
|||||||
update_contestant_categories
|
update_contestant_categories
|
||||||
redirect_to @contest, notice: t("contestants.edit.notice")
|
redirect_to @contest, notice: t("contestants.edit.notice")
|
||||||
else
|
else
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
render :edit, status: :unprocessable_entity
|
render :edit, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -83,8 +69,6 @@ class ContestantsController < ApplicationController
|
|||||||
if @csv_import.save
|
if @csv_import.save
|
||||||
redirect_to "/contests/#{@contest.id}/import/#{@csv_import.id}"
|
redirect_to "/contests/#{@contest.id}/import/#{@csv_import.id}"
|
||||||
else
|
else
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
render :import, status: :unprocessable_entity
|
render :import, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -92,8 +76,6 @@ class ContestantsController < ApplicationController
|
|||||||
def convert_csv
|
def convert_csv
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@csv_import = CsvImport.find(params[:id])
|
@csv_import = CsvImport.find(params[:id])
|
||||||
@content = JSON.parse(@csv_import.content)
|
@content = JSON.parse(@csv_import.content)
|
||||||
@form = Forms::CsvConversionForm.new
|
@form = Forms::CsvConversionForm.new
|
||||||
@@ -119,8 +101,6 @@ class ContestantsController < ApplicationController
|
|||||||
end
|
end
|
||||||
redirect_to contest_path(@contest), notice: t("contestants.import.notice")
|
redirect_to contest_path(@contest), notice: t("contestants.import.notice")
|
||||||
else
|
else
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
render :convert_csv, status: :unprocessable_entity
|
render :convert_csv, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -128,12 +108,7 @@ class ContestantsController < ApplicationController
|
|||||||
def export
|
def export
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@contestants = @contest.contestants.sort_by { |contestant| [
|
@contestants = ranked_contestants(@contest)
|
||||||
-contestant.completions.where(remaining_pieces: nil).size,
|
|
||||||
(contestant.completions.where(remaining_pieces: nil).size == @contest.puzzles.length ? 1 : 0) * contestant.time_seconds,
|
|
||||||
contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? contestant.completions[-1].remaining_pieces : 1000000,
|
|
||||||
contestant.time_seconds
|
|
||||||
] }
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.csv do
|
format.csv do
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
class ContestsController < ApplicationController
|
class ContestsController < ApplicationController
|
||||||
include CompletionsConcern
|
include CompletionsConcern
|
||||||
|
include ContestantsConcern
|
||||||
|
|
||||||
before_action :set_contest, only: %i[ destroy edit show update ]
|
before_action :set_contest, only: %i[ destroy show ]
|
||||||
before_action :set_settings_contest, only: %i[ settings_general_edit settings_general_update settings_offline_edit settings_offline_update settings_categories_edit ]
|
before_action :set_settings_contest, only: %i[ settings_general_edit settings_general_update settings_offline_edit settings_offline_update settings_categories_edit ]
|
||||||
before_action :offline_setup, only: %i[ offline_new offline_create offline_edit offline_update offline_completed ]
|
before_action :offline_setup, only: %i[ offline_new offline_create offline_edit offline_update offline_completed ]
|
||||||
skip_before_action :require_authentication, only: %i[ scoreboard offline_new offline_create offline_edit offline_update offline_completed ]
|
skip_before_action :require_authentication, only: %i[ scoreboard offline_new offline_create offline_edit offline_update offline_completed ]
|
||||||
@@ -19,35 +20,16 @@ class ContestsController < ApplicationController
|
|||||||
redirect_to contest_contestants_path(@contest)
|
redirect_to contest_contestants_path(@contest)
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize @contest
|
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
end
|
|
||||||
|
|
||||||
def settings_general_edit
|
def settings_general_edit
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@title = t("contests.edit.title")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def settings_offline_edit
|
def settings_offline_edit
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@title = t("contests.edit.title")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def settings_categories_edit
|
def settings_categories_edit
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@title = t("contests.edit.title")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def settings_general_update
|
def settings_general_update
|
||||||
@@ -56,9 +38,6 @@ class ContestsController < ApplicationController
|
|||||||
if @contest.update(settings_general_params)
|
if @contest.update(settings_general_params)
|
||||||
redirect_to "/contests/#{@contest.id}/settings/general", notice: t("contests.edit.notice")
|
redirect_to "/contests/#{@contest.id}/settings/general", notice: t("contests.edit.notice")
|
||||||
else
|
else
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@title = t("contests.edit.title")
|
|
||||||
render :settings_general_edit, status: :unprocessable_entity
|
render :settings_general_edit, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -69,9 +48,6 @@ class ContestsController < ApplicationController
|
|||||||
if @contest.update(settings_offline_params)
|
if @contest.update(settings_offline_params)
|
||||||
redirect_to "/contests/#{@contest.id}/settings/offline", notice: t("contests.edit.notice")
|
redirect_to "/contests/#{@contest.id}/settings/offline", notice: t("contests.edit.notice")
|
||||||
else
|
else
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@title = t("contests.edit.title")
|
|
||||||
render :settings_offline_edit, status: :unprocessable_entity
|
render :settings_offline_edit, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -85,7 +61,7 @@ class ContestsController < ApplicationController
|
|||||||
def create
|
def create
|
||||||
authorize :contest
|
authorize :contest
|
||||||
|
|
||||||
@contest = Contest.new(contest_params)
|
@contest = Contest.new(new_contest_params)
|
||||||
@contest.user_id = current_user.id
|
@contest.user_id = current_user.id
|
||||||
if @contest.save
|
if @contest.save
|
||||||
redirect_to "/contests/#{@contest.id}/settings/general", notice: t("contests.new.notice")
|
redirect_to "/contests/#{@contest.id}/settings/general", notice: t("contests.new.notice")
|
||||||
@@ -94,18 +70,6 @@ class ContestsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
|
||||||
authorize @contest
|
|
||||||
|
|
||||||
if @contest.update(contest_params)
|
|
||||||
redirect_to @contest, notice: t("contests.edit.notice")
|
|
||||||
else
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
render :edit, status: :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@@ -124,20 +88,18 @@ class ContestsController < ApplicationController
|
|||||||
I18n.locale = @contest.lang
|
I18n.locale = @contest.lang
|
||||||
|
|
||||||
@title = I18n.t("contests.scoreboard.title", name: @contest.name)
|
@title = I18n.t("contests.scoreboard.title", name: @contest.name)
|
||||||
@contestants = @contest.contestants.sort_by { |contestant| [
|
@contestants = ranked_contestants(@contest)
|
||||||
-contestant.completions.where(remaining_pieces: nil).size,
|
|
||||||
(contestant.completions.where(remaining_pieces: nil).size == @contest.puzzles.length ? 1 : 0) * contestant.time_seconds,
|
|
||||||
contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? contestant.completions[-1].remaining_pieces : 1000000,
|
|
||||||
contestant.time_seconds
|
|
||||||
] }
|
|
||||||
filter_contestants_per_category
|
filter_contestants_per_category
|
||||||
if params.key?(:hide_offline) && params[:hide_offline] == "true"
|
if params.key?(:hide_offline) && params[:hide_offline] == "true"
|
||||||
@contestants = @contestants.select { |contestant| !contestant.offline.present? }
|
@contestants = @contestants.select { |contestant| !contestant.offline.present? }
|
||||||
end
|
end
|
||||||
@puzzles = @contest.puzzles.order(:id)
|
@puzzles = @contest.puzzles.order(:id)
|
||||||
@action_name = t("helpers.buttons.refresh")
|
if params.key?(:hide_offline) && params.key?(:category)
|
||||||
if params.key?(:category)
|
@action_path = "/public/#{@contest.friendly_id}?hide_offline=#{params[:hide_offline]}&category=#{params[:category]}"
|
||||||
|
elsif params.key?(:category)
|
||||||
@action_path = "/public/#{@contest.friendly_id}?category=#{params[:category]}"
|
@action_path = "/public/#{@contest.friendly_id}?category=#{params[:category]}"
|
||||||
|
elsif params.key?(:hide_offline)
|
||||||
|
@action_path = "/public/#{@contest.friendly_id}?hide_offline=#{params[:hide_offline]}"
|
||||||
else
|
else
|
||||||
@action_path = "/public/#{@contest.friendly_id}"
|
@action_path = "/public/#{@contest.friendly_id}"
|
||||||
end
|
end
|
||||||
@@ -236,12 +198,12 @@ class ContestsController < ApplicationController
|
|||||||
@contest = Contest.find(params[:contest_id])
|
@contest = Contest.find(params[:contest_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def contest_params
|
def new_contest_params
|
||||||
params.expect(contest: [ :lang, :name, :offline_form, :public, :team, :allow_registration ])
|
params.expect(contest: [ :name ])
|
||||||
end
|
end
|
||||||
|
|
||||||
def settings_general_params
|
def settings_general_params
|
||||||
params.expect(contest: [ :lang, :name, :public, :team, :allow_registration ])
|
params.expect(contest: [ :lang, :name, :public, :ranking_mode, :team, :allow_registration ])
|
||||||
end
|
end
|
||||||
|
|
||||||
def settings_offline_params
|
def settings_offline_params
|
||||||
|
|||||||
@@ -79,9 +79,6 @@ class MessagesController < ApplicationController
|
|||||||
def convert
|
def convert
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
|
|
||||||
@completion = Completion.new()
|
@completion = Completion.new()
|
||||||
@completion.display_time_from_start = @message.display_time
|
@completion.display_time_from_start = @message.display_time
|
||||||
@completion.completed = true
|
@completion.completed = true
|
||||||
|
|||||||
@@ -11,16 +11,11 @@ class PuzzlesController < ApplicationController
|
|||||||
|
|
||||||
def edit
|
def edit
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
@puzzle = Puzzle.new
|
@puzzle = Puzzle.new
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -32,8 +27,6 @@ class PuzzlesController < ApplicationController
|
|||||||
if @puzzle.save
|
if @puzzle.save
|
||||||
redirect_to contest_path(@contest), notice: t("puzzles.new.notice")
|
redirect_to contest_path(@contest), notice: t("puzzles.new.notice")
|
||||||
else
|
else
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
render :new, status: :unprocessable_entity
|
render :new, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -44,8 +37,6 @@ class PuzzlesController < ApplicationController
|
|||||||
if @puzzle.update(puzzle_params)
|
if @puzzle.update(puzzle_params)
|
||||||
redirect_to @contest, notice: t("puzzles.edit.notice")
|
redirect_to @contest, notice: t("puzzles.edit.notice")
|
||||||
else
|
else
|
||||||
@action_name = t("helpers.buttons.back")
|
|
||||||
@action_path = contest_path(@contest)
|
|
||||||
render :edit, status: :unprocessable_entity
|
render :edit, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,11 +4,14 @@ class UsersController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
authorize :user
|
authorize :user
|
||||||
|
|
||||||
|
@title = t("users.index.title")
|
||||||
@users = User.all
|
@users = User.all
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
authorize @user
|
authorize @user
|
||||||
|
|
||||||
|
@title = t("users.edit.title")
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
|
|||||||
3
app/lib/ranking.rb
Normal file
3
app/lib/ranking.rb
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module Ranking
|
||||||
|
AVAILABLE_RANKING_MODES = [ { id: "actual", name: I18n.t("lib.ranking.actual") }, { id: "theorical", name: I18n.t("lib.ranking.theorical") } ]
|
||||||
|
end
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
# name :string
|
# name :string
|
||||||
# offline_form :boolean default(FALSE)
|
# offline_form :boolean default(FALSE)
|
||||||
# public :boolean default(FALSE)
|
# public :boolean default(FALSE)
|
||||||
|
# ranking_mode :string
|
||||||
# slug :string
|
# slug :string
|
||||||
# team :boolean default(FALSE)
|
# team :boolean default(FALSE)
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
@@ -38,6 +39,7 @@ class Contest < ApplicationRecord
|
|||||||
|
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
validates :lang, inclusion: { in: Languages::AVAILABLE_LANGUAGES.map { |lang| lang[:id] } }
|
validates :lang, inclusion: { in: Languages::AVAILABLE_LANGUAGES.map { |lang| lang[:id] } }
|
||||||
|
validates :ranking_mode, inclusion: { in: Ranking::AVAILABLE_RANKING_MODES.map { |lang| lang[:id] } }
|
||||||
|
|
||||||
generates_token_for :token
|
generates_token_for :token
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -42,3 +42,12 @@ javascript:
|
|||||||
li.nav-item
|
li.nav-item
|
||||||
a.nav-link class=active_page(contest_messages_path(@contest)) href=contest_messages_path(@contest)
|
a.nav-link class=active_page(contest_messages_path(@contest)) href=contest_messages_path(@contest)
|
||||||
= t("messages.plural").capitalize
|
= t("messages.plural").capitalize
|
||||||
|
li.nav-item
|
||||||
|
a.nav-link class=active_page("/contests/#{@contest.id}/settings/general") href="/contests/#{@contest.id}/settings/general"
|
||||||
|
= t("contests.form.general")
|
||||||
|
li.nav-item
|
||||||
|
a.nav-link class=active_page("/contests/#{@contest.id}/settings/offline") href="/contests/#{@contest.id}/settings/offline"
|
||||||
|
= t("contests.form.offline")
|
||||||
|
li.nav-item
|
||||||
|
a.nav-link class=active_page("/contests/#{@contest.id}/settings/categories") href="/contests/#{@contest.id}/settings/categories"
|
||||||
|
= t("contests.form.categories")
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
.row
|
|
||||||
.col
|
|
||||||
h3 Informations
|
|
||||||
|
|
||||||
= form_with model: contestant, url: url, method: method do |form|
|
= form_with model: contestant, url: url, method: method do |form|
|
||||||
.row.mb-3
|
.row.mb-3
|
||||||
.col
|
.col
|
||||||
@@ -15,7 +11,7 @@
|
|||||||
= form.label :email
|
= form.label :email
|
||||||
.form-text
|
.form-text
|
||||||
= t("activerecord.attributes.contestant.email_description")
|
= t("activerecord.attributes.contestant.email_description")
|
||||||
- if @contest.categories
|
- if @contest.categories && method == :patch
|
||||||
.row.mt-4
|
.row.mt-4
|
||||||
.col
|
.col
|
||||||
- @contest.categories.each do |category|
|
- @contest.categories.each do |category|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
= render "contest_nav"
|
|
||||||
|
|
||||||
.row.mb-4 style="height: calc(100vh - 280px)"
|
.row.mb-4 style="height: calc(100vh - 280px)"
|
||||||
.col.d-flex.flex-column style="height: 100%"
|
.col.d-flex.flex-column style="height: 100%"
|
||||||
.row.mb-4
|
.row.mb-4
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
h5.mb-3 = t("contestants.new.title")
|
||||||
= render "form", contest: @contest, contestant: @contestant, submit_text: t("helpers.buttons.add"), method: :post, url: "/contests/#{@contest.id}/contestants"
|
= render "form", contest: @contest, contestant: @contestant, submit_text: t("helpers.buttons.add"), method: :post, url: "/contests/#{@contest.id}/contestants"
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
.row
|
|
||||||
.col
|
|
||||||
ul.nav.nav-tabs.mb-4
|
|
||||||
li.nav-item
|
|
||||||
a.nav-link class=active_page("/contests/#{@contest.id}/settings/general") href="/contests/#{@contest.id}/settings/general"
|
|
||||||
= t("contests.form.general")
|
|
||||||
li.nav-item
|
|
||||||
a.nav-link class=active_page("/contests/#{@contest.id}/settings/offline") href="/contests/#{@contest.id}/settings/offline"
|
|
||||||
= t("contests.form.offline")
|
|
||||||
li.nav-item
|
|
||||||
a.nav-link class=active_page("/contests/#{@contest.id}/settings/categories") href="/contests/#{@contest.id}/settings/categories"
|
|
||||||
= t("contests.form.categories")
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
= render "settings_nav"
|
|
||||||
|
|
||||||
= form_with model: Category, url: "/contests/#{@contest.id}/categories" do |form|
|
= form_with model: Category, url: "/contests/#{@contest.id}/categories" do |form|
|
||||||
- if @contest.categories.size > 0
|
- if @contest.categories.size > 0
|
||||||
.row
|
.row
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
= render "settings_nav"
|
|
||||||
|
|
||||||
= form_with model: @contest, url: "/contests/#{@contest.id}/settings/general" do |form|
|
= form_with model: @contest, url: "/contests/#{@contest.id}/settings/general" do |form|
|
||||||
.row.mt-2.mb-3
|
.row.mt-2.mb-3
|
||||||
.col
|
.col
|
||||||
@@ -16,6 +14,11 @@
|
|||||||
.form-check.form-switch
|
.form-check.form-switch
|
||||||
= form.check_box :public, class: "form-check-input"
|
= form.check_box :public, class: "form-check-input"
|
||||||
= form.label :public
|
= form.label :public
|
||||||
|
.row.mb-3
|
||||||
|
.col
|
||||||
|
.form-floating
|
||||||
|
= form.select :ranking_mode, Ranking::AVAILABLE_RANKING_MODES.map { |mode| [ mode[:name], mode[:id] ] }, {}, class: "form-select"
|
||||||
|
= form.label :ranking_mode
|
||||||
.row.mb-3
|
.row.mb-3
|
||||||
.col
|
.col
|
||||||
.form-check.form-switch
|
.form-check.form-switch
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
= render "settings_nav"
|
|
||||||
|
|
||||||
- if @contest.puzzles.length > 1
|
- if @contest.puzzles.length > 1
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
|
|||||||
@@ -44,9 +44,15 @@ html
|
|||||||
= msg
|
= msg
|
||||||
|
|
||||||
h1.mb-4
|
h1.mb-4
|
||||||
= @title
|
- if @contest
|
||||||
- if @action_path
|
= @contest.name
|
||||||
|
- if active_page("/public") == "active" && @action_path
|
||||||
a.ms-4.btn.btn-primary href=@action_path style="margin-top: -6px"
|
a.ms-4.btn.btn-primary href=@action_path style="margin-top: -6px"
|
||||||
= @action_name
|
= t("helpers.buttons.refresh")
|
||||||
|
- else
|
||||||
|
= @title
|
||||||
|
|
||||||
|
- if @contest && active_page("/public") != "active"
|
||||||
|
= render "contest_nav"
|
||||||
|
|
||||||
= yield
|
= yield
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
= render "contest_nav"
|
|
||||||
|
|
||||||
.row.mb-4 style="height: calc(100vh - 280px)"
|
.row.mb-4 style="height: calc(100vh - 280px)"
|
||||||
.col.d-flex.flex-column style="height: 100%"
|
.col.d-flex.flex-column style="height: 100%"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
= render "contest_nav"
|
|
||||||
|
|
||||||
.row.mb-4 style="height: calc(100vh - 280px)"
|
.row.mb-4 style="height: calc(100vh - 280px)"
|
||||||
.col.d-flex.flex-column style="height: 100%"
|
.col.d-flex.flex-column style="height: 100%"
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ en:
|
|||||||
offline_form_description: Offline participants will have to fill the form by providing an image taken of the puzzle before starting solving it, and validate their finish time with an upload of an image of the completed puzzle
|
offline_form_description: Offline participants will have to fill the form by providing an image taken of the puzzle before starting solving it, and validate their finish time with an upload of an image of the completed puzzle
|
||||||
offline_form_warning: Only for single-puzzle contests
|
offline_form_warning: Only for single-puzzle contests
|
||||||
public: Enable the public scoreboard
|
public: Enable the public scoreboard
|
||||||
|
ranking_mode: Ranking mode (public scoreboard & CSV exports)
|
||||||
team: Team contest
|
team: Team contest
|
||||||
team_description: For UI display purposes mainly
|
team_description: For UI display purposes mainly
|
||||||
allow_registration: Allow registration
|
allow_registration: Allow registration
|
||||||
@@ -259,6 +260,10 @@ en:
|
|||||||
field: Field
|
field: Field
|
||||||
none: No field selected
|
none: No field selected
|
||||||
rank: Rank
|
rank: Rank
|
||||||
|
lib:
|
||||||
|
ranking:
|
||||||
|
actual: First by time if completed, then by number of pieces assembled
|
||||||
|
theorical: By time only (projected time calculated with the ppm count)
|
||||||
messages:
|
messages:
|
||||||
index:
|
index:
|
||||||
no_messages: No messages received yet
|
no_messages: No messages received yet
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ fr:
|
|||||||
offline_form_description: Les participant.e.s hors-ligne pourront participer en prenant une photo du puzzle avant de le commencer, puis valider leur temps avec une photo du puzzle une fois complété
|
offline_form_description: Les participant.e.s hors-ligne pourront participer en prenant une photo du puzzle avant de le commencer, puis valider leur temps avec une photo du puzzle une fois complété
|
||||||
offline_form_warning: Activable uniquement pour les concours avec un seul puzzle
|
offline_form_warning: Activable uniquement pour les concours avec un seul puzzle
|
||||||
public: Activer le classement public
|
public: Activer le classement public
|
||||||
|
ranking_mode: Mode de classement (classement public & exports CSV)
|
||||||
team: Concours par équipes
|
team: Concours par équipes
|
||||||
team_description: Principalement pour des raisons d'affichage
|
team_description: Principalement pour des raisons d'affichage
|
||||||
allow_registration: Autoriser l'inscription via l'interface
|
allow_registration: Autoriser l'inscription via l'interface
|
||||||
@@ -230,6 +231,10 @@ fr:
|
|||||||
field: Champ
|
field: Champ
|
||||||
none: Aucun champ sélectionné
|
none: Aucun champ sélectionné
|
||||||
rank: Rang
|
rank: Rang
|
||||||
|
lib:
|
||||||
|
ranking:
|
||||||
|
actual: Par temps d'abord, puis par nombre de pièces assemblées
|
||||||
|
theorical: Par temps uniquement (temps projeté calculé à partir de la vitesse d'assemblage)
|
||||||
messages:
|
messages:
|
||||||
index:
|
index:
|
||||||
no_messages: Pas de messages reçus pour le moment
|
no_messages: Pas de messages reçus pour le moment
|
||||||
|
|||||||
5
db/migrate/20251114085123_add_ranking_mode_to_contest.rb
Normal file
5
db/migrate/20251114085123_add_ranking_mode_to_contest.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddRankingModeToContest < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :contests, :ranking_mode, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
3
db/schema.rb
generated
3
db/schema.rb
generated
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[8.0].define(version: 2025_11_10_110151) do
|
ActiveRecord::Schema[8.0].define(version: 2025_11_14_085123) do
|
||||||
create_table "active_storage_attachments", force: :cascade do |t|
|
create_table "active_storage_attachments", force: :cascade do |t|
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.string "record_type", null: false
|
t.string "record_type", null: false
|
||||||
@@ -95,6 +95,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_10_110151) do
|
|||||||
t.string "lang", default: "en"
|
t.string "lang", default: "en"
|
||||||
t.boolean "public", default: false
|
t.boolean "public", default: false
|
||||||
t.boolean "offline_form", default: false
|
t.boolean "offline_form", default: false
|
||||||
|
t.string "ranking_mode"
|
||||||
t.index ["slug"], name: "index_contests_on_slug", unique: true
|
t.index ["slug"], name: "index_contests_on_slug", unique: true
|
||||||
t.index ["user_id"], name: "index_contests_on_user_id"
|
t.index ["user_id"], name: "index_contests_on_user_id"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
# name :string
|
# name :string
|
||||||
# offline_form :boolean default(FALSE)
|
# offline_form :boolean default(FALSE)
|
||||||
# public :boolean default(FALSE)
|
# public :boolean default(FALSE)
|
||||||
|
# ranking_mode :string
|
||||||
# slug :string
|
# slug :string
|
||||||
# team :boolean default(FALSE)
|
# team :boolean default(FALSE)
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
|
|||||||
Reference in New Issue
Block a user