@@ -3,7 +3,7 @@ class ContestsController < ApplicationController
|
|||||||
include ContestantsConcern
|
include ContestantsConcern
|
||||||
|
|
||||||
before_action :set_contest, only: %i[ destroy show ]
|
before_action :set_contest, only: %i[ destroy show ]
|
||||||
before_action :set_settings_contest, only: %i[ settings_general_edit settings_general_update settings_public_edit settings_public_update settings_onsite_edit settings_onsite_update settings_online_edit settings_online_update settings_categories_edit ]
|
before_action :set_settings_contest, only: %i[ stopwatch stopwatch_continue stopwatch_pause stopwatch_reset stopwatch_start settings_general_edit settings_general_update settings_public_edit settings_public_update settings_onsite_edit settings_onsite_update settings_online_edit settings_online_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 ]
|
||||||
|
|
||||||
@@ -80,6 +80,47 @@ class ContestsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def stopwatch
|
||||||
|
authorize @contest
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopwatch_continue
|
||||||
|
authorize @contest
|
||||||
|
|
||||||
|
pause_duration = Time.now() - @contest.pause_time
|
||||||
|
@contest.start_time = @contest.start_time + pause_duration
|
||||||
|
@contest.pause_time = nil
|
||||||
|
@contest.save
|
||||||
|
redirect_to "/contests/#{@contest.id}/stopwatch"
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopwatch_pause
|
||||||
|
authorize @contest
|
||||||
|
authorize @contest
|
||||||
|
|
||||||
|
@contest.pause_time = Time.now()
|
||||||
|
@contest.save
|
||||||
|
redirect_to "/contests/#{@contest.id}/stopwatch"
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopwatch_reset
|
||||||
|
authorize @contest
|
||||||
|
|
||||||
|
@contest.start_time = nil
|
||||||
|
@contest.pause_time = nil
|
||||||
|
@contest.save
|
||||||
|
redirect_to "/contests/#{@contest.id}/stopwatch"
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopwatch_start
|
||||||
|
authorize @contest
|
||||||
|
|
||||||
|
@contest.start_time = Time.now()
|
||||||
|
@contest.pause_time = nil
|
||||||
|
@contest.save
|
||||||
|
redirect_to "/contests/#{@contest.id}/stopwatch"
|
||||||
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :contest
|
authorize :contest
|
||||||
|
|
||||||
@@ -241,7 +282,7 @@ class ContestsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def settings_public_params
|
def settings_public_params
|
||||||
params.expect(contest: [ :public, :ranking_mode ])
|
params.expect(contest: [ :public, :ranking_mode, :show_stopwatch ])
|
||||||
end
|
end
|
||||||
|
|
||||||
def settings_onsite_params
|
def settings_onsite_params
|
||||||
|
|||||||
@@ -10,9 +10,12 @@
|
|||||||
# lang :string default("en")
|
# lang :string default("en")
|
||||||
# name :string
|
# name :string
|
||||||
# offline_form :boolean default(FALSE)
|
# offline_form :boolean default(FALSE)
|
||||||
|
# pause_time :datetime
|
||||||
# public :boolean default(FALSE)
|
# public :boolean default(FALSE)
|
||||||
# ranking_mode :string
|
# ranking_mode :string
|
||||||
|
# show_stopwatch :boolean
|
||||||
# slug :string
|
# slug :string
|
||||||
|
# start_time :datetime
|
||||||
# team :boolean default(FALSE)
|
# team :boolean default(FALSE)
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
|||||||
@@ -75,6 +75,26 @@ class ContestPolicy < ApplicationPolicy
|
|||||||
edit?
|
edit?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def stopwatch?
|
||||||
|
edit?
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopwatch_continue?
|
||||||
|
edit?
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopwatch_pause?
|
||||||
|
edit?
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopwatch_reset?
|
||||||
|
edit?
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopwatch_start?
|
||||||
|
edit?
|
||||||
|
end
|
||||||
|
|
||||||
def finalize_import?
|
def finalize_import?
|
||||||
owner_or_admin
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
li.nav-item
|
li.nav-item
|
||||||
a.nav-link class=active_page(contest_puzzles_path(@contest)) href=contest_puzzles_path(@contest)
|
a.nav-link class=active_page(contest_puzzles_path(@contest)) href=contest_puzzles_path(@contest)
|
||||||
= t("puzzles.plural").capitalize
|
= t("puzzles.plural").capitalize
|
||||||
|
li.nav-item
|
||||||
|
a.nav-link class=active_page("/contests/#{@contest.id}/stopwatch") href="/contests/#{@contest.id}/stopwatch"
|
||||||
|
= t("contests.nav.stopwatch").capitalize
|
||||||
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
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th
|
th
|
||||||
|
- if @contest.show_stopwatch
|
||||||
|
.stopwatch id="display-time" style="font-size: 60px; font-weight: 400;"
|
||||||
|
= render "stopwatch_js"
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.row
|
.row
|
||||||
.mt-3.col-6.d-flex.flex-column style="height: calc(100vh - 250px)"
|
.mt-3.col-6.d-flex.flex-column style="height: calc(100vh - 310px)"
|
||||||
.d-flex.flex-column style="overflow-y: auto"
|
.d-flex.flex-column style="overflow-y: auto"
|
||||||
table.table.table-striped.table-hover
|
table.table.table-striped.table-hover
|
||||||
thead
|
thead
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ css:
|
|||||||
|
|
||||||
.row
|
.row
|
||||||
- if @puzzles.size > 0
|
- if @puzzles.size > 0
|
||||||
.d-flex.flex-column.justify-content-center.mb-5
|
.d-flex.flex-column.justify-content-center.mb-2
|
||||||
= image_tag(@puzzles[0].image, style: "max-height: 200px; object-fit: contain") if @puzzles[0].image.attached?
|
= image_tag(@puzzles[0].image, style: "max-height: 200px; object-fit: contain") if @puzzles[0].image.attached?
|
||||||
.mt-2.fs-6 style="text-align: center"
|
.mt-2.fs-6 style="text-align: center"
|
||||||
=> "#{@puzzles[0].name} -"
|
=> "#{@puzzles[0].name} -"
|
||||||
= "#{@puzzles[0].brand} #{@puzzles[0].pieces}p"
|
= "#{@puzzles[0].brand} #{@puzzles[0].pieces}p"
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.mt-3.d-flex.flex-column style="height: calc(100vh - 250px)"
|
.mt-3.d-flex.flex-column
|
||||||
.d-flex.flex-column style="overflow-y: auto"
|
.d-flex.flex-column style="overflow-y: auto"
|
||||||
table.table.table-striped.table-hover
|
table.table.table-striped.table-hover
|
||||||
thead
|
thead
|
||||||
|
|||||||
22
app/views/contests/_stopwatch_js.html.slim
Normal file
22
app/views/contests/_stopwatch_js.html.slim
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
javascript:
|
||||||
|
startTime = #{@contest.start_time.present? ? @contest.start_time.to_i : "null"};
|
||||||
|
pauseTime = #{@contest.pause_time.present? ? @contest.pause_time.to_i : "null"};
|
||||||
|
function updateTime() {
|
||||||
|
const displayTimeEl = document.getElementById('display-time');
|
||||||
|
if (displayTimeEl) {
|
||||||
|
if (startTime) {
|
||||||
|
let s = Math.floor((Date.now() - 1000 * startTime) / 1000);
|
||||||
|
if (pauseTime) s = Math.floor(pauseTime - startTime);
|
||||||
|
let ss = s % 60;
|
||||||
|
let mm = Math.floor(s / 60) % 60;
|
||||||
|
let hh = Math.floor(s / 3600);
|
||||||
|
displayTimeEl.innerHTML = `${hh < 10 ? `0${hh}` : hh}:${mm < 10 ? `0${mm}` : mm}:${ss < 10 ? `0${ss}` : ss}`;
|
||||||
|
setTimeout(updateTime, 1000);
|
||||||
|
} else {
|
||||||
|
displayTimeEl.innerHTML = '00:00:00';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTimeout(updateTime, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(updateTime, 1);
|
||||||
@@ -3,12 +3,16 @@ css:
|
|||||||
.mobile-single { display: block !important; }
|
.mobile-single { display: block !important; }
|
||||||
.desktop-single { display: none; }
|
.desktop-single { display: none; }
|
||||||
#scoreboard-switches { display: none; }
|
#scoreboard-switches { display: none; }
|
||||||
|
.stopwatch { font-size: 50px !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
- if @contest.puzzles.size < 2
|
- if @contest.puzzles.size < 2
|
||||||
= render "selectors"
|
= render "selectors"
|
||||||
|
|
||||||
turbo-frame id="scoreboard"
|
turbo-frame id="scoreboard"
|
||||||
|
- if @contest.show_stopwatch
|
||||||
|
.stopwatch id="display-time" style="font-size: 60px; font-weight: 400; margin-bottom: 0; text-align: center;"
|
||||||
|
= render "stopwatch_js"
|
||||||
a.btn.btn-primary href="" id="refresh-button" style="display: none;"
|
a.btn.btn-primary href="" id="refresh-button" style="display: none;"
|
||||||
.mobile-single style="display: none;"
|
.mobile-single style="display: none;"
|
||||||
= render "scoreboard_mobile_single"
|
= render "scoreboard_mobile_single"
|
||||||
|
|||||||
@@ -6,6 +6,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.mt-2
|
||||||
|
.col
|
||||||
|
.form-check.form-switch
|
||||||
|
= form.check_box :show_stopwatch, class: "form-check-input"
|
||||||
|
= form.label :show_stopwatch
|
||||||
.row.mt-3
|
.row.mt-3
|
||||||
.col
|
.col
|
||||||
.form-floating
|
.form-floating
|
||||||
|
|||||||
16
app/views/contests/stopwatch.html.slim
Normal file
16
app/views/contests/stopwatch.html.slim
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.row
|
||||||
|
.col
|
||||||
|
.alert.alert-primary
|
||||||
|
= t("contests.stopwatch.info")
|
||||||
|
h1.mt-3 id="display-time" style="font-size: 80px;"
|
||||||
|
= render "stopwatch_js"
|
||||||
|
.row.mt-3
|
||||||
|
.col.d-flex
|
||||||
|
- if !@contest.start_time.present?
|
||||||
|
= button_to t("helpers.buttons.stopwatch_start"), "/contests/#{@contest.id}/stopwatch_start", method: :post, class: "btn btn-primary"
|
||||||
|
- if @contest.pause_time.present?
|
||||||
|
= button_to t("helpers.buttons.stopwatch_continue"), "/contests/#{@contest.id}/stopwatch_continue", method: :post, class: "btn btn-primary"
|
||||||
|
- if @contest.start_time.present? && !@contest.pause_time.present?
|
||||||
|
= button_to t("helpers.buttons.stopwatch_pause"), "/contests/#{@contest.id}/stopwatch_pause", method: :post, class: "btn btn-primary"
|
||||||
|
- if @contest.start_time.present?
|
||||||
|
= button_to t("helpers.buttons.stopwatch_reset"), "/contests/#{@contest.id}/stopwatch_reset", method: :post, class: "ms-3 btn btn-warning"
|
||||||
@@ -65,6 +65,7 @@ en:
|
|||||||
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)
|
ranking_mode: Ranking mode (public scoreboard & CSV exports)
|
||||||
|
show_stopwatch: Display the stopwatch
|
||||||
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
|
||||||
@@ -206,12 +207,13 @@ en:
|
|||||||
onsite: Onsite contests
|
onsite: Onsite contests
|
||||||
public: Public scoreboard
|
public: Public scoreboard
|
||||||
settings: Settings
|
settings: Settings
|
||||||
|
stopwatch: Stopwatch
|
||||||
new:
|
new:
|
||||||
notice: Contest added
|
notice: Contest added
|
||||||
title: New jigsaw puzzle contest
|
title: New jigsaw puzzle contest
|
||||||
scoreboard:
|
scoreboard:
|
||||||
all_categories: All categories
|
all_categories: All categories
|
||||||
auto_refresh: Auto refresh
|
auto_refresh: Auto refresh (30s)
|
||||||
hide_offline: Hide offline participants
|
hide_offline: Hide offline participants
|
||||||
refresh: Activate auto-refresh (every 5s)
|
refresh: Activate auto-refresh (every 5s)
|
||||||
title: "%{name}"
|
title: "%{name}"
|
||||||
@@ -225,6 +227,8 @@ en:
|
|||||||
offline_form_disabled: The offline form is disabled
|
offline_form_disabled: The offline form is disabled
|
||||||
public_scoreboard_disabled: The public scoreboard is disabled
|
public_scoreboard_disabled: The public scoreboard is disabled
|
||||||
url_copied: URL copied to the clipboard
|
url_copied: URL copied to the clipboard
|
||||||
|
stopwatch:
|
||||||
|
info: This stopwatch is used for display in the public scoreboard, when allowed in the settings
|
||||||
contestants:
|
contestants:
|
||||||
convert_csv:
|
convert_csv:
|
||||||
title: Import participants
|
title: Import participants
|
||||||
@@ -287,6 +291,10 @@ en:
|
|||||||
sign_in: Sign in
|
sign_in: Sign in
|
||||||
save: Save
|
save: Save
|
||||||
start: Click here to start your participation
|
start: Click here to start your participation
|
||||||
|
stopwatch_continue: Continue
|
||||||
|
stopwatch_pause: Pause
|
||||||
|
stopwatch_reset: Reset
|
||||||
|
stopwatch_start: Start
|
||||||
update: Save modifications
|
update: Save modifications
|
||||||
field: Field
|
field: Field
|
||||||
none: No field selected
|
none: No field selected
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ fr:
|
|||||||
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)
|
ranking_mode: Mode de classement (classement public & exports CSV)
|
||||||
|
show_stopwatch: Afficher le chronomètre
|
||||||
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
|
||||||
@@ -177,12 +178,13 @@ fr:
|
|||||||
onsite: Concours en présentiel
|
onsite: Concours en présentiel
|
||||||
public: Classement public
|
public: Classement public
|
||||||
settings: Paramètres
|
settings: Paramètres
|
||||||
|
stopwatch: Chronomètre
|
||||||
new:
|
new:
|
||||||
notice: Concours ajouté
|
notice: Concours ajouté
|
||||||
title: Nouveau concours
|
title: Nouveau concours
|
||||||
scoreboard:
|
scoreboard:
|
||||||
all_categories: Toutes les catégories
|
all_categories: Toutes les catégories
|
||||||
auto_refresh: Auto-rafraichissement
|
auto_refresh: Auto-rafraichissement (30s)
|
||||||
hide_offline: Cacher les participant.e.s hors-ligne
|
hide_offline: Cacher les participant.e.s hors-ligne
|
||||||
refresh: Activer le rafraichissement automatique de la page (toutes les 5s)
|
refresh: Activer le rafraichissement automatique de la page (toutes les 5s)
|
||||||
title: "%{name}"
|
title: "%{name}"
|
||||||
@@ -196,6 +198,8 @@ fr:
|
|||||||
offline_form_disabled: Le formulaire hors-ligne n'est pas activé
|
offline_form_disabled: Le formulaire hors-ligne n'est pas activé
|
||||||
public_scoreboard_disabled: Le classement public n'est pas activé
|
public_scoreboard_disabled: Le classement public n'est pas activé
|
||||||
url_copied: L’URL a été copiée dans le presse-papier
|
url_copied: L’URL a été copiée dans le presse-papier
|
||||||
|
stopwatch:
|
||||||
|
info: Ce chronomètre est utilisé pour être affiché dans le classement public, quand autorisé dans les paramètres
|
||||||
contestants:
|
contestants:
|
||||||
convert_csv:
|
convert_csv:
|
||||||
title: Importer des participant.e.s
|
title: Importer des participant.e.s
|
||||||
@@ -258,6 +262,10 @@ fr:
|
|||||||
sign_in: Se connecter
|
sign_in: Se connecter
|
||||||
save: Modifier
|
save: Modifier
|
||||||
start: Clique ici pour démarrer ta participation
|
start: Clique ici pour démarrer ta participation
|
||||||
|
stopwatch_continue: Reprendre
|
||||||
|
stopwatch_pause: Pause
|
||||||
|
stopwatch_reset: Ré-initialiser
|
||||||
|
stopwatch_start: Démarrer
|
||||||
update: Enregistrer les modifications
|
update: Enregistrer les modifications
|
||||||
field: Champ
|
field: Champ
|
||||||
none: Aucun champ sélectionné
|
none: Aucun champ sélectionné
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ Rails.application.routes.draw do
|
|||||||
get "settings/online", to: "contests#settings_online_edit"
|
get "settings/online", to: "contests#settings_online_edit"
|
||||||
patch "settings/online", to: "contests#settings_online_update"
|
patch "settings/online", to: "contests#settings_online_update"
|
||||||
get "settings/categories", to: "contests#settings_categories_edit"
|
get "settings/categories", to: "contests#settings_categories_edit"
|
||||||
|
get "stopwatch", to: "contests#stopwatch"
|
||||||
|
post "stopwatch_continue", to: "contests#stopwatch_continue"
|
||||||
|
post "stopwatch_pause", to: "contests#stopwatch_pause"
|
||||||
|
post "stopwatch_reset", to: "contests#stopwatch_reset"
|
||||||
|
post "stopwatch_start", to: "contests#stopwatch_start"
|
||||||
resources :categories, only: [ :create, :destroy ]
|
resources :categories, only: [ :create, :destroy ]
|
||||||
resources :completions
|
resources :completions
|
||||||
resources :contestants
|
resources :contestants
|
||||||
|
|||||||
5
db/migrate/20251209073711_add_start_time_to_contest.rb
Normal file
5
db/migrate/20251209073711_add_start_time_to_contest.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddStartTimeToContest < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :contests, :start_time, :datetime
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/migrate/20251209075739_add_pause_time_to_contest.rb
Normal file
5
db/migrate/20251209075739_add_pause_time_to_contest.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddPauseTimeToContest < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :contests, :pause_time, :datetime
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddShowStopwatchToContest < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :contests, :show_stopwatch, :boolean
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/schema.rb
generated
5
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_12_04_100550) do
|
ActiveRecord::Schema[8.0].define(version: 2025_12_09_081941) 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
|
||||||
@@ -103,6 +103,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_04_100550) do
|
|||||||
t.string "duration"
|
t.string "duration"
|
||||||
t.integer "duration_seconds"
|
t.integer "duration_seconds"
|
||||||
t.string "code"
|
t.string "code"
|
||||||
|
t.datetime "start_time"
|
||||||
|
t.datetime "pause_time"
|
||||||
|
t.boolean "show_stopwatch"
|
||||||
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
|
||||||
|
|||||||
@@ -10,9 +10,12 @@
|
|||||||
# lang :string default("en")
|
# lang :string default("en")
|
||||||
# name :string
|
# name :string
|
||||||
# offline_form :boolean default(FALSE)
|
# offline_form :boolean default(FALSE)
|
||||||
|
# pause_time :datetime
|
||||||
# public :boolean default(FALSE)
|
# public :boolean default(FALSE)
|
||||||
# ranking_mode :string
|
# ranking_mode :string
|
||||||
|
# show_stopwatch :boolean
|
||||||
# slug :string
|
# slug :string
|
||||||
|
# start_time :datetime
|
||||||
# team :boolean default(FALSE)
|
# team :boolean default(FALSE)
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
|||||||
Reference in New Issue
Block a user