Add contestant categories to contests
All checks were successful
CI / scan_ruby (push) Successful in 1m4s
CI / scan_js (push) Successful in 3m20s
CI / lint (push) Successful in 15s
CI / test (push) Successful in 2m7s

This commit is contained in:
sto 2025-07-14 14:54:55 +02:00
parent ee476ab81b
commit 502649620b
12 changed files with 204 additions and 3 deletions

View File

@ -0,0 +1,37 @@
class CategoriesController < ApplicationController
before_action :set_contest
before_action :set_category, only: %i[ destroy]
def create
authorize @contest
@category = Category.new(category_params)
@category.contest_id = @contest.id
if @category.save
redirect_to edit_contest_path(@contest), notice: t("categories.new.notice")
else
redirect_to edit_contest_path(@contest), notice: t("categories.new.error")
end
end
def destroy
authorize @contest
@category.destroy
redirect_to edit_contest_path(@contest), notice: t("categories.destroy.notice")
end
private
def set_contest
@contest = Contest.find(params[:contest_id])
end
def set_category
@category = Category.find(params[:id])
end
def category_params
params.expect(category: [ :name ])
end
end

24
app/models/category.rb Normal file
View File

@ -0,0 +1,24 @@
# == Schema Information
#
# Table name: categories
#
# id :integer not null, primary key
# name :string
# created_at :datetime not null
# updated_at :datetime not null
# contest_id :integer not null
#
# Indexes
#
# index_categories_on_contest_id (contest_id)
#
# Foreign Keys
#
# contest_id (contest_id => contests.id)
#
class Category < ApplicationRecord
belongs_to :contest
has_and_belongs_to_many :contestants
validates :name, presence: true
end

View File

@ -26,6 +26,7 @@ class Contest < ApplicationRecord
extend FriendlyId
belongs_to :user
has_many :categories
has_many :completions, dependent: :destroy
has_many :contestants, dependent: :destroy
has_many :puzzles, dependent: :destroy

View File

@ -22,6 +22,7 @@
class Contestant < ApplicationRecord
belongs_to :contest
has_many :completions, dependent: :destroy
has_and_belongs_to_many :categories
before_validation :initialize_time_seconds_if_empty

View File

@ -1,3 +1,4 @@
h4.mt-5 = t("contests.form.general")
= form_with model: contest do |form|
.row.mb-3
.col
@ -20,7 +21,7 @@
= form.check_box :team, class: "form-check-input"
= form.label :team
.form-text = t("activerecord.attributes.contest.team_description")
.row.mb-3
.row.mb-3 style="display: none"
.col
.form-check.form-switch
= form.check_box :allow_registration, class: "form-check-input"
@ -28,4 +29,37 @@
.form-text = t("activerecord.attributes.contest.allow_registration_description")
.row
.col
= form.submit submit_text, class: "btn btn-primary"
= form.submit submit_text, class: "btn btn-primary"
h4.mt-5 = t("contests.form.categories")
= form_with model: Category, url: "/contests/#{@contest.id}/categories" do |form|
- if @contest.categories.size > 0
.row
.col-6
table.table.table-striped.table-hover
thead
tr
th
= t("activerecord.attributes.category.name")
th
= t("activerecord.attributes.category.contestant_count")
tbody
- @contest.categories.each do |category|
tr.align-middle scope="row"
td
= category.name
td
= category.contestants.size
td
= link_to t("helpers.buttons.delete"), contest_category_path(@contest, category), data: { turbo_method: :delete }, class: "btn btn-sm btn-danger ms-2"
.row.mt-3
.col-4
.form-floating
= form.text_field :name, autocomplete: "off", value: nil, class: "form-control"
= form.label :name, class: "required"
= t("activerecord.attributes.category.new")
.row.mt-3
.col
= form.submit t("helpers.buttons.add"), class: "btn btn-primary"

View File

@ -38,6 +38,10 @@ en:
greater_than: "Participant names are required"
activerecord:
attributes:
category:
contestant_count: Contestants count
new: New category
name: Category
completion:
contestant: Participant
display_time: Time
@ -116,6 +120,12 @@ en:
blank: Your email cannot be empty
username:
blank: Your username cannot be empty
categories:
destroy:
notice: Category deleted
new:
error: The category name can't be empty
notice: Category added
completions:
destroy:
notice: Completion deleted
@ -132,6 +142,9 @@ en:
edit:
notice: Contest updated
title: Edit contest settings
form:
categories: Participant categories
general: General parameters
index:
title: Welcome %{username}!
manage_contests: Manage my contests

View File

@ -9,6 +9,10 @@ fr:
greater_than: "Choisir une colonne pour les noms des participant.e.s est nécessaire"
activerecord:
attributes:
category:
contestant_count: Nombre de participant.e.s
new: Nouvelle catégorie
name: Catégorie
completion:
contestant_id: Participant.e
display_time: Temps
@ -87,6 +91,12 @@ fr:
blank: L'email est obligatoire
username:
blank: Le nom d'utilisateur.ice est obligatoire
categories:
destroy:
notice: Catégorie supprimée
new:
error: Le nom de la catégorie ne peut pas être vide
notice: Catégorie ajoutée
completions:
destroy:
notice: Complétion supprimée
@ -103,6 +113,9 @@ fr:
edit:
notice: Concours modifié
title: Paramètres du concours
form:
categories: Catégories de participant.e.s
general: Paramètres généraux
index:
title: Bienvenue %{username} !
manage_contests: Mes concours de puzzle

View File

@ -9,6 +9,7 @@ Rails.application.routes.draw do
root "contests#index"
resources :contests do
resources :categories, only: [ :create, :destroy ]
resources :completions
resources :contestants
resources :puzzles

View File

@ -0,0 +1,15 @@
class CreateCategories < ActiveRecord::Migration[8.0]
def change
create_table :categories do |t|
t.string :name
t.belongs_to :contest, null: false, foreign_key: true
t.timestamps
end
create_join_table :categories, :contestants do |t|
t.index :category_id
t.index :contestant_id
end
end
end

18
db/schema.rb generated
View File

@ -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_27_070407) do
ActiveRecord::Schema[8.0].define(version: 2025_07_14_115208) do
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
@ -39,6 +39,21 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_27_070407) do
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
create_table "categories", force: :cascade do |t|
t.string "name"
t.integer "contest_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["contest_id"], name: "index_categories_on_contest_id"
end
create_table "categories_contestants", id: false, force: :cascade do |t|
t.integer "category_id", null: false
t.integer "contestant_id", null: false
t.index ["category_id"], name: "index_categories_contestants_on_category_id"
t.index ["contestant_id"], name: "index_categories_contestants_on_contestant_id"
end
create_table "completions", force: :cascade do |t|
t.integer "time_seconds"
t.integer "contestant_id", null: false
@ -141,6 +156,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_27_070407) do
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "categories", "contests"
add_foreign_key "completions", "contestants"
add_foreign_key "completions", "contests"
add_foreign_key "completions", "messages"

View File

@ -0,0 +1,23 @@
# == Schema Information
#
# Table name: categories
#
# id :integer not null, primary key
# name :string
# created_at :datetime not null
# updated_at :datetime not null
# contest_id :integer not null
#
# Indexes
#
# index_categories_on_contest_id (contest_id)
#
# Foreign Keys
#
# contest_id (contest_id => contests.id)
#
FactoryBot.define do
factory :category do
name { "MyString" }
end
end

View File

@ -0,0 +1,23 @@
# == Schema Information
#
# Table name: categories
#
# id :integer not null, primary key
# name :string
# created_at :datetime not null
# updated_at :datetime not null
# contest_id :integer not null
#
# Indexes
#
# index_categories_on_contest_id (contest_id)
#
# Foreign Keys
#
# contest_id (contest_id => contests.id)
#
require 'rails_helper'
RSpec.describe Category, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end