Contest language & top buttons
This commit is contained in:
parent
71f2bb6b70
commit
ac3b354480
@ -70,6 +70,8 @@ class ContestsController < ApplicationController
|
||||
end
|
||||
authorize @contest
|
||||
|
||||
I18n.locale = @contest.lang
|
||||
|
||||
@title = I18n.t("contests.scoreboard.title", name: @contest.name)
|
||||
@contestants = @contest.contestants.sort_by { |contestant| [ -contestant.completions.size, contestant.time_seconds ] }
|
||||
@puzzles = @contest.puzzles.order(:id)
|
||||
@ -89,6 +91,6 @@ class ContestsController < ApplicationController
|
||||
end
|
||||
|
||||
def contest_params
|
||||
params.expect(contest: [ :name, :team, :allow_registration ])
|
||||
params.expect(contest: [ :lang, :name, :team, :allow_registration ])
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,3 @@
|
||||
module Languages
|
||||
AVAILABLE_LANGUAGES = [ { id: "en", name: "English" }, { id: "fr", name: "French" } ]
|
||||
AVAILABLE_LANGUAGES = [ { id: "en", name: "English" }, { id: "fr", name: "Français" } ]
|
||||
end
|
||||
|
@ -4,6 +4,7 @@
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# allow_registration :boolean default(FALSE)
|
||||
# lang :string default("en")
|
||||
# name :string
|
||||
# slug :string
|
||||
# team :boolean default(FALSE)
|
||||
@ -32,6 +33,7 @@ class Contest < ApplicationRecord
|
||||
friendly_id :name, use: :slugged
|
||||
|
||||
validates :name, presence: true
|
||||
validates :lang, inclusion: { in: Languages::AVAILABLE_LANGUAGES.map { |lang| lang[:id] } }
|
||||
|
||||
generates_token_for :token
|
||||
end
|
||||
|
@ -4,6 +4,11 @@
|
||||
.form-floating
|
||||
= form.text_field :name, autocomplete: "off", class: "form-control"
|
||||
= 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
|
||||
.col
|
||||
.form-check.form-switch
|
||||
|
@ -2,13 +2,14 @@ table.table.table-striped.table-hover
|
||||
thead
|
||||
tr
|
||||
th scope="col"
|
||||
| Rank
|
||||
= t("helpers.rank")
|
||||
th scope="col"
|
||||
| Name
|
||||
= t("activerecord.attributes.contestant.name")
|
||||
- if @contest.puzzles.size > 1
|
||||
th scope="col"
|
||||
= t("activerecord.attributes.contestant.completions")
|
||||
th scope="col"
|
||||
| Completed puzzles
|
||||
th scope="col"
|
||||
| Total time
|
||||
= t("activerecord.attributes.contestant.display_time")
|
||||
tbody
|
||||
- @contestants.each_with_index do |contestant, index|
|
||||
tr scope="row"
|
||||
@ -16,7 +17,8 @@ table.table.table-striped.table-hover
|
||||
= index + 1
|
||||
td
|
||||
= contestant.name
|
||||
td
|
||||
= contestant.completions.length
|
||||
- if @contest.puzzles.size > 1
|
||||
td
|
||||
= contestant.completions.length
|
||||
td
|
||||
= contestant.display_time
|
@ -1,21 +1,31 @@
|
||||
.row.mb-4
|
||||
- if @badges.size > 0
|
||||
.row.mb-4
|
||||
.col
|
||||
.badges style="margin-top: -18px; position: absolute"
|
||||
- @badges.each do |badge|
|
||||
span.badge.text-bg-info.me-2
|
||||
= badge
|
||||
|
||||
javascript:
|
||||
async function copyExtensionUrlToClipboard() {
|
||||
await navigator.clipboard.writeText("#{message_url}?token=#{@contest.generate_token_for(:token)}");
|
||||
}
|
||||
|
||||
.row.mb-5
|
||||
.col
|
||||
css:
|
||||
.badges { margin-top: -18px; position: absolute; }
|
||||
.badges
|
||||
- @badges.each do |badge|
|
||||
span.badge.text-bg-info.me-2
|
||||
= badge
|
||||
|
||||
.row
|
||||
.col.alert.alert-success
|
||||
= t("contests.show.public_scoreboard")
|
||||
= link_to root_url + "public/#{@contest.slug}", root_url + "public/#{@contest.slug}"
|
||||
|
||||
.row.mb-4
|
||||
.col.alert.alert-success
|
||||
|> URL for the public scoreboard extension:
|
||||
= link_to "#{message_url}?token=#{@contest.generate_token_for(:token)}"
|
||||
a.btn.btn-success href="/public/#{@contest.slug}"
|
||||
= 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
|
||||
.col-7
|
||||
|
@ -45,17 +45,18 @@ en:
|
||||
display_relative_time: Time for this puzzle
|
||||
puzzle: Puzzle
|
||||
contest:
|
||||
name: "Name"
|
||||
team: "Team contest"
|
||||
team_description: "For UI display purposes mainly"
|
||||
allow_registration: "Allow registration"
|
||||
allow_registration_description: "Generates a shareable registration form for this contest"
|
||||
lang: Language for the public scoreboard
|
||||
name: Name
|
||||
team: Team contest
|
||||
team_description: For UI display purposes mainly
|
||||
allow_registration: Allow registration
|
||||
allow_registration_description: Generates a shareable registration form for this contest
|
||||
contestant:
|
||||
completions: completions
|
||||
display_time: Time
|
||||
email: "Email"
|
||||
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: Email
|
||||
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.
|
||||
csv_import:
|
||||
file: File
|
||||
separator: Separator
|
||||
@ -128,9 +129,10 @@ en:
|
||||
title: "%{name}"
|
||||
show:
|
||||
title: "%{name}"
|
||||
add_participant: "Add contestant"
|
||||
add_puzzle: "Add puzzle"
|
||||
public_scoreboard: "Public scoreboard: "
|
||||
add_participant: Add contestant
|
||||
add_puzzle: Add puzzle
|
||||
copy_extension_url: Copy the URL for connecting from the browser extension
|
||||
open_public_scoreboard: Open public scoreboard
|
||||
contestants:
|
||||
convert_csv:
|
||||
title: "Import participants"
|
||||
|
@ -16,17 +16,18 @@ fr:
|
||||
display_relative_time: Temps pour ce puzzle
|
||||
puzzle: Puzzle
|
||||
contest:
|
||||
name: "Nom"
|
||||
team: "Concours par équipes"
|
||||
team_description: "Principalement pour des raisons d'affichage"
|
||||
allow_registration: "Autoriser l'inscription via l'interface"
|
||||
allow_registration_description: "Génère un formulaire d'inscription pour ce concours"
|
||||
lang: Langue pour le classement public
|
||||
name: Nom
|
||||
team: Concours par équipes
|
||||
team_description: Principalement pour des raisons d'affichage
|
||||
allow_registration: Autoriser l'inscription via l'interface
|
||||
allow_registration_description: Génère un formulaire d'inscription pour ce concours
|
||||
contestant:
|
||||
completions: Complétions
|
||||
display_time: Temps
|
||||
email: "Email"
|
||||
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: Email
|
||||
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é.
|
||||
csv_import:
|
||||
file: Fichier
|
||||
separator: Délimiteur
|
||||
@ -99,9 +100,10 @@ fr:
|
||||
title: "%{name}"
|
||||
show:
|
||||
title: "%{name}"
|
||||
add_participant: "Ajouter un.e participant.e"
|
||||
add_puzzle: "Ajouter un puzzle"
|
||||
public_scoreboard: "Classement public : "
|
||||
add_participant: Ajouter un.e participant.e
|
||||
add_puzzle: Ajouter un puzzle
|
||||
copy_extension_url: Copier l'URL pour la connexion depuis l'extension web
|
||||
open_public_scoreboard: Ouvrir le classement public
|
||||
contestants:
|
||||
convert_csv:
|
||||
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.
|
||||
|
||||
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|
|
||||
t.string "name", 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 "allow_registration", default: false
|
||||
t.string "slug"
|
||||
t.string "lang", default: "en"
|
||||
t.index ["slug"], name: "index_contests_on_slug", unique: true
|
||||
t.index ["user_id"], name: "index_contests_on_user_id"
|
||||
end
|
||||
|
@ -4,6 +4,7 @@
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# allow_registration :boolean default(FALSE)
|
||||
# lang :string default("en")
|
||||
# name :string
|
||||
# slug :string
|
||||
# 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
|
||||
# allow_registration :boolean default(FALSE)
|
||||
# lang :string default("en")
|
||||
# name :string
|
||||
# slug :string
|
||||
# team :boolean default(FALSE)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# allow_registration :boolean default(FALSE)
|
||||
# lang :string default("en")
|
||||
# name :string
|
||||
# slug :string
|
||||
# team :boolean default(FALSE)
|
||||
|
Loading…
x
Reference in New Issue
Block a user