Events: initial implementation
This commit is contained in:
56
app/controllers/events_controller.rb
Normal file
56
app/controllers/events_controller.rb
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
class EventsController < ApplicationController
|
||||||
|
before_action :set_event, only: %i[ destroy show ]
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize :event
|
||||||
|
|
||||||
|
@events = current_user.events.order(created_at: :desc)
|
||||||
|
@contests_without_event = current_user.contests.where(event: nil).size
|
||||||
|
@title = I18n.t("events.index.title", username: current_user.username)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
authorize :event
|
||||||
|
|
||||||
|
@event = Event.new
|
||||||
|
@title = I18n.t("events.new.title")
|
||||||
|
@nonav = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize :event
|
||||||
|
|
||||||
|
@event = Event.new(new_event_params)
|
||||||
|
@event.lang = @current_user.lang
|
||||||
|
@event.user_id = current_user.id
|
||||||
|
if @event.save
|
||||||
|
redirect_to "/events/#{@event.id}", notice: t("events.new.notice")
|
||||||
|
else
|
||||||
|
@title = I18n.t("events.new.title")
|
||||||
|
@nonav = true
|
||||||
|
render :new, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
authorize @event
|
||||||
|
|
||||||
|
@contests = @event.contests
|
||||||
|
@title = @event.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @event
|
||||||
|
|
||||||
|
@event.destroy
|
||||||
|
redirect_to events_path, notice: t("events.destroy.notice")
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_event
|
||||||
|
@event = Event.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_event_params
|
||||||
|
params.expect(event: [ :name, :shared_scoreboard ])
|
||||||
|
end
|
||||||
|
end
|
||||||
2
app/helpers/events_helper.rb
Normal file
2
app/helpers/events_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module EventsHelper
|
||||||
|
end
|
||||||
@@ -20,21 +20,25 @@
|
|||||||
# 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
|
||||||
|
# event_id :integer
|
||||||
# user_id :integer not null
|
# user_id :integer not null
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
# index_contests_on_event_id (event_id)
|
||||||
# index_contests_on_slug (slug) UNIQUE
|
# index_contests_on_slug (slug) UNIQUE
|
||||||
# index_contests_on_user_id (user_id)
|
# index_contests_on_user_id (user_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
|
# event_id (event_id => events.id)
|
||||||
# user_id (user_id => users.id)
|
# user_id (user_id => users.id)
|
||||||
#
|
#
|
||||||
class Contest < ApplicationRecord
|
class Contest < ApplicationRecord
|
||||||
extend FriendlyId
|
extend FriendlyId
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
belongs_to :event, optional: true
|
||||||
has_many :categories
|
has_many :categories
|
||||||
has_many :completions, dependent: :destroy
|
has_many :completions, dependent: :destroy
|
||||||
has_many :contestants, dependent: :destroy
|
has_many :contestants, dependent: :destroy
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
# display_time :string
|
# display_time :string
|
||||||
# email :string
|
# email :string
|
||||||
# name :string
|
# name :string
|
||||||
# projected_time :string
|
|
||||||
# qrcode :string
|
# qrcode :string
|
||||||
# time_seconds :integer
|
# time_seconds :integer
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
|
|||||||
26
app/models/event.rb
Normal file
26
app/models/event.rb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: events
|
||||||
|
#
|
||||||
|
# id :integer not null, primary key
|
||||||
|
# lang :string default("en")
|
||||||
|
# name :string
|
||||||
|
# shared_scoreboard :boolean
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# user_id :integer not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_events_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# user_id (user_id => users.id)
|
||||||
|
#
|
||||||
|
class Event < ApplicationRecord
|
||||||
|
belongs_to :user
|
||||||
|
has_many :contests, dependent: :destroy
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
|
end
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
# index_users_on_email_address (email_address) UNIQUE
|
# index_users_on_email_address (email_address) UNIQUE
|
||||||
#
|
#
|
||||||
class User < ApplicationRecord
|
class User < ApplicationRecord
|
||||||
|
has_many :events, dependent: :destroy
|
||||||
has_many :contests, dependent: :destroy
|
has_many :contests, dependent: :destroy
|
||||||
has_many :sessions, dependent: :destroy
|
has_many :sessions, dependent: :destroy
|
||||||
has_secure_password
|
has_secure_password
|
||||||
|
|||||||
25
app/policies/event_policy.rb
Normal file
25
app/policies/event_policy.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
class EventPolicy < ApplicationPolicy
|
||||||
|
def owner_or_admin
|
||||||
|
if record == :event
|
||||||
|
true
|
||||||
|
else
|
||||||
|
record.user.id == user.id || user.admin?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def index?
|
||||||
|
owner_or_admin
|
||||||
|
end
|
||||||
|
|
||||||
|
def new?
|
||||||
|
owner_or_admin
|
||||||
|
end
|
||||||
|
|
||||||
|
def create?
|
||||||
|
owner_or_admin
|
||||||
|
end
|
||||||
|
|
||||||
|
def show?
|
||||||
|
owner_or_admin
|
||||||
|
end
|
||||||
|
end
|
||||||
22
app/views/events/_form.html.slim
Normal file
22
app/views/events/_form.html.slim
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
= form_with model: event, url: url, method: method do |form|
|
||||||
|
.row.mb-3
|
||||||
|
.col
|
||||||
|
.form-floating
|
||||||
|
= form.text_field :name, autocomplete: "off", class: "form-control"
|
||||||
|
= form.label :name, class: "required"
|
||||||
|
|
||||||
|
.row.mb-3
|
||||||
|
.col
|
||||||
|
.form-check.form-switch
|
||||||
|
= form.check_box :shared_scoreboard, class: "form-check-input"
|
||||||
|
= form.label :shared_scoreboard
|
||||||
|
.form-text = t("activerecord.attributes.event.shared_scoreboard_description")
|
||||||
|
|
||||||
|
.alert.alert-primary role="alert"
|
||||||
|
= t("events.form.shared_scoreboard_message")
|
||||||
|
|
||||||
|
.row.mt-4
|
||||||
|
.col
|
||||||
|
- if method == :patch
|
||||||
|
= link_to t("helpers.buttons.delete"), event_path(event), data: { turbo_method: :delete }, class: "btn btn-danger me-2"
|
||||||
|
= form.submit submit_text, class: "btn btn-primary"
|
||||||
36
app/views/events/index.html.slim
Normal file
36
app/views/events/index.html.slim
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.row
|
||||||
|
.col
|
||||||
|
h4.mb-3
|
||||||
|
= t("events.index.subtitle")
|
||||||
|
.float-end
|
||||||
|
a.btn.btn-primary.mb-4 href=new_event_path
|
||||||
|
= t("events.index.new")
|
||||||
|
|
||||||
|
.alert.alert-primary role="alert"
|
||||||
|
= t("events.index.message")
|
||||||
|
|
||||||
|
- @events.each do |event|
|
||||||
|
.row.mt-3
|
||||||
|
.col
|
||||||
|
css:
|
||||||
|
.card:hover { background-color: lightblue; }
|
||||||
|
.card.h-100
|
||||||
|
.card-header
|
||||||
|
= event.name
|
||||||
|
.card-body
|
||||||
|
.card-text.mb-2
|
||||||
|
= "#{event.contests.size} " + t("events.index.contests") if event.contests.size > 1
|
||||||
|
= "#{event.contests.size} " + t("events.index.contest") if event.contests.size <= 1
|
||||||
|
a.stretched-link href=event_path(event)
|
||||||
|
.row.mt-3
|
||||||
|
.col
|
||||||
|
css:
|
||||||
|
.card:hover { background-color: lightblue; }
|
||||||
|
.card.h-100
|
||||||
|
.card-header
|
||||||
|
= t("events.index.my_contests_outside_events")
|
||||||
|
.card-body
|
||||||
|
.card-text.mb-2
|
||||||
|
= "#{@contests_without_event} " + t("events.index.contests") if @contests_without_event > 1
|
||||||
|
= "#{@contests_without_event} " + t("events.index.contest") if @contests_without_event <= 1
|
||||||
|
a.stretched-link href=contests_path
|
||||||
1
app/views/events/new.html.slim
Normal file
1
app/views/events/new.html.slim
Normal file
@@ -0,0 +1 @@
|
|||||||
|
= render "form", event: @event, submit_text: t("helpers.buttons.add"), method: :post, url: "/events"
|
||||||
28
app/views/events/show.html.slim
Normal file
28
app/views/events/show.html.slim
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
.row
|
||||||
|
.col
|
||||||
|
h4.mb-3
|
||||||
|
= t("contests.index.manage_contests")
|
||||||
|
.float-end
|
||||||
|
a.btn.btn-primary.mb-4 href=new_contest_path
|
||||||
|
= t("contests.index.new_contest")
|
||||||
|
|
||||||
|
.row.row-cols-1.row-cols-md-3.g-4
|
||||||
|
- @contests.each do |contest|
|
||||||
|
.col
|
||||||
|
css:
|
||||||
|
.card:hover { background-color: lightblue; }
|
||||||
|
.card.h-100
|
||||||
|
.card-header
|
||||||
|
= contest.name
|
||||||
|
.card-body
|
||||||
|
.card-text.mb-2
|
||||||
|
= "#{contest.puzzles.length} #{t('puzzles.singular')}" if contest.puzzles.length <= 1
|
||||||
|
= "#{contest.puzzles.length} #{t('puzzles.plural')}" if contest.puzzles.length > 1
|
||||||
|
= " - #{contest.contestants.length} #{t('contestants.singular')}" if contest.contestants.length <= 1
|
||||||
|
= " - #{contest.contestants.length} #{t('contestants.plural')}" if contest.contestants.length > 1
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
- contest.puzzles.each do |puzzle|
|
||||||
|
- if puzzle.image.attached?
|
||||||
|
= image_tag puzzle.image, style: "max-height: 50px;", class: "mb-2 me-2"
|
||||||
|
a.stretched-link href=contest_path(contest)
|
||||||
@@ -82,6 +82,10 @@ en:
|
|||||||
csv_import:
|
csv_import:
|
||||||
file: File
|
file: File
|
||||||
separator: Separator
|
separator: Separator
|
||||||
|
event:
|
||||||
|
name: Name
|
||||||
|
shared_scoreboard: Shared scoreboard
|
||||||
|
shared_scoreboard_description: If activated, there will be a scoreboard link specific to the event, with tabs for each contest
|
||||||
message:
|
message:
|
||||||
author: Author
|
author: Author
|
||||||
processed: Processed?
|
processed: Processed?
|
||||||
@@ -142,6 +146,10 @@ en:
|
|||||||
blank: "No file selected"
|
blank: "No file selected"
|
||||||
empty: "This file is empty"
|
empty: "This file is empty"
|
||||||
not_a_csv_file: "it must be a CSV file"
|
not_a_csv_file: "it must be a CSV file"
|
||||||
|
event:
|
||||||
|
attributes:
|
||||||
|
name:
|
||||||
|
blank: The event name cannot be empty
|
||||||
offline:
|
offline:
|
||||||
attributes:
|
attributes:
|
||||||
end_image:
|
end_image:
|
||||||
@@ -277,6 +285,22 @@ en:
|
|||||||
plural: teams
|
plural: teams
|
||||||
upload_csv:
|
upload_csv:
|
||||||
title: Import participants
|
title: Import participants
|
||||||
|
events:
|
||||||
|
destroy:
|
||||||
|
notice: Event deleted
|
||||||
|
form:
|
||||||
|
shared_scoreboard_message: As safety measure, for each tab, no information will be shown until the first finish, or if the stopwatch for that contest is started.
|
||||||
|
index:
|
||||||
|
contest: contest
|
||||||
|
contests: contests
|
||||||
|
message: New feature! Events offer to groupe multiple contests in one management space. This also offers, if activated, to have a single scoreboard link for all contests of an event.
|
||||||
|
my_contests_outside_events: My contests not linked to any event
|
||||||
|
new: Create a new event
|
||||||
|
subtitle: My events
|
||||||
|
title: Welcome %{username}!
|
||||||
|
new:
|
||||||
|
notice: Event added
|
||||||
|
title: New event
|
||||||
helpers:
|
helpers:
|
||||||
badges:
|
badges:
|
||||||
registration: "registration"
|
registration: "registration"
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ fr:
|
|||||||
csv_import:
|
csv_import:
|
||||||
file: Fichier
|
file: Fichier
|
||||||
separator: Délimiteur
|
separator: Délimiteur
|
||||||
|
event:
|
||||||
|
name: Nom
|
||||||
|
shared_scoreboard: Classement partagé
|
||||||
|
shared_scoreboard_description: Si activé, un lien de classement partagé sera disponible pour l'événement, avec des onglets pour chacune des épreuves.
|
||||||
message:
|
message:
|
||||||
author: Auteur.ice
|
author: Auteur.ice
|
||||||
processed: Traité ?
|
processed: Traité ?
|
||||||
@@ -113,6 +117,10 @@ fr:
|
|||||||
blank: "Aucun fichier sélectionné"
|
blank: "Aucun fichier sélectionné"
|
||||||
empty: "Ce fichier est vide"
|
empty: "Ce fichier est vide"
|
||||||
not_a_csv_file: "Le fichier doit être au format CSV"
|
not_a_csv_file: "Le fichier doit être au format CSV"
|
||||||
|
event:
|
||||||
|
attributes:
|
||||||
|
name:
|
||||||
|
blank: Le nom de l'événement ne peut pas être vide
|
||||||
offline:
|
offline:
|
||||||
attributes:
|
attributes:
|
||||||
end_image:
|
end_image:
|
||||||
@@ -248,6 +256,22 @@ fr:
|
|||||||
plural: équipes
|
plural: équipes
|
||||||
upload_csv:
|
upload_csv:
|
||||||
title: Importer des participant.e.s
|
title: Importer des participant.e.s
|
||||||
|
events:
|
||||||
|
destroy:
|
||||||
|
notice: Événement supprimé
|
||||||
|
form:
|
||||||
|
shared_scoreboard_message: Par mesure de sécurité, chaque onglet restera vide jusqu'à ce que soit il y a des premières complétions, soit le chronomètre de l'épreuve a été démarré.
|
||||||
|
index:
|
||||||
|
contest: épreuve
|
||||||
|
contests: épreuves
|
||||||
|
message: Nouvelle fonctionalité ! Les événements permettent de regrouper plusieurs concours/épreuves en un seul espace de gestion. Cela permet aussi, si souhaité, d'avoir un lien de classement commun à toutes les épreuces d'un même événement.
|
||||||
|
my_contests_outside_events: Mes concours non associés à un événement
|
||||||
|
new: Créer un nouvel événement
|
||||||
|
subtitle: Mes événements
|
||||||
|
title: Bienvenue %{username} !
|
||||||
|
new:
|
||||||
|
notice: Événement ajouté
|
||||||
|
title: Nouvel événement
|
||||||
helpers:
|
helpers:
|
||||||
badges:
|
badges:
|
||||||
registration: "auto-inscription"
|
registration: "auto-inscription"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Rails.application.routes.draw do
|
|||||||
get "up" => "rails/health#show", as: :rails_health_check
|
get "up" => "rails/health#show", as: :rails_health_check
|
||||||
|
|
||||||
# Defines the root path route ("/")
|
# Defines the root path route ("/")
|
||||||
root "contests#index"
|
root "events#index"
|
||||||
|
|
||||||
resources :contests do
|
resources :contests do
|
||||||
get "settings/general", to: "contests#settings_general_edit"
|
get "settings/general", to: "contests#settings_general_edit"
|
||||||
@@ -39,6 +39,7 @@ Rails.application.routes.draw do
|
|||||||
get "generate_qrcodes_pdf", to: "contestants#generate_qrcodes_pdf"
|
get "generate_qrcodes_pdf", to: "contestants#generate_qrcodes_pdf"
|
||||||
get "generate_qrcodes_archive", to: "contestants#generate_qrcodes_archive"
|
get "generate_qrcodes_archive", to: "contestants#generate_qrcodes_archive"
|
||||||
end
|
end
|
||||||
|
resources :events
|
||||||
resources :passwords, param: :token
|
resources :passwords, param: :token
|
||||||
resource :session
|
resource :session
|
||||||
resources :users do
|
resources :users do
|
||||||
@@ -63,10 +64,6 @@ Rails.application.routes.draw do
|
|||||||
post "public/p/:contestant_id", to: "contestants#post_public_completion"
|
post "public/p/:contestant_id", to: "contestants#post_public_completion"
|
||||||
get "public/p/:contestant_id/updated", to: "contestants#public_completion_updated"
|
get "public/p/:contestant_id/updated", to: "contestants#public_completion_updated"
|
||||||
|
|
||||||
direct :direct_test do
|
|
||||||
"https://lol.com"
|
|
||||||
end
|
|
||||||
|
|
||||||
direct :public_scoreboard do |contest|
|
direct :public_scoreboard do |contest|
|
||||||
"/public/#{contest.friendly_id}/public"
|
"/public/#{contest.friendly_id}/public"
|
||||||
end
|
end
|
||||||
|
|||||||
7
db/migrate/20260725121740_create_events.rb
Normal file
7
db/migrate/20260725121740_create_events.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class CreateEvents < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
create_table :events do |t|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/migrate/20260725122117_add_name_to_event.rb
Normal file
5
db/migrate/20260725122117_add_name_to_event.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddNameToEvent < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :events, :name, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/migrate/20260725122358_add_event_to_contest.rb
Normal file
5
db/migrate/20260725122358_add_event_to_contest.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddEventToContest < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_reference :contests, :event, foreign_key: true
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/migrate/20260725122559_add_user_to_event.rb
Normal file
5
db/migrate/20260725122559_add_user_to_event.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddUserToEvent < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_reference :events, :user, null: false, foreign_key: true
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddSharedScoreboardToEvent < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :events, :shared_scoreboard, :boolean
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/migrate/20260726114604_add_lang_to_event.rb
Normal file
5
db/migrate/20260726114604_add_lang_to_event.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddLangToEvent < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :events, :lang, :string, default: 'en'
|
||||||
|
end
|
||||||
|
end
|
||||||
19
db/schema.rb
generated
19
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: 2026_04_24_091108) do
|
ActiveRecord::Schema[8.0].define(version: 2026_07_26_114604) 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
|
||||||
@@ -83,7 +83,6 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_24_091108) do
|
|||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "display_time"
|
t.string "display_time"
|
||||||
t.integer "time_seconds"
|
t.integer "time_seconds"
|
||||||
t.string "projected_time"
|
|
||||||
t.string "qrcode"
|
t.string "qrcode"
|
||||||
t.index ["contest_id"], name: "index_contestants_on_contest_id"
|
t.index ["contest_id"], name: "index_contestants_on_contest_id"
|
||||||
end
|
end
|
||||||
@@ -106,7 +105,9 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_24_091108) do
|
|||||||
t.datetime "start_time"
|
t.datetime "start_time"
|
||||||
t.datetime "pause_time"
|
t.datetime "pause_time"
|
||||||
t.boolean "show_stopwatch"
|
t.boolean "show_stopwatch"
|
||||||
t.string "organizer_form"
|
t.boolean "organizer_form"
|
||||||
|
t.integer "event_id"
|
||||||
|
t.index ["event_id"], name: "index_contests_on_event_id"
|
||||||
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
|
||||||
@@ -118,6 +119,16 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_24_091108) do
|
|||||||
t.string "content", null: false
|
t.string "content", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "events", force: :cascade do |t|
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.string "name"
|
||||||
|
t.integer "user_id", null: false
|
||||||
|
t.boolean "shared_scoreboard"
|
||||||
|
t.string "lang", default: "en"
|
||||||
|
t.index ["user_id"], name: "index_events_on_user_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "friendly_id_slugs", force: :cascade do |t|
|
create_table "friendly_id_slugs", force: :cascade do |t|
|
||||||
t.string "slug", null: false
|
t.string "slug", null: false
|
||||||
t.integer "sluggable_id", null: false
|
t.integer "sluggable_id", null: false
|
||||||
@@ -198,7 +209,9 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_24_091108) do
|
|||||||
add_foreign_key "completions", "messages"
|
add_foreign_key "completions", "messages"
|
||||||
add_foreign_key "completions", "puzzles"
|
add_foreign_key "completions", "puzzles"
|
||||||
add_foreign_key "contestants", "contests"
|
add_foreign_key "contestants", "contests"
|
||||||
|
add_foreign_key "contests", "events"
|
||||||
add_foreign_key "contests", "users"
|
add_foreign_key "contests", "users"
|
||||||
|
add_foreign_key "events", "users"
|
||||||
add_foreign_key "messages", "contests"
|
add_foreign_key "messages", "contests"
|
||||||
add_foreign_key "offlines", "completions"
|
add_foreign_key "offlines", "completions"
|
||||||
add_foreign_key "offlines", "contestants"
|
add_foreign_key "offlines", "contestants"
|
||||||
|
|||||||
@@ -20,15 +20,18 @@
|
|||||||
# 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
|
||||||
|
# event_id :integer
|
||||||
# user_id :integer not null
|
# user_id :integer not null
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
# index_contests_on_event_id (event_id)
|
||||||
# index_contests_on_slug (slug) UNIQUE
|
# index_contests_on_slug (slug) UNIQUE
|
||||||
# index_contests_on_user_id (user_id)
|
# index_contests_on_user_id (user_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
|
# event_id (event_id => events.id)
|
||||||
# user_id (user_id => users.id)
|
# user_id (user_id => users.id)
|
||||||
#
|
#
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
|
|||||||
25
spec/factories/events.rb
Normal file
25
spec/factories/events.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: events
|
||||||
|
#
|
||||||
|
# id :integer not null, primary key
|
||||||
|
# lang :string default("en")
|
||||||
|
# name :string
|
||||||
|
# shared_scoreboard :boolean
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# user_id :integer not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_events_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# user_id (user_id => users.id)
|
||||||
|
#
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :event do
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
15
spec/helpers/events_helper_spec.rb
Normal file
15
spec/helpers/events_helper_spec.rb
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the EventsHelper. For example:
|
||||||
|
#
|
||||||
|
# describe EventsHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe EventsHelper, type: :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
25
spec/models/event_spec.rb
Normal file
25
spec/models/event_spec.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: events
|
||||||
|
#
|
||||||
|
# id :integer not null, primary key
|
||||||
|
# lang :string default("en")
|
||||||
|
# name :string
|
||||||
|
# shared_scoreboard :boolean
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# user_id :integer not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_events_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# user_id (user_id => users.id)
|
||||||
|
#
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Event, type: :model do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
7
spec/requests/events_spec.rb
Normal file
7
spec/requests/events_spec.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe "Events", type: :request do
|
||||||
|
describe "GET /index" do
|
||||||
|
pending "add some examples (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user