Add offline model and "new" form/controller
This commit is contained in:
		| @@ -1,6 +1,6 @@ | |||||||
| class ContestsController < ApplicationController | class ContestsController < ApplicationController | ||||||
|   before_action :set_contest, only: %i[ destroy edit show update ] |   before_action :set_contest, only: %i[ destroy edit show update ] | ||||||
|   skip_before_action :require_authentication, only: %i[ scoreboard ] |   skip_before_action :require_authentication, only: %i[ scoreboard offline_new ] | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     authorize :contest |     authorize :contest | ||||||
| @@ -99,6 +99,21 @@ class ContestsController < ApplicationController | |||||||
|     render :scoreboard |     render :scoreboard | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def offline_new | ||||||
|  |     @contest = Contest.find_by(slug: params[:id]) | ||||||
|  |     unless @contest && @contest.public | ||||||
|  |       skip_authorization | ||||||
|  |       not_found and return | ||||||
|  |     end | ||||||
|  |     authorize :contest | ||||||
|  |  | ||||||
|  |     I18n.locale = @contest.lang | ||||||
|  |  | ||||||
|  |     @title = I18n.t("contests.scoreboard.title", name: @contest.name) | ||||||
|  |  | ||||||
|  |     @offline = Offline.new | ||||||
|  |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_badges |   def set_badges | ||||||
|   | |||||||
| @@ -31,6 +31,7 @@ class Contest < ApplicationRecord | |||||||
|   has_many :contestants, dependent: :destroy |   has_many :contestants, dependent: :destroy | ||||||
|   has_many :puzzles, dependent: :destroy |   has_many :puzzles, dependent: :destroy | ||||||
|   has_many :messages, dependent: :destroy |   has_many :messages, dependent: :destroy | ||||||
|  |   has_many :offlines, dependent: :destroy | ||||||
|  |  | ||||||
|   friendly_id :name, use: :slugged |   friendly_id :name, use: :slugged | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										27
									
								
								app/models/offline.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/models/offline.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | # == Schema Information | ||||||
|  | # | ||||||
|  | # Table name: offlines | ||||||
|  | # | ||||||
|  | #  id         :integer          not null, primary key | ||||||
|  | #  end_time   :datetime | ||||||
|  | #  start_time :datetime         not null | ||||||
|  | #  created_at :datetime         not null | ||||||
|  | #  updated_at :datetime         not null | ||||||
|  | #  contest_id :integer          not null | ||||||
|  | # | ||||||
|  | # Indexes | ||||||
|  | # | ||||||
|  | #  index_offlines_on_contest_id  (contest_id) | ||||||
|  | # | ||||||
|  | # Foreign Keys | ||||||
|  | # | ||||||
|  | #  contest_id  (contest_id => contests.id) | ||||||
|  | # | ||||||
|  | class Offline < ApplicationRecord | ||||||
|  |   belongs_to :contest | ||||||
|  |  | ||||||
|  |   has_one_attached :start_image | ||||||
|  |   has_one_attached :end_image | ||||||
|  |  | ||||||
|  |   validates :start_time, presence: true | ||||||
|  | end | ||||||
| @@ -47,6 +47,22 @@ class ContestPolicy < ApplicationPolicy | |||||||
|     record.user.id == user.id || user.admin? |     record.user.id == user.id || user.admin? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def offline_new? | ||||||
|  |     true | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def offline_create? | ||||||
|  |     true | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def offline_edit? | ||||||
|  |     true | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def offline_update? | ||||||
|  |     true | ||||||
|  |   end | ||||||
|  |  | ||||||
|   def scoreboard? |   def scoreboard? | ||||||
|     true |     true | ||||||
|   end |   end | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								app/views/contests/offline_new.html.slim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/views/contests/offline_new.html.slim
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | = form_with model: @offline, url: "/public/#{@contest.id}/offline" do |form| | ||||||
|  |   .row.mb-3 | ||||||
|  |     .col | ||||||
|  |       .form-text.mb-1 | ||||||
|  |         = t("puzzles.image_select") | ||||||
|  |       = form.file_field :start_image, accept: "image/*", class: "form-control" | ||||||
|  |       .form-text.error-message style="display: none;" id="image-error-message" | ||||||
|  |         = t("puzzles.form.file_too_big") | ||||||
|  |       javascript: | ||||||
|  |         function setMaxUploadSize() { | ||||||
|  |           const el = document.querySelector('input[type="file"]'); | ||||||
|  |           el.onchange = function() { | ||||||
|  |             if(this.files[0].size > 2 * 1024 * 1024) { | ||||||
|  |               document.getElementById('image-error-message').style.display = 'block'; | ||||||
|  |               this.value = ""; | ||||||
|  |             } else { | ||||||
|  |               document.getElementById('image-error-message').style.display = 'none'; | ||||||
|  |             } | ||||||
|  |           }; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         setMaxUploadSize(); | ||||||
|  |   .row.mt-3 | ||||||
|  |     .col | ||||||
|  |       = form.submit t("helpers.buttons.add"), class: "btn btn-primary" | ||||||
| @@ -32,4 +32,8 @@ Rails.application.routes.draw do | |||||||
|   post "message", to: "messages#create" |   post "message", to: "messages#create" | ||||||
|  |  | ||||||
|   get "public/:id", to: "contests#scoreboard" |   get "public/:id", to: "contests#scoreboard" | ||||||
|  |   get "public/:id/offline", to: "contests#offline_new" | ||||||
|  |   post "public/:id/offline", to: "contests#offline_create" | ||||||
|  |   get "public/:id/offline/:offline_id", to: "contests#offline_edit" | ||||||
|  |   post "public/:id/offline/:offline_id", to: "contests#offline_update" | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								db/migrate/20251029155116_create_offlines.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								db/migrate/20251029155116_create_offlines.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | class CreateOfflines < ActiveRecord::Migration[8.0] | ||||||
|  |   def change | ||||||
|  |     create_table :offlines do |t| | ||||||
|  |       t.timestamps | ||||||
|  |       t.belongs_to :contest, null: false, foreign_key: true | ||||||
|  |       t.datetime :start_time, null: false | ||||||
|  |       t.datetime :end_time | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										12
									
								
								db/schema.rb
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								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_10_28_131431) do | ActiveRecord::Schema[8.0].define(version: 2025_10_29_155116) 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 | ||||||
| @@ -125,6 +125,15 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_28_131431) do | |||||||
|     t.index ["contest_id"], name: "index_messages_on_contest_id" |     t.index ["contest_id"], name: "index_messages_on_contest_id" | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   create_table "offlines", force: :cascade do |t| | ||||||
|  |     t.datetime "created_at", null: false | ||||||
|  |     t.datetime "updated_at", null: false | ||||||
|  |     t.integer "contest_id", null: false | ||||||
|  |     t.datetime "start_time", null: false | ||||||
|  |     t.datetime "end_time" | ||||||
|  |     t.index ["contest_id"], name: "index_offlines_on_contest_id" | ||||||
|  |   end | ||||||
|  |  | ||||||
|   create_table "puzzles", force: :cascade do |t| |   create_table "puzzles", force: :cascade do |t| | ||||||
|     t.string "name" |     t.string "name" | ||||||
|     t.datetime "created_at", null: false |     t.datetime "created_at", null: false | ||||||
| @@ -165,6 +174,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_28_131431) do | |||||||
|   add_foreign_key "contestants", "contests" |   add_foreign_key "contestants", "contests" | ||||||
|   add_foreign_key "contests", "users" |   add_foreign_key "contests", "users" | ||||||
|   add_foreign_key "messages", "contests" |   add_foreign_key "messages", "contests" | ||||||
|  |   add_foreign_key "offlines", "contests" | ||||||
|   add_foreign_key "puzzles", "contests" |   add_foreign_key "puzzles", "contests" | ||||||
|   add_foreign_key "sessions", "users" |   add_foreign_key "sessions", "users" | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								spec/factories/offlines.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								spec/factories/offlines.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | # == Schema Information | ||||||
|  | # | ||||||
|  | # Table name: offlines | ||||||
|  | # | ||||||
|  | #  id         :integer          not null, primary key | ||||||
|  | #  end_time   :datetime | ||||||
|  | #  start_time :datetime         not null | ||||||
|  | #  created_at :datetime         not null | ||||||
|  | #  updated_at :datetime         not null | ||||||
|  | #  contest_id :integer          not null | ||||||
|  | # | ||||||
|  | # Indexes | ||||||
|  | # | ||||||
|  | #  index_offlines_on_contest_id  (contest_id) | ||||||
|  | # | ||||||
|  | # Foreign Keys | ||||||
|  | # | ||||||
|  | #  contest_id  (contest_id => contests.id) | ||||||
|  | # | ||||||
|  | FactoryBot.define do | ||||||
|  |   factory :offline do | ||||||
|  |      | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										24
									
								
								spec/models/offline_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								spec/models/offline_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | # == Schema Information | ||||||
|  | # | ||||||
|  | # Table name: offlines | ||||||
|  | # | ||||||
|  | #  id         :integer          not null, primary key | ||||||
|  | #  end_time   :datetime | ||||||
|  | #  start_time :datetime         not null | ||||||
|  | #  created_at :datetime         not null | ||||||
|  | #  updated_at :datetime         not null | ||||||
|  | #  contest_id :integer          not null | ||||||
|  | # | ||||||
|  | # Indexes | ||||||
|  | # | ||||||
|  | #  index_offlines_on_contest_id  (contest_id) | ||||||
|  | # | ||||||
|  | # Foreign Keys | ||||||
|  | # | ||||||
|  | #  contest_id  (contest_id => contests.id) | ||||||
|  | # | ||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | RSpec.describe Offline, type: :model do | ||||||
|  |   pending "add some examples to (or delete) #{__FILE__}" | ||||||
|  | end | ||||||
		Reference in New Issue
	
	Block a user