Turn contest dashboard into tabs for easier navigation
This commit is contained in:
@@ -42,7 +42,7 @@ class CompletionsController < ApplicationController
|
|||||||
if @contestant && !params[:completion].key?(:message_id)
|
if @contestant && !params[:completion].key?(:message_id)
|
||||||
redirect_to edit_contest_contestant_path(@contest, @contestant), notice: t("completions.new.notice")
|
redirect_to edit_contest_contestant_path(@contest, @contestant), notice: t("completions.new.notice")
|
||||||
else
|
else
|
||||||
redirect_to @contest, notice: t("completions.new.notice")
|
redirect_to contest_messages_path(@contest), notice: t("completions.new.notice")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if params[:completion].key?(:message_id)
|
if params[:completion].key?(:message_id)
|
||||||
|
|||||||
@@ -3,6 +3,19 @@ class ContestantsController < ApplicationController
|
|||||||
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 ]
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize @contest
|
||||||
|
|
||||||
|
@title = @contest.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
|
||||||
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@@ -158,4 +171,14 @@ class ContestantsController < ApplicationController
|
|||||||
end
|
end
|
||||||
@contestant.save
|
@contestant.save
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_contestants_per_category
|
||||||
|
if params.key?(:category) && params[:category] != "-1"
|
||||||
|
if params[:category] == "-2"
|
||||||
|
@contestants = @contestants.select { |contestant| contestant.categories.size == 0 }
|
||||||
|
else
|
||||||
|
@contestants = @contestants.select { |contestant| contestant.categories.where(id: params[:category]).any? }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -16,19 +16,7 @@ class ContestsController < ApplicationController
|
|||||||
def show
|
def show
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@title = I18n.t("contests.show.title", name: @contest.name)
|
redirect_to contest_contestants_path(@contest)
|
||||||
@action_name = t("helpers.buttons.settings")
|
|
||||||
@action_path = "/contests/#{@contest.id}/settings/general"
|
|
||||||
@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
|
|
||||||
@puzzles = @contest.puzzles.order(:id)
|
|
||||||
@messages = @contest.messages.order(:time_seconds)
|
|
||||||
set_badges
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
@@ -240,12 +228,6 @@ class ContestsController < ApplicationController
|
|||||||
@title = I18n.t("contests.scoreboard.title", name: @contest.name)
|
@title = I18n.t("contests.scoreboard.title", name: @contest.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_badges
|
|
||||||
@badges = []
|
|
||||||
@badges.push(t("helpers.badges.team")) if @contest.team
|
|
||||||
@badges.push(t("helpers.badges.registration")) if @contest.allow_registration
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_contest
|
def set_contest
|
||||||
@contest = Contest.find(params[:id])
|
@contest = Contest.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class MessagesController < ApplicationController
|
|||||||
skip_before_action :require_authentication, only: %i[ create connect cors_preflight_check ]
|
skip_before_action :require_authentication, only: %i[ create connect cors_preflight_check ]
|
||||||
|
|
||||||
before_action :cors_set_access_control_headers, only: %i[ create connect cors_preflight_check ]
|
before_action :cors_set_access_control_headers, only: %i[ create connect cors_preflight_check ]
|
||||||
before_action :set_contest, only: %i[ convert destroy ]
|
before_action :set_contest, only: %i[ convert destroy index ]
|
||||||
before_action :set_data, only: %i[ convert ]
|
before_action :set_data, only: %i[ convert ]
|
||||||
|
|
||||||
def self.local_prefixes
|
def self.local_prefixes
|
||||||
@@ -68,6 +68,14 @@ class MessagesController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize @contest
|
||||||
|
|
||||||
|
@title = @contest.name
|
||||||
|
@messages = @contest.messages.order(:time_seconds)
|
||||||
|
@puzzles = @contest.puzzles
|
||||||
|
end
|
||||||
|
|
||||||
def convert
|
def convert
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
@@ -76,6 +84,7 @@ class MessagesController < ApplicationController
|
|||||||
|
|
||||||
@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
|
||||||
|
|
||||||
render "completions/new"
|
render "completions/new"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,6 +2,13 @@ class PuzzlesController < ApplicationController
|
|||||||
before_action :set_contest
|
before_action :set_contest
|
||||||
before_action :set_puzzle, only: %i[ destroy edit update]
|
before_action :set_puzzle, only: %i[ destroy edit update]
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize @contest
|
||||||
|
|
||||||
|
@title = @contest.name
|
||||||
|
@puzzles = @contest.puzzles.order(:id)
|
||||||
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
authorize @contest
|
authorize @contest
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,2 @@
|
|||||||
class CompletionPolicy < ContestPolicy
|
class CompletionPolicy < ContestPolicy
|
||||||
def index?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def show?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,30 +1,38 @@
|
|||||||
class ContestPolicy < ApplicationPolicy
|
class ContestPolicy < ApplicationPolicy
|
||||||
|
def owner_or_admin
|
||||||
|
if record == :contest
|
||||||
|
true
|
||||||
|
else
|
||||||
|
record.user.id == user.id || user.admin?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def index?
|
def index?
|
||||||
true
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def show?
|
def show?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def new?
|
def new?
|
||||||
true
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def create?
|
def create?
|
||||||
true
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert?
|
def convert?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_csv?
|
def convert_csv?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit?
|
def edit?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def settings_general_edit?
|
def settings_general_edit?
|
||||||
@@ -48,23 +56,27 @@ class ContestPolicy < ApplicationPolicy
|
|||||||
end
|
end
|
||||||
|
|
||||||
def finalize_import?
|
def finalize_import?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def update?
|
def update?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy?
|
def destroy?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def import?
|
def import?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def export?
|
def export?
|
||||||
record.user.id == user.id || user.admin?
|
owner_or_admin
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload_csv?
|
||||||
|
owner_or_admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def offline?
|
def offline?
|
||||||
@@ -94,8 +106,4 @@ class ContestPolicy < ApplicationPolicy
|
|||||||
def scoreboard?
|
def scoreboard?
|
||||||
record.public
|
record.public
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload_csv?
|
|
||||||
record.user.id == user.id || user.admin?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +1,2 @@
|
|||||||
class ContestantPolicy < ContestPolicy
|
class ContestantPolicy < ContestPolicy
|
||||||
def index?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def show?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +1,2 @@
|
|||||||
class PuzzlePolicy < ContestPolicy
|
class PuzzlePolicy < ContestPolicy
|
||||||
def index?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def show?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
44
app/views/application/_contest_nav.html.slim
Normal file
44
app/views/application/_contest_nav.html.slim
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
javascript:
|
||||||
|
async function copyExtensionUrlToClipboard() {
|
||||||
|
await navigator.clipboard.writeText("#{message_url}?token=#{@contest.generate_token_for(:token)}");
|
||||||
|
alert("#{t("contests.show.url_copied")}");
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.mb-4
|
||||||
|
.col
|
||||||
|
- if @contest.public
|
||||||
|
a.btn.btn-success href="/public/#{@contest.slug}"
|
||||||
|
= t("contests.show.open_public_scoreboard")
|
||||||
|
- else
|
||||||
|
a.btn.btn-success.disabled
|
||||||
|
= t("contests.show.public_scoreboard_disabled")
|
||||||
|
- if @contest.offline_form && @contest.puzzles.length < 2
|
||||||
|
a.ms-3.btn.btn-success href="/public/#{@contest.slug}/offline"
|
||||||
|
= t("contests.show.open_offline_form")
|
||||||
|
- else
|
||||||
|
a.ms-3.btn.btn-success.disabled
|
||||||
|
= t("contests.show.offline_form_disabled")
|
||||||
|
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
|
||||||
|
.col
|
||||||
|
ul.nav.nav-tabs.mb-4
|
||||||
|
li.nav-item
|
||||||
|
a.nav-link class=active_page(contest_contestants_path(@contest)) href=contest_contestants_path(@contest)
|
||||||
|
= t("contestants.plural").capitalize
|
||||||
|
li.nav-item
|
||||||
|
a.nav-link class=active_page(contest_puzzles_path(@contest)) href=contest_puzzles_path(@contest)
|
||||||
|
= t("puzzles.plural").capitalize
|
||||||
|
li.nav-item
|
||||||
|
a.nav-link class=active_page(contest_messages_path(@contest)) href=contest_messages_path(@contest)
|
||||||
|
= t("messages.plural").capitalize
|
||||||
64
app/views/contestants/index.html.slim
Normal file
64
app/views/contestants/index.html.slim
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
= render "contest_nav"
|
||||||
|
|
||||||
|
.row.mb-4 style="height: calc(100vh - 280px)"
|
||||||
|
.col.d-flex.flex-column style="height: 100%"
|
||||||
|
.row.mb-4
|
||||||
|
.col
|
||||||
|
a.btn.btn-primary href=new_contest_contestant_path(@contest) style="margin-top: -3px"
|
||||||
|
| + #{t("helpers.buttons.add")}
|
||||||
|
a.ms-2.btn.btn.btn-primary href=contest_import_path(@contest) style="margin-top: -3px"
|
||||||
|
| #{t("helpers.buttons.import")}
|
||||||
|
a.ms-2.btn.btn.btn-primary href="/contests/#{@contest.id}/export.csv" style="margin-top: -3px"
|
||||||
|
| #{t("helpers.buttons.export")}
|
||||||
|
|
||||||
|
- if @contest.categories.size > 0
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
select.mt-2.mb-2 id="categories" style="padding: 5px"
|
||||||
|
option value=-1
|
||||||
|
| Tous.tes les participant.e.s
|
||||||
|
option value=-2
|
||||||
|
| Participant.e.s sans catégorie
|
||||||
|
- @contest.categories.each do |category|
|
||||||
|
option value=category.id
|
||||||
|
= category.name
|
||||||
|
javascript:
|
||||||
|
categorySelectEl = document.getElementById('categories');
|
||||||
|
urlParams = new URLSearchParams(window.location.search);
|
||||||
|
selectedCategory = urlParams.get('category');
|
||||||
|
Array.from(categorySelectEl.children).forEach((option) => {
|
||||||
|
if (option.value == selectedCategory) option.selected = true;
|
||||||
|
});
|
||||||
|
categorySelectEl.addEventListener('change', (e) => {
|
||||||
|
window.location.replace(`#{contest_path(@contest)}?category=${e.target.value}`);
|
||||||
|
})
|
||||||
|
.d-flex.flex-column style="overflow-y: auto"
|
||||||
|
table.table.table-striped.table-hover
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th
|
||||||
|
= t("activerecord.attributes.contestant.name")
|
||||||
|
th
|
||||||
|
= t("activerecord.attributes.contestant.offline")
|
||||||
|
th
|
||||||
|
= t("activerecord.attributes.contestant.completions")
|
||||||
|
th
|
||||||
|
= t("activerecord.attributes.contestant.display_time")
|
||||||
|
tbody
|
||||||
|
- @contestants.each_with_index do |contestant, index|
|
||||||
|
tr scope="row"
|
||||||
|
td
|
||||||
|
= contestant.name
|
||||||
|
td
|
||||||
|
- if contestant.offline.present?
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-square" viewBox="0 0 16 16">
|
||||||
|
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
||||||
|
<path d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z"/>
|
||||||
|
</svg>
|
||||||
|
td
|
||||||
|
= contestant.completions.where(remaining_pieces: nil).length
|
||||||
|
td
|
||||||
|
= contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? "#{contestant.completions.map{|completion| completion.puzzle.pieces}.sum - contestant.completions[-1].remaining_pieces}p" : contestant.display_time
|
||||||
|
td
|
||||||
|
a.btn.btn-sm.btn-secondary href=edit_contest_contestant_path(@contest, contestant)
|
||||||
|
= t("helpers.buttons.open")
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
- if @badges.size > 0 && false
|
|
||||||
.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)}");
|
|
||||||
alert("#{t("contests.show.url_copied")}");
|
|
||||||
}
|
|
||||||
|
|
||||||
.row.mb-5
|
|
||||||
.col
|
|
||||||
- if @contest.public
|
|
||||||
a.btn.btn-success href="/public/#{@contest.slug}"
|
|
||||||
= t("contests.show.open_public_scoreboard")
|
|
||||||
- else
|
|
||||||
a.btn.btn-success.disabled
|
|
||||||
= t("contests.show.public_scoreboard_disabled")
|
|
||||||
- if @contest.offline_form && @contest.puzzles.length < 2
|
|
||||||
a.ms-3.btn.btn-success href="/public/#{@contest.slug}/offline"
|
|
||||||
= t("contests.show.open_offline_form")
|
|
||||||
- else
|
|
||||||
a.ms-3.btn.btn-success.disabled
|
|
||||||
= t("contests.show.offline_form_disabled")
|
|
||||||
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 style="height: calc(100vh - 280px)"
|
|
||||||
.col-6.d-flex.flex-column style="height: 100%"
|
|
||||||
.row
|
|
||||||
.col
|
|
||||||
h4
|
|
||||||
= t("contestants.plural").capitalize
|
|
||||||
a.ms-3.btn.btn-sm.btn-primary href=new_contest_contestant_path(@contest) style="margin-top: -3px"
|
|
||||||
| + #{t("helpers.buttons.add")}
|
|
||||||
a.ms-2.btn.btn-sm.btn.btn-primary href=contest_import_path(@contest) style="margin-top: -3px"
|
|
||||||
| #{t("helpers.buttons.import")}
|
|
||||||
a.ms-2.btn.btn-sm.btn.btn-primary href="/contests/#{@contest.id}/export.csv" style="margin-top: -3px"
|
|
||||||
| #{t("helpers.buttons.export")}
|
|
||||||
- if @contest.categories.size > 0
|
|
||||||
.row
|
|
||||||
.col
|
|
||||||
select.mt-2.mb-2 id="categories" style="padding: 5px"
|
|
||||||
option value=-1
|
|
||||||
| Tous.tes les participant.e.s
|
|
||||||
option value=-2
|
|
||||||
| Participant.e.s sans catégorie
|
|
||||||
- @contest.categories.each do |category|
|
|
||||||
option value=category.id
|
|
||||||
= category.name
|
|
||||||
javascript:
|
|
||||||
categorySelectEl = document.getElementById('categories');
|
|
||||||
urlParams = new URLSearchParams(window.location.search);
|
|
||||||
selectedCategory = urlParams.get('category');
|
|
||||||
Array.from(categorySelectEl.children).forEach((option) => {
|
|
||||||
if (option.value == selectedCategory) option.selected = true;
|
|
||||||
});
|
|
||||||
categorySelectEl.addEventListener('change', (e) => {
|
|
||||||
window.location.replace(`#{contest_path(@contest)}?category=${e.target.value}`);
|
|
||||||
})
|
|
||||||
.d-flex.flex-column style="overflow-y: auto"
|
|
||||||
table.table.table-striped.table-hover
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th
|
|
||||||
= t("activerecord.attributes.contestant.name")
|
|
||||||
th
|
|
||||||
= t("activerecord.attributes.contestant.offline")
|
|
||||||
th
|
|
||||||
= t("activerecord.attributes.contestant.completions")
|
|
||||||
th
|
|
||||||
= t("activerecord.attributes.contestant.display_time")
|
|
||||||
tbody
|
|
||||||
- @contestants.each_with_index do |contestant, index|
|
|
||||||
tr scope="row"
|
|
||||||
td
|
|
||||||
= contestant.name
|
|
||||||
td
|
|
||||||
- if contestant.offline.present?
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-square" viewBox="0 0 16 16">
|
|
||||||
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
|
||||||
<path d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z"/>
|
|
||||||
</svg>
|
|
||||||
td
|
|
||||||
= contestant.completions.where(remaining_pieces: nil).length
|
|
||||||
td
|
|
||||||
= contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? "#{contestant.completions.map{|completion| completion.puzzle.pieces}.sum - contestant.completions[-1].remaining_pieces}p" : contestant.display_time
|
|
||||||
td
|
|
||||||
a.btn.btn-sm.btn-secondary href=edit_contest_contestant_path(@contest, contestant)
|
|
||||||
= t("helpers.buttons.open")
|
|
||||||
.col-6.d-flex.flex-column style="height: 100%"
|
|
||||||
.row
|
|
||||||
.col
|
|
||||||
h4
|
|
||||||
= t("puzzles.plural").capitalize
|
|
||||||
a.ms-3.btn.btn-sm.btn-primary href=new_contest_puzzle_path(@contest) style="margin-top: -3px"
|
|
||||||
| + #{t("helpers.buttons.add")}
|
|
||||||
table.table.table-striped.table-hover
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th
|
|
||||||
= t("activerecord.attributes.puzzle.image")
|
|
||||||
th
|
|
||||||
= t("activerecord.attributes.puzzle.name")
|
|
||||||
th
|
|
||||||
= t("activerecord.attributes.puzzle.brand")
|
|
||||||
th
|
|
||||||
= t("activerecord.attributes.puzzle.pieces")
|
|
||||||
tbody
|
|
||||||
- @puzzles.each do |puzzle|
|
|
||||||
tr.align-middle scope="row"
|
|
||||||
td
|
|
||||||
= image_tag(puzzle.image, class: "img-fluid", style: "max-height: 48px;") if puzzle.image.attached?
|
|
||||||
td
|
|
||||||
= puzzle.name
|
|
||||||
td
|
|
||||||
= puzzle.brand
|
|
||||||
td
|
|
||||||
= puzzle.pieces
|
|
||||||
td
|
|
||||||
a.btn.btn-sm.btn-secondary href=edit_contest_puzzle_path(@contest, puzzle)
|
|
||||||
= t("helpers.buttons.edit")
|
|
||||||
- if @messages
|
|
||||||
.row.mt-5
|
|
||||||
.col
|
|
||||||
h4 = t("messages.plural").capitalize
|
|
||||||
- if @puzzles.size == 0
|
|
||||||
.row
|
|
||||||
.col.alert.alert-danger
|
|
||||||
= t("messages.warning")
|
|
||||||
.d-flex.flex-column style="overflow-y: auto"
|
|
||||||
table.table.table-striped.table-hover
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th scope="col" style="white-space: nowrap"
|
|
||||||
= t("activerecord.attributes.message.processed")
|
|
||||||
th scope="col"
|
|
||||||
= t("activerecord.attributes.message.time")
|
|
||||||
th scope="col"
|
|
||||||
= t("activerecord.attributes.message.author")
|
|
||||||
th.w-25 scope="col"
|
|
||||||
= t("activerecord.attributes.message.text")
|
|
||||||
th.w-25 scope="col"
|
|
||||||
tbody
|
|
||||||
- @messages.each do |message|
|
|
||||||
tr.align-middle scope="row"
|
|
||||||
td style="text-align: center"
|
|
||||||
- if message.completions.size > 0
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-square" viewBox="0 0 16 16">
|
|
||||||
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
|
||||||
<path d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z"/>
|
|
||||||
</svg>
|
|
||||||
td
|
|
||||||
= message.display_time
|
|
||||||
td
|
|
||||||
= message.author
|
|
||||||
td
|
|
||||||
= message.text
|
|
||||||
td
|
|
||||||
.d-inline-flex
|
|
||||||
- if @puzzles.size > 0
|
|
||||||
a.btn.btn-sm.btn-secondary href=contest_message_convert_path(@contest, message) style="white-space: nowrap;"
|
|
||||||
= t("helpers.buttons.add_completion")
|
|
||||||
- else
|
|
||||||
a.btn.btn-sm.btn-secondary.disabled href=contest_message_convert_path(@contest, message) style="white-space: nowrap;"
|
|
||||||
= t("helpers.buttons.add_completion")
|
|
||||||
= link_to "x", contest_message_path(@contest, message), data: { turbo_method: :delete }, class: "btn btn-sm btn-danger ms-2"
|
|
||||||
52
app/views/messages/index.html.slim
Normal file
52
app/views/messages/index.html.slim
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
= render "contest_nav"
|
||||||
|
|
||||||
|
.row.mb-4 style="height: calc(100vh - 280px)"
|
||||||
|
.col.d-flex.flex-column style="height: 100%"
|
||||||
|
|
||||||
|
.row.mb-4
|
||||||
|
.col
|
||||||
|
- if @messages.length == 0
|
||||||
|
.alert.alert-warning
|
||||||
|
= t("messages.index.no_messages")
|
||||||
|
- else
|
||||||
|
- if @puzzles.size == 0
|
||||||
|
.row
|
||||||
|
.col.alert.alert-danger
|
||||||
|
= t("messages.warning")
|
||||||
|
.d-flex.flex-column style="overflow-y: auto"
|
||||||
|
table.table.table-striped.table-hover
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th scope="col" style="white-space: nowrap"
|
||||||
|
= t("activerecord.attributes.message.processed")
|
||||||
|
th scope="col"
|
||||||
|
= t("activerecord.attributes.message.time")
|
||||||
|
th scope="col"
|
||||||
|
= t("activerecord.attributes.message.author")
|
||||||
|
th.w-25 scope="col"
|
||||||
|
= t("activerecord.attributes.message.text")
|
||||||
|
th.w-25 scope="col"
|
||||||
|
tbody
|
||||||
|
- @messages.each do |message|
|
||||||
|
tr.align-middle scope="row"
|
||||||
|
td style="text-align: center"
|
||||||
|
- if message.completions.size > 0
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-square" viewBox="0 0 16 16">
|
||||||
|
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
||||||
|
<path d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z"/>
|
||||||
|
</svg>
|
||||||
|
td
|
||||||
|
= message.display_time
|
||||||
|
td
|
||||||
|
= message.author
|
||||||
|
td
|
||||||
|
= message.text
|
||||||
|
td
|
||||||
|
.d-inline-flex
|
||||||
|
- if @puzzles.size > 0
|
||||||
|
a.btn.btn-sm.btn-secondary href=contest_message_convert_path(@contest, message) style="white-space: nowrap;"
|
||||||
|
= t("helpers.buttons.add_completion")
|
||||||
|
- else
|
||||||
|
a.btn.btn-sm.btn-secondary.disabled href=contest_message_convert_path(@contest, message) style="white-space: nowrap;"
|
||||||
|
= t("helpers.buttons.add_completion")
|
||||||
|
= link_to "x", contest_message_path(@contest, message), data: { turbo_method: :delete }, class: "btn btn-sm btn-danger ms-2"
|
||||||
34
app/views/puzzles/index.html.slim
Normal file
34
app/views/puzzles/index.html.slim
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
= render "contest_nav"
|
||||||
|
|
||||||
|
.row.mb-4 style="height: calc(100vh - 280px)"
|
||||||
|
.col.d-flex.flex-column style="height: 100%"
|
||||||
|
|
||||||
|
.row.mb-4
|
||||||
|
.col
|
||||||
|
a.btn.btn-primary href=new_contest_puzzle_path(@contest) style="margin-top: -3px"
|
||||||
|
| + #{t("helpers.buttons.add")}
|
||||||
|
table.table.table-striped.table-hover
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th
|
||||||
|
= t("activerecord.attributes.puzzle.image")
|
||||||
|
th
|
||||||
|
= t("activerecord.attributes.puzzle.name")
|
||||||
|
th
|
||||||
|
= t("activerecord.attributes.puzzle.brand")
|
||||||
|
th
|
||||||
|
= t("activerecord.attributes.puzzle.pieces")
|
||||||
|
tbody
|
||||||
|
- @puzzles.each do |puzzle|
|
||||||
|
tr.align-middle scope="row"
|
||||||
|
td
|
||||||
|
= image_tag(puzzle.image, class: "img-fluid", style: "max-height: 128px;") if puzzle.image.attached?
|
||||||
|
td
|
||||||
|
= puzzle.name
|
||||||
|
td
|
||||||
|
= puzzle.brand
|
||||||
|
td
|
||||||
|
= puzzle.pieces
|
||||||
|
td
|
||||||
|
a.btn.btn-sm.btn-secondary href=edit_contest_puzzle_path(@contest, puzzle)
|
||||||
|
= t("helpers.buttons.edit")
|
||||||
@@ -260,6 +260,8 @@ en:
|
|||||||
none: No field selected
|
none: No field selected
|
||||||
rank: Rank
|
rank: Rank
|
||||||
messages:
|
messages:
|
||||||
|
index:
|
||||||
|
no_messages: No messages received yet
|
||||||
convert:
|
convert:
|
||||||
title: New completion
|
title: New completion
|
||||||
destroy:
|
destroy:
|
||||||
|
|||||||
@@ -231,6 +231,8 @@ fr:
|
|||||||
none: Aucun champ sélectionné
|
none: Aucun champ sélectionné
|
||||||
rank: Rang
|
rank: Rang
|
||||||
messages:
|
messages:
|
||||||
|
index:
|
||||||
|
no_messages: Pas de messages reçus pour le moment
|
||||||
convert:
|
convert:
|
||||||
title: Ajout d'une complétion
|
title: Ajout d'une complétion
|
||||||
destroy:
|
destroy:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Rails.application.routes.draw do
|
|||||||
resources :completions
|
resources :completions
|
||||||
resources :contestants
|
resources :contestants
|
||||||
resources :puzzles
|
resources :puzzles
|
||||||
resources :messages, only: :destroy do
|
resources :messages, only: [ :destroy, :index ] do
|
||||||
get "convert", to: "messages#convert"
|
get "convert", to: "messages#convert"
|
||||||
end
|
end
|
||||||
get "import", to: "contestants#import"
|
get "import", to: "contestants#import"
|
||||||
|
|||||||
Reference in New Issue
Block a user