Contest language & top buttons
This commit is contained in:
parent
71f2bb6b70
commit
ac3b354480
@ -70,6 +70,8 @@ class ContestsController < ApplicationController
|
|||||||
end
|
end
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
|
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| [ -contestant.completions.size, contestant.time_seconds ] }
|
@contestants = @contest.contestants.sort_by { |contestant| [ -contestant.completions.size, contestant.time_seconds ] }
|
||||||
@puzzles = @contest.puzzles.order(:id)
|
@puzzles = @contest.puzzles.order(:id)
|
||||||
@ -89,6 +91,6 @@ class ContestsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def contest_params
|
def contest_params
|
||||||
params.expect(contest: [ :name, :team, :allow_registration ])
|
params.expect(contest: [ :lang, :name, :team, :allow_registration ])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
module Languages
|
module Languages
|
||||||
AVAILABLE_LANGUAGES = [ { id: "en", name: "English" }, { id: "fr", name: "French" } ]
|
AVAILABLE_LANGUAGES = [ { id: "en", name: "English" }, { id: "fr", name: "Français" } ]
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# allow_registration :boolean default(FALSE)
|
# allow_registration :boolean default(FALSE)
|
||||||
|
# lang :string default("en")
|
||||||
# name :string
|
# name :string
|
||||||
# slug :string
|
# slug :string
|
||||||
# team :boolean default(FALSE)
|
# team :boolean default(FALSE)
|
||||||
@ -32,6 +33,7 @@ class Contest < ApplicationRecord
|
|||||||
friendly_id :name, use: :slugged
|
friendly_id :name, use: :slugged
|
||||||
|
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
|
validates :lang, inclusion: { in: Languages::AVAILABLE_LANGUAGES.map { |lang| lang[:id] } }
|
||||||
|
|
||||||
generates_token_for :token
|
generates_token_for :token
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
.form-floating
|
.form-floating
|
||||||
= form.text_field :name, autocomplete: "off", class: "form-control"
|
= form.text_field :name, autocomplete: "off", class: "form-control"
|
||||||
= form.label :name, class: "required"
|
= form.label :name, class: "required"
|
||||||
|
.row.mb-3
|
||||||
|
.col
|
||||||
|
.form-floating
|
||||||
|
= form.select :lang, Languages::AVAILABLE_LANGUAGES.map { |lang| [ lang[:name], lang[:id] ] }, {}, class: "form-select"
|
||||||
|
= form.label :lang
|
||||||
.row.mb-3
|
.row.mb-3
|
||||||
.col
|
.col
|
||||||
.form-check.form-switch
|
.form-check.form-switch
|
||||||
|
@ -2,13 +2,14 @@ table.table.table-striped.table-hover
|
|||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th scope="col"
|
th scope="col"
|
||||||
| Rank
|
= t("helpers.rank")
|
||||||
th scope="col"
|
th scope="col"
|
||||||
| Name
|
= t("activerecord.attributes.contestant.name")
|
||||||
|
- if @contest.puzzles.size > 1
|
||||||
th scope="col"
|
th scope="col"
|
||||||
| Completed puzzles
|
= t("activerecord.attributes.contestant.completions")
|
||||||
th scope="col"
|
th scope="col"
|
||||||
| Total time
|
= t("activerecord.attributes.contestant.display_time")
|
||||||
tbody
|
tbody
|
||||||
- @contestants.each_with_index do |contestant, index|
|
- @contestants.each_with_index do |contestant, index|
|
||||||
tr scope="row"
|
tr scope="row"
|
||||||
@ -16,6 +17,7 @@ table.table.table-striped.table-hover
|
|||||||
= index + 1
|
= index + 1
|
||||||
td
|
td
|
||||||
= contestant.name
|
= contestant.name
|
||||||
|
- if @contest.puzzles.size > 1
|
||||||
td
|
td
|
||||||
= contestant.completions.length
|
= contestant.completions.length
|
||||||
td
|
td
|
||||||
|
@ -1,21 +1,31 @@
|
|||||||
.row.mb-4
|
- if @badges.size > 0
|
||||||
|
.row.mb-4
|
||||||
.col
|
.col
|
||||||
css:
|
.badges style="margin-top: -18px; position: absolute"
|
||||||
.badges { margin-top: -18px; position: absolute; }
|
|
||||||
.badges
|
|
||||||
- @badges.each do |badge|
|
- @badges.each do |badge|
|
||||||
span.badge.text-bg-info.me-2
|
span.badge.text-bg-info.me-2
|
||||||
= badge
|
= badge
|
||||||
|
|
||||||
.row
|
javascript:
|
||||||
.col.alert.alert-success
|
async function copyExtensionUrlToClipboard() {
|
||||||
= t("contests.show.public_scoreboard")
|
await navigator.clipboard.writeText("#{message_url}?token=#{@contest.generate_token_for(:token)}");
|
||||||
= link_to root_url + "public/#{@contest.slug}", root_url + "public/#{@contest.slug}"
|
}
|
||||||
|
|
||||||
.row.mb-4
|
.row.mb-5
|
||||||
.col.alert.alert-success
|
.col
|
||||||
|> URL for the public scoreboard extension:
|
a.btn.btn-success href="/public/#{@contest.slug}"
|
||||||
= link_to "#{message_url}?token=#{@contest.generate_token_for(:token)}"
|
= t("contests.show.open_public_scoreboard")
|
||||||
|
button.btn.btn-success.ms-3 onclick="copyExtensionUrlToClipboard()"
|
||||||
|
css:
|
||||||
|
button > svg {
|
||||||
|
margin-right: 2px;
|
||||||
|
margin-top: -3px;
|
||||||
|
}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16">
|
||||||
|
<path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z"/>
|
||||||
|
<path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z"/>
|
||||||
|
</svg>
|
||||||
|
=< t("contests.show.copy_extension_url")
|
||||||
|
|
||||||
.row.mb-4
|
.row.mb-4
|
||||||
.col-7
|
.col-7
|
||||||
|
@ -45,17 +45,18 @@ en:
|
|||||||
display_relative_time: Time for this puzzle
|
display_relative_time: Time for this puzzle
|
||||||
puzzle: Puzzle
|
puzzle: Puzzle
|
||||||
contest:
|
contest:
|
||||||
name: "Name"
|
lang: Language for the public scoreboard
|
||||||
team: "Team contest"
|
name: Name
|
||||||
team_description: "For UI display purposes mainly"
|
team: Team contest
|
||||||
allow_registration: "Allow registration"
|
team_description: For UI display purposes mainly
|
||||||
allow_registration_description: "Generates a shareable registration form for this contest"
|
allow_registration: Allow registration
|
||||||
|
allow_registration_description: Generates a shareable registration form for this contest
|
||||||
contestant:
|
contestant:
|
||||||
completions: completions
|
completions: completions
|
||||||
display_time: Time
|
display_time: Time
|
||||||
email: "Email"
|
email: Email
|
||||||
name: "Name"
|
name: Name
|
||||||
email_description: "Optional. Used for sending emails through this app, or for identifying participants whose gmeet handle doesn't match their registered name."
|
email_description: Optional. Used for sending emails through this app, or for identifying participants whose gmeet handle doesn't match their registered name.
|
||||||
csv_import:
|
csv_import:
|
||||||
file: File
|
file: File
|
||||||
separator: Separator
|
separator: Separator
|
||||||
@ -128,9 +129,10 @@ en:
|
|||||||
title: "%{name}"
|
title: "%{name}"
|
||||||
show:
|
show:
|
||||||
title: "%{name}"
|
title: "%{name}"
|
||||||
add_participant: "Add contestant"
|
add_participant: Add contestant
|
||||||
add_puzzle: "Add puzzle"
|
add_puzzle: Add puzzle
|
||||||
public_scoreboard: "Public scoreboard: "
|
copy_extension_url: Copy the URL for connecting from the browser extension
|
||||||
|
open_public_scoreboard: Open public scoreboard
|
||||||
contestants:
|
contestants:
|
||||||
convert_csv:
|
convert_csv:
|
||||||
title: "Import participants"
|
title: "Import participants"
|
||||||
|
@ -16,17 +16,18 @@ fr:
|
|||||||
display_relative_time: Temps pour ce puzzle
|
display_relative_time: Temps pour ce puzzle
|
||||||
puzzle: Puzzle
|
puzzle: Puzzle
|
||||||
contest:
|
contest:
|
||||||
name: "Nom"
|
lang: Langue pour le classement public
|
||||||
team: "Concours par équipes"
|
name: Nom
|
||||||
team_description: "Principalement pour des raisons d'affichage"
|
team: Concours par équipes
|
||||||
allow_registration: "Autoriser l'inscription via l'interface"
|
team_description: Principalement pour des raisons d'affichage
|
||||||
allow_registration_description: "Génère un formulaire d'inscription pour ce concours"
|
allow_registration: Autoriser l'inscription via l'interface
|
||||||
|
allow_registration_description: Génère un formulaire d'inscription pour ce concours
|
||||||
contestant:
|
contestant:
|
||||||
completions: Complétions
|
completions: Complétions
|
||||||
display_time: Temps
|
display_time: Temps
|
||||||
email: "Email"
|
email: Email
|
||||||
name: "Nom"
|
name: Nom
|
||||||
email_description: "Optionnel. Utile pour envoyer des emails aux participant.e.s depuis cette app, ou pour reconnaître les pseudos gmeet quand ils ne correspondent pas au nom préalablement entré."
|
email_description: Optionnel. Utile pour envoyer des emails aux participant.e.s depuis cette app, ou pour reconnaître les pseudos gmeet quand ils ne correspondent pas au nom préalablement entré.
|
||||||
csv_import:
|
csv_import:
|
||||||
file: Fichier
|
file: Fichier
|
||||||
separator: Délimiteur
|
separator: Délimiteur
|
||||||
@ -99,9 +100,10 @@ fr:
|
|||||||
title: "%{name}"
|
title: "%{name}"
|
||||||
show:
|
show:
|
||||||
title: "%{name}"
|
title: "%{name}"
|
||||||
add_participant: "Ajouter un.e participant.e"
|
add_participant: Ajouter un.e participant.e
|
||||||
add_puzzle: "Ajouter un puzzle"
|
add_puzzle: Ajouter un puzzle
|
||||||
public_scoreboard: "Classement public : "
|
copy_extension_url: Copier l'URL pour la connexion depuis l'extension web
|
||||||
|
open_public_scoreboard: Ouvrir le classement public
|
||||||
contestants:
|
contestants:
|
||||||
convert_csv:
|
convert_csv:
|
||||||
title: "Importer des participant.e.s"
|
title: "Importer des participant.e.s"
|
||||||
|
5
db/migrate/20250620051905_add_lang_to_contest.rb
Normal file
5
db/migrate/20250620051905_add_lang_to_contest.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class AddLangToContest < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :contests, :lang, :string, default: 'en'
|
||||||
|
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_06_18_155041) do
|
ActiveRecord::Schema[8.0].define(version: 2025_06_20_051905) 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
|
||||||
@ -74,6 +74,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_18_155041) do
|
|||||||
t.boolean "team", default: false
|
t.boolean "team", default: false
|
||||||
t.boolean "allow_registration", default: false
|
t.boolean "allow_registration", default: false
|
||||||
t.string "slug"
|
t.string "slug"
|
||||||
|
t.string "lang", default: "en"
|
||||||
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
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# allow_registration :boolean default(FALSE)
|
# allow_registration :boolean default(FALSE)
|
||||||
|
# lang :string default("en")
|
||||||
# name :string
|
# name :string
|
||||||
# slug :string
|
# slug :string
|
||||||
# team :boolean default(FALSE)
|
# team :boolean default(FALSE)
|
||||||
|
1
test/fixtures/contests.yml
vendored
1
test/fixtures/contests.yml
vendored
@ -6,6 +6,7 @@
|
|||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# allow_registration :boolean default(FALSE)
|
# allow_registration :boolean default(FALSE)
|
||||||
|
# lang :string default("en")
|
||||||
# name :string
|
# name :string
|
||||||
# slug :string
|
# slug :string
|
||||||
# team :boolean default(FALSE)
|
# team :boolean default(FALSE)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# allow_registration :boolean default(FALSE)
|
# allow_registration :boolean default(FALSE)
|
||||||
|
# lang :string default("en")
|
||||||
# name :string
|
# name :string
|
||||||
# slug :string
|
# slug :string
|
||||||
# team :boolean default(FALSE)
|
# team :boolean default(FALSE)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user