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)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# event_id :integer
|
||||
# user_id :integer not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_contests_on_slug (slug) UNIQUE
|
||||
# index_contests_on_user_id (user_id)
|
||||
# index_contests_on_event_id (event_id)
|
||||
# index_contests_on_slug (slug) UNIQUE
|
||||
# index_contests_on_user_id (user_id)
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# user_id (user_id => users.id)
|
||||
# event_id (event_id => events.id)
|
||||
# user_id (user_id => users.id)
|
||||
#
|
||||
class Contest < ApplicationRecord
|
||||
extend FriendlyId
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :event, optional: true
|
||||
has_many :categories
|
||||
has_many :completions, dependent: :destroy
|
||||
has_many :contestants, dependent: :destroy
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
#
|
||||
# Table name: contestants
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# display_time :string
|
||||
# email :string
|
||||
# name :string
|
||||
# projected_time :string
|
||||
# qrcode :string
|
||||
# time_seconds :integer
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# contest_id :integer not null
|
||||
# id :integer not null, primary key
|
||||
# display_time :string
|
||||
# email :string
|
||||
# name :string
|
||||
# qrcode :string
|
||||
# time_seconds :integer
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# contest_id :integer not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
||||
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
|
||||
#
|
||||
class User < ApplicationRecord
|
||||
has_many :events, dependent: :destroy
|
||||
has_many :contests, dependent: :destroy
|
||||
has_many :sessions, dependent: :destroy
|
||||
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)
|
||||
Reference in New Issue
Block a user