Add TicketType model with validations and fix dropdown menu

- Create TicketType model with Party association and Ticket relationship
- Add comprehensive validations for name, description, pricing, and date ranges
- Generate migration for ticket_types table with all required fields
- Add Alpine.js import to fix dropdown menu functionality
- Update ticket model with validations for qr_code, price, and status
This commit is contained in:
kbe
2025-08-23 19:31:17 +02:00
parent ef9cfd6cdf
commit b5b5ca1cc0
10 changed files with 129 additions and 8 deletions

View File

@@ -1,3 +1,3 @@
// Entry point for the build script in your package.json
import "@hotwired/turbo-rails"
import "./controllers"
import "@hotwired/turbo-rails";
import "./controllers";

View File

@@ -1,9 +1,14 @@
import { Application } from "@hotwired/stimulus"
import { Application } from "@hotwired/stimulus";
import Alpine from "alpinejs";
const application = Application.start()
const application = Application.start();
// Configure Stimulus development experience
application.debug = false
window.Stimulus = application
application.debug = false;
window.Stimulus = application;
export { application }
// Configure and load Alpine
window.Alpine = Alpine;
Alpine.start();
export { application };

8
app/models/ticket.rb Normal file
View File

@@ -0,0 +1,8 @@
class Ticket < ApplicationRecord
# Validations
validates :qr_code, presence: true, uniqueness: true
validates :party_id, presence: true
validates :user_id, presence: true
validates :price_cents, presence: true, numericality: { greater_than: 0 }
validates :status, presence: true, inclusion: { in: %w[active used expired refunded] }
end

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

@@ -0,0 +1,24 @@
class TicketType < ApplicationRecord
# Associations
belongs_to :party
has_many :tickets
# Validations
validates :name, presence: true, length: { minimum: 3, maximum: 50 }
validates :description, presence: true, length: { minimum: 10, maximum: 500 }
validates :price_cents, presence: true, numericality: { greater_than: 0 }
validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }
validates :party_id, presence: true
validates :sale_start_at, presence: true
validates :sale_end_at, presence: true
validate :sale_end_after_start
validates :requires_id, inclusion: { in: [true, false] }
validates :minimum_age, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 120 }, allow_nil: true
private
def sale_end_after_start
return unless sale_start_at && sale_end_at
errors.add(:sale_end_at, "must be after sale start") if sale_end_at <= sale_start_at
end
end

View File

@@ -0,0 +1,17 @@
class CreateTicketTypes < ActiveRecord::Migration[8.0]
def change
create_table :ticket_types do |t|
t.references :party, null: false, foreign_key: true
t.string :name
t.text :description
t.integer :price_cents
t.integer :quantity
t.datetime :sale_start_at
t.datetime :sale_end_at
t.boolean :requires_id
t.integer :minimum_age
t.timestamps
end
end
end

View File

@@ -0,0 +1,9 @@
class CreateTickets < ActiveRecord::Migration[8.0]
def change
create_table :tickets do |t|
t.string :qr_code
t.timestamps
end
end
end

25
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_08_23_145902) do
ActiveRecord::Schema[8.0].define(version: 2025_08_23_171354) do
create_table "parties", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
t.string "name", null: false
t.text "description", null: false
@@ -27,6 +27,27 @@ ActiveRecord::Schema[8.0].define(version: 2025_08_23_145902) do
t.index ["state"], name: "index_parties_on_state"
end
create_table "ticket_types", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
t.bigint "party_id", null: false
t.string "name"
t.text "description"
t.integer "price_cents"
t.integer "quantity"
t.datetime "sale_start_at"
t.datetime "sale_end_at"
t.boolean "requires_id"
t.integer "minimum_age"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["party_id"], name: "index_ticket_types_on_party_id"
end
create_table "tickets", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
t.string "qr_code"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
@@ -38,4 +59,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_08_23_145902) do
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "ticket_types", "parties"
end

21
opencode.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"myprovider": {
"npm": "@ai-sdk/openai-compatible",
"name": "Scaleway",
"options": {
"baseURL": "https://api.scaleway.ai/v1",
"apiKey": "928c8440-604b-423a-983b-00afd5a1164e"
},
"models": {
"devstral-small-2505": {
"name": "Devstral 2505"
},
"qwen3-coder-30b-a3b-instruct": {
"name": "Qwen3 Coder 30b a3b instruct"
}
}
}
}
}

7
test/fixtures/tickets.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
qr_code: MyString
two:
qr_code: MyString

View File

@@ -0,0 +1,7 @@
require "test_helper"
class TicketTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end