diff --git a/app/assets/builds/application.css b/app/assets/builds/application.css index 64e0ff1..67ef7d4 100644 --- a/app/assets/builds/application.css +++ b/app/assets/builds/application.css @@ -2886,6 +2886,10 @@ button, [role="button"], a.btn-game, .btn-game { margin-top: 2rem; } +.-mt-1 { + margin-top: -0.25rem; +} + .line-clamp-2 { overflow: hidden; display: -webkit-box; diff --git a/app/controllers/admin/teams_controller.rb b/app/controllers/admin/teams_controller.rb index c951827..10e884a 100644 --- a/app/controllers/admin/teams_controller.rb +++ b/app/controllers/admin/teams_controller.rb @@ -108,6 +108,6 @@ def assign_users end def team_params - params.require(:team).permit(:house_id, :captain_id, :number, :airtag_slug, :avatar) + params.require(:team).permit(:house_id, :captain_id, :number, :name, :airtag_slug, :avatar) end end diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index 221b315..825c143 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -35,7 +35,7 @@ def map { lat: ping.latitude, lng: ping.longitude, - teamName: team.name, + teamName: "#{team.house.name} ##{team.number}#{team.custom_name? ? " – #{team[:name]}" : ""}", timeAgo: helpers.distance_of_time_in_words(ping.pinged_at, Time.now), avatarUrl: team.avatar_url, backgroundUrl: "https://placehold.co/500/#{house_color}/#{house_color}", diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb index 8f556fd..be4d90d 100644 --- a/app/controllers/teams_controller.rb +++ b/app/controllers/teams_controller.rb @@ -1,10 +1,38 @@ class TeamsController < ApplicationController + before_action :set_team + before_action :require_captain!, only: [ :edit, :update ] + def show - @team = Team.includes(:house, users: []).find(params[:id]) @submissions = Submission.where(team: @team) .where.not(status: :pending) .includes(:challenge, :user, { zone: :challenge }) .order(created_at: :desc) @pings = @team.location_pings.order(:pinged_at) end + + def edit; end + + def update + if @team.update(team_name_params) + redirect_to team_path(@team), notice: "Team name updated." + else + render :edit, status: :unprocessable_entity + end + end + + private + + def set_team + @team = Team.includes(:house, users: []).find(params[:id]) + end + + def require_captain! + unless current_user == @team.captain + redirect_to team_path(@team), alert: "Only the team captain can do that." + end + end + + def team_name_params + params.require(:team).permit(:name) + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9098904..077e81f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,4 +1,10 @@ module ApplicationHelper + def team_label(team) + label = "Team ##{team.number}" + label += " – #{team[:name]}" if team.custom_name? + label + end + def admin_tool(class_name: "", element: "div", **options, &block) return unless current_user&.admin? concat content_tag(element, class: "admin-tool #{class_name}", **options, &block) diff --git a/app/models/airtag_api_log.rb b/app/models/airtag_api_log.rb index 16607ee..5ed4e04 100644 --- a/app/models/airtag_api_log.rb +++ b/app/models/airtag_api_log.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: airtag_api_logs +# Database name: primary # # id :bigint not null, primary key # fetched_at :datetime not null diff --git a/app/models/allowed_email.rb b/app/models/allowed_email.rb index 97a4ff4..75fbb56 100644 --- a/app/models/allowed_email.rb +++ b/app/models/allowed_email.rb @@ -3,6 +3,7 @@ # == Schema Information # # Table name: allowed_emails +# Database name: primary # # id :bigint not null, primary key # email :string not null diff --git a/app/models/challenge.rb b/app/models/challenge.rb index 44f18b2..6af92f5 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: challenges +# Database name: primary # # id :bigint not null, primary key # description :text diff --git a/app/models/game_setting.rb b/app/models/game_setting.rb index 79b046c..417de4f 100644 --- a/app/models/game_setting.rb +++ b/app/models/game_setting.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: game_settings +# Database name: primary # # id :bigint not null, primary key # game_started :boolean default(FALSE), not null diff --git a/app/models/house.rb b/app/models/house.rb index f99181b..6e4c94d 100644 --- a/app/models/house.rb +++ b/app/models/house.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: houses +# Database name: primary # # id :bigint not null, primary key # color :string diff --git a/app/models/location_ping.rb b/app/models/location_ping.rb index d564c58..4df68f5 100644 --- a/app/models/location_ping.rb +++ b/app/models/location_ping.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: location_pings +# Database name: primary # # id :bigint not null, primary key # latitude :decimal(10, 7) not null diff --git a/app/models/notification.rb b/app/models/notification.rb index 4b4bb0d..e7800f2 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: notifications +# Database name: primary # # id :bigint not null, primary key # link :string diff --git a/app/models/submission.rb b/app/models/submission.rb index 8879560..97bdcc4 100644 --- a/app/models/submission.rb +++ b/app/models/submission.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: submissions +# Database name: primary # # id :bigint not null, primary key # admin_notes :text diff --git a/app/models/team.rb b/app/models/team.rb index d9f526d..17ea064 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -1,10 +1,12 @@ # == Schema Information # # Table name: teams +# Database name: primary # # id :bigint not null, primary key # airtag_slug :string # last_synced_at :datetime +# name :string # number :integer not null # created_at :datetime not null # updated_at :datetime not null @@ -34,9 +36,18 @@ class Team < ApplicationRecord validates :number, presence: true, uniqueness: { scope: :house_id } validates :airtag_slug, uniqueness: { allow_blank: true } + validates :name, length: { maximum: 25 }, allow_blank: true + + before_validation do + self[:name] = ActionController::Base.helpers.strip_tags(self[:name]).strip.presence + end def name - "#{house.name} #{number}" + self[:name].presence || "#{house.name} #{number}" + end + + def custom_name? + self[:name].present? end def points diff --git a/app/models/user.rb b/app/models/user.rb index 5b457b3..a43c5d0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,6 +3,7 @@ # == Schema Information # # Table name: users +# Database name: primary # # id :bigint not null, primary key # avatar_url :string diff --git a/app/models/zone.rb b/app/models/zone.rb index 99dfe81..dc5b91b 100644 --- a/app/models/zone.rb +++ b/app/models/zone.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: zones +# Database name: primary # # id :bigint not null, primary key # coordinates :string diff --git a/app/models/zone/claim.rb b/app/models/zone/claim.rb index b418e75..ea2dafa 100644 --- a/app/models/zone/claim.rb +++ b/app/models/zone/claim.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: zone_claims +# Database name: primary # # id :bigint not null, primary key # created_at :datetime not null diff --git a/app/views/admin/teams/_form.html.erb b/app/views/admin/teams/_form.html.erb index d4abb99..ad7db7f 100644 --- a/app/views/admin/teams/_form.html.erb +++ b/app/views/admin/teams/_form.html.erb @@ -25,6 +25,11 @@ class: "filter-input" %> +
<%= current_user.display_name %>
<% if team %>- <%= house&.name %> · #<%= team.number %> · <%= team.points %>pts + <%= house&.name %> · <%= team_label(team) %> · <%= team.points %>pts
<% else %>No team
@@ -159,7 +177,7 @@<%= current_user.display_name %>
<% if team %> -<%= house&.name %> · Team #<%= team.number %>
+<%= house&.name %> · <%= team_label(team) %>
<% else %>No team assigned
<% end %> @@ -171,6 +189,23 @@ <% end %>Team
#<%= team.number %>
+ <% if team.custom_name? %><%= team[:name] %>
<% end %>Points
@@ -585,7 +586,7 @@ <%= team.number %>Team #<%= team.number %><% if is_yours %>You<% end %>
+<%= team_label(team) %><% if is_yours %>You<% end %>
<%= @current_house.name %>
+ <%= link_to @team.house.name, house_path(@team.house), class: "hover:underline" %> +
+Max 25 characters. Leave blank to use the default: "<%= @team.house.name %> <%= @team.number %>".
+<%= link_to @team.house.name, house_path(@team.house), class: "hover:underline" %>
-Team
<% if @user.team %> - <%= link_to team_path(@user.team), class: "font-black text-black hover:underline" do %> - #<%= @user.team.number %> - <% end %> + <%= link_to team_label(@user.team), team_path(@user.team), class: "font-black text-black hover:underline" %> <% else %> — <% end %> diff --git a/config/routes.rb b/config/routes.rb index 3b7fc77..99cf22b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -15,7 +15,7 @@ resources :submissions, only: [ :create ] resources :users, only: [ :show ] resources :houses, only: [ :show ] - resources :teams, only: [ :show ] + resources :teams, only: [ :show, :edit, :update ] resource :location_pings, only: [ :create ] namespace :admin do diff --git a/db/migrate/20260522142717_add_name_to_teams.rb b/db/migrate/20260522142717_add_name_to_teams.rb new file mode 100644 index 0000000..4ea3fbc --- /dev/null +++ b/db/migrate/20260522142717_add_name_to_teams.rb @@ -0,0 +1,5 @@ +class AddNameToTeams < ActiveRecord::Migration[8.1] + def change + add_column :teams, :name, :string + end +end diff --git a/db/queue_schema.rb b/db/queue_schema.rb index 85194b6..c13f810 100644 --- a/db/queue_schema.rb +++ b/db/queue_schema.rb @@ -1,129 +1,366 @@ -ActiveRecord::Schema[7.1].define(version: 1) do +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[8.1].define(version: 2026_05_22_142717) do + # These are extensions that must be enabled in order to support this database + enable_extension "pg_catalog.plpgsql" + + create_table "action_text_rich_texts", force: :cascade do |t| + t.text "body" + t.datetime "created_at", null: false + t.string "name", null: false + t.bigint "record_id", null: false + t.string "record_type", null: false + t.datetime "updated_at", null: false + t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true + end + + create_table "active_storage_attachments", force: :cascade do |t| + t.bigint "blob_id", null: false + t.datetime "created_at", null: false + t.string "name", null: false + t.bigint "record_id", null: false + t.string "record_type", null: false + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true + end + + create_table "active_storage_blobs", force: :cascade do |t| + t.bigint "byte_size", null: false + t.string "checksum" + t.string "content_type" + t.datetime "created_at", null: false + t.string "filename", null: false + t.string "key", null: false + t.text "metadata" + t.string "service_name", null: false + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true + end + + create_table "active_storage_variant_records", force: :cascade do |t| + t.bigint "blob_id", null: false + t.string "variation_digest", null: false + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true + end + + create_table "airtag_api_logs", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "fetched_at", null: false + t.integer "location_count", default: 0, null: false + t.text "response_body" + t.boolean "success", default: true, null: false + t.datetime "updated_at", null: false + t.index ["fetched_at"], name: "index_airtag_api_logs_on_fetched_at" + end + + create_table "allowed_emails", force: :cascade do |t| + t.datetime "created_at", null: false + t.string "email", null: false + t.string "note" + t.datetime "updated_at", null: false + t.index ["email"], name: "index_allowed_emails_on_email", unique: true + end + + create_table "challenges", force: :cascade do |t| + t.datetime "created_at", null: false + t.text "description" + t.boolean "general", default: false, null: false + t.boolean "individual_media", default: false, null: false + t.datetime "last_synced_at" + t.boolean "must_keep_item", default: false, null: false + t.integer "status", default: 0, null: false + t.string "title", null: false + t.datetime "updated_at", null: false + t.bigint "user_id" + t.integer "value", default: 100, null: false + t.index ["user_id"], name: "index_challenges_on_user_id" + end + + create_table "game_settings", force: :cascade do |t| + t.datetime "created_at", null: false + t.boolean "game_started", default: false, null: false + t.datetime "updated_at", null: false + end + + create_table "houses", force: :cascade do |t| + t.string "color" + t.datetime "created_at", null: false + t.bigint "head_of_house_id" + t.datetime "last_synced_at" + t.string "name", null: false + t.datetime "updated_at", null: false + t.index ["head_of_house_id"], name: "index_houses_on_head_of_house_id" + t.index ["name"], name: "index_houses_on_name", unique: true + end + + create_table "location_pings", force: :cascade do |t| + t.datetime "created_at", null: false + t.decimal "latitude", precision: 10, scale: 7, null: false + t.decimal "longitude", precision: 10, scale: 7, null: false + t.string "method", default: "airtag", null: false + t.datetime "pinged_at", null: false + t.integer "team_id", null: false + t.datetime "updated_at", null: false + t.bigint "user_id" + t.index ["team_id", "pinged_at"], name: "index_location_pings_on_team_id_and_pinged_at", unique: true + t.index ["team_id"], name: "index_location_pings_on_team_id" + t.index ["user_id"], name: "index_location_pings_on_user_id" + end + + create_table "notifications", force: :cascade do |t| + t.datetime "created_at", null: false + t.string "link" + t.string "message", null: false + t.bigint "notifiable_id" + t.string "notifiable_type" + t.boolean "read", default: false, null: false + t.datetime "updated_at", null: false + t.bigint "user_id", null: false + t.index ["notifiable_type", "notifiable_id"], name: "index_notifications_on_notifiable" + t.index ["user_id"], name: "index_notifications_on_user_id" + end + create_table "solid_queue_blocked_executions", force: :cascade do |t| - t.bigint "job_id", null: false - t.string "queue_name", null: false - t.integer "priority", default: 0, null: false t.string "concurrency_key", null: false - t.datetime "expires_at", null: false t.datetime "created_at", null: false - t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release" - t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance" - t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true + t.datetime "expires_at", null: false + t.bigint "job_id", null: false + t.integer "priority", default: 0, null: false + t.string "queue_name", null: false + t.index ["concurrency_key", "priority", "job_id"], name: "index_solid_queue_blocked_executions_for_release" + t.index ["expires_at", "concurrency_key"], name: "index_solid_queue_blocked_executions_for_maintenance" + t.index ["job_id"], name: "index_solid_queue_blocked_executions_on_job_id", unique: true end create_table "solid_queue_claimed_executions", force: :cascade do |t| + t.datetime "created_at", null: false t.bigint "job_id", null: false t.bigint "process_id" - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true - t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" + t.index ["job_id"], name: "index_solid_queue_claimed_executions_on_job_id", unique: true + t.index ["process_id", "job_id"], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" end create_table "solid_queue_failed_executions", force: :cascade do |t| - t.bigint "job_id", null: false - t.text "error" t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true + t.text "error" + t.bigint "job_id", null: false + t.index ["job_id"], name: "index_solid_queue_failed_executions_on_job_id", unique: true end create_table "solid_queue_jobs", force: :cascade do |t| - t.string "queue_name", null: false - t.string "class_name", null: false - t.text "arguments" - t.integer "priority", default: 0, null: false t.string "active_job_id" - t.datetime "scheduled_at" - t.datetime "finished_at" + t.text "arguments" + t.string "class_name", null: false t.string "concurrency_key" t.datetime "created_at", null: false + t.datetime "finished_at" + t.integer "priority", default: 0, null: false + t.string "queue_name", null: false + t.datetime "scheduled_at" t.datetime "updated_at", null: false - t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id" - t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name" - t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at" - t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering" - t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting" + t.index ["active_job_id"], name: "index_solid_queue_jobs_on_active_job_id" + t.index ["class_name"], name: "index_solid_queue_jobs_on_class_name" + t.index ["finished_at"], name: "index_solid_queue_jobs_on_finished_at" + t.index ["queue_name", "finished_at"], name: "index_solid_queue_jobs_for_filtering" + t.index ["scheduled_at", "finished_at"], name: "index_solid_queue_jobs_for_alerting" end create_table "solid_queue_pauses", force: :cascade do |t| - t.string "queue_name", null: false t.datetime "created_at", null: false - t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true + t.string "queue_name", null: false + t.index ["queue_name"], name: "index_solid_queue_pauses_on_queue_name", unique: true end create_table "solid_queue_processes", force: :cascade do |t| + t.datetime "created_at", null: false + t.string "hostname" t.string "kind", null: false t.datetime "last_heartbeat_at", null: false - t.bigint "supervisor_id" - t.integer "pid", null: false - t.string "hostname" t.text "metadata" - t.datetime "created_at", null: false t.string "name", null: false - t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at" - t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true - t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id" + t.integer "pid", null: false + t.bigint "supervisor_id" + t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at" + t.index ["name", "supervisor_id"], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true + t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id" end create_table "solid_queue_ready_executions", force: :cascade do |t| + t.datetime "created_at", null: false t.bigint "job_id", null: false - t.string "queue_name", null: false t.integer "priority", default: 0, null: false - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true - t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all" - t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue" + t.string "queue_name", null: false + t.index ["job_id"], name: "index_solid_queue_ready_executions_on_job_id", unique: true + t.index ["priority", "job_id"], name: "index_solid_queue_poll_all" + t.index ["queue_name", "priority", "job_id"], name: "index_solid_queue_poll_by_queue" end create_table "solid_queue_recurring_executions", force: :cascade do |t| + t.datetime "created_at", null: false t.bigint "job_id", null: false - t.string "task_key", null: false t.datetime "run_at", null: false - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true - t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true + t.string "task_key", null: false + t.index ["job_id"], name: "index_solid_queue_recurring_executions_on_job_id", unique: true + t.index ["task_key", "run_at"], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true end create_table "solid_queue_recurring_tasks", force: :cascade do |t| - t.string "key", null: false - t.string "schedule", null: false - t.string "command", limit: 2048 - t.string "class_name" t.text "arguments" - t.string "queue_name" + t.string "class_name" + t.string "command", limit: 2048 + t.datetime "created_at", null: false + t.text "description" + t.string "key", null: false t.integer "priority", default: 0 + t.string "queue_name" + t.string "schedule", null: false t.boolean "static", default: true, null: false - t.text "description" - t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true - t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static" + t.index ["key"], name: "index_solid_queue_recurring_tasks_on_key", unique: true + t.index ["static"], name: "index_solid_queue_recurring_tasks_on_static" end create_table "solid_queue_scheduled_executions", force: :cascade do |t| + t.datetime "created_at", null: false t.bigint "job_id", null: false - t.string "queue_name", null: false t.integer "priority", default: 0, null: false + t.string "queue_name", null: false t.datetime "scheduled_at", null: false - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true - t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all" + t.index ["job_id"], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true + t.index ["scheduled_at", "priority", "job_id"], name: "index_solid_queue_dispatch_all" end create_table "solid_queue_semaphores", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "expires_at", null: false t.string "key", null: false + t.datetime "updated_at", null: false t.integer "value", default: 1, null: false - t.datetime "expires_at", null: false + t.index ["expires_at"], name: "index_solid_queue_semaphores_on_expires_at" + t.index ["key", "value"], name: "index_solid_queue_semaphores_on_key_and_value" + t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true + end + + create_table "submissions", force: :cascade do |t| + t.text "admin_notes" + t.integer "challenge_id" + t.datetime "created_at", null: false + t.decimal "latitude", precision: 10, scale: 7 + t.decimal "longitude", precision: 10, scale: 7 + t.text "rejection_reason" + t.text "review_note" + t.datetime "reviewed_at" + t.bigint "reviewed_by_user_id" + t.integer "status", default: 0, null: false + t.bigint "team_id", null: false + t.datetime "updated_at", null: false + t.bigint "user_id", null: false + t.bigint "zone_id" + t.index ["challenge_id"], name: "index_submissions_on_challenge_id" + t.index ["reviewed_by_user_id"], name: "index_submissions_on_reviewed_by_user_id" + t.index ["team_id"], name: "index_submissions_on_team_id" + t.index ["user_id"], name: "index_submissions_on_user_id" + t.index ["zone_id", "team_id"], name: "index_submissions_unique_pending_per_team_zone", unique: true, where: "(status = 0)" + t.index ["zone_id"], name: "index_submissions_on_zone_id" + end + + create_table "teams", force: :cascade do |t| + t.string "airtag_slug" + t.bigint "captain_id" + t.datetime "created_at", null: false + t.bigint "house_id", null: false + t.datetime "last_synced_at" + t.string "name" + t.integer "number", null: false + t.datetime "updated_at", null: false + t.index ["captain_id"], name: "index_teams_on_captain_id" + t.index ["house_id", "number"], name: "index_teams_on_house_id_and_number", unique: true + t.index ["house_id"], name: "index_teams_on_house_id" + end + + create_table "users", force: :cascade do |t| + t.string "airtable_id" + t.string "avatar_url" + t.datetime "created_at", null: false + t.string "email" + t.datetime "game_authorized_at" + t.text "hca_access_token" + t.string "hca_id" + t.datetime "last_synced_at" + t.string "name" + t.string "preferred_name" + t.string "role", default: "member", null: false + t.string "slack_id" + t.datetime "slack_synced_at" + t.bigint "team_id" + t.datetime "updated_at", null: false + t.index ["airtable_id"], name: "index_users_on_airtable_id", unique: true + t.index ["hca_id"], name: "index_users_on_hca_id", unique: true + t.index ["team_id"], name: "index_users_on_team_id" + end + + create_table "versions", force: :cascade do |t| + t.datetime "created_at" + t.string "event", null: false + t.bigint "item_id", null: false + t.string "item_type", null: false + t.text "object" + t.text "object_changes" + t.string "whodunnit" + t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id" + end + + create_table "zone_claims", force: :cascade do |t| + t.datetime "created_at", null: false + t.bigint "stealing_claim_id" + t.bigint "submission_id", null: false + t.bigint "team_id", null: false + t.datetime "updated_at", null: false + t.bigint "zone_id", null: false + t.index ["stealing_claim_id"], name: "index_zone_claims_on_stealing_claim_id" + t.index ["submission_id"], name: "index_zone_claims_on_submission_id" + t.index ["team_id"], name: "index_zone_claims_on_team_id" + t.index ["zone_id"], name: "index_zone_claims_on_zone_id" + end + + create_table "zones", force: :cascade do |t| + t.bigint "challenge_id" + t.string "coordinates" t.datetime "created_at", null: false + t.string "external_id" + t.datetime "last_synced_at" + t.string "name" + t.integer "status", default: 0, null: false t.datetime "updated_at", null: false - t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at" - t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value" - t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true + t.index ["challenge_id"], name: "index_zones_on_challenge_id" + t.index ["external_id"], name: "index_zones_on_external_id", unique: true end + 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 "challenges", "users" + add_foreign_key "location_pings", "teams" add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "submissions", "challenges", on_delete: :nullify + add_foreign_key "submissions", "teams", on_delete: :nullify + add_foreign_key "submissions", "users", column: "reviewed_by_user_id", on_delete: :nullify + add_foreign_key "submissions", "users", on_delete: :nullify + add_foreign_key "submissions", "zones", on_delete: :cascade + add_foreign_key "teams", "houses" + add_foreign_key "users", "teams" + add_foreign_key "zones", "challenges", on_delete: :nullify end diff --git a/db/schema.rb b/db/schema.rb index 7fc3b2e..c13f810 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_05_22_000000) do +ActiveRecord::Schema[8.1].define(version: 2026_05_22_142717) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -129,6 +129,127 @@ t.index ["user_id"], name: "index_notifications_on_user_id" end + create_table "solid_queue_blocked_executions", force: :cascade do |t| + t.string "concurrency_key", null: false + t.datetime "created_at", null: false + t.datetime "expires_at", null: false + t.bigint "job_id", null: false + t.integer "priority", default: 0, null: false + t.string "queue_name", null: false + t.index ["concurrency_key", "priority", "job_id"], name: "index_solid_queue_blocked_executions_for_release" + t.index ["expires_at", "concurrency_key"], name: "index_solid_queue_blocked_executions_for_maintenance" + t.index ["job_id"], name: "index_solid_queue_blocked_executions_on_job_id", unique: true + end + + create_table "solid_queue_claimed_executions", force: :cascade do |t| + t.datetime "created_at", null: false + t.bigint "job_id", null: false + t.bigint "process_id" + t.index ["job_id"], name: "index_solid_queue_claimed_executions_on_job_id", unique: true + t.index ["process_id", "job_id"], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" + end + + create_table "solid_queue_failed_executions", force: :cascade do |t| + t.datetime "created_at", null: false + t.text "error" + t.bigint "job_id", null: false + t.index ["job_id"], name: "index_solid_queue_failed_executions_on_job_id", unique: true + end + + create_table "solid_queue_jobs", force: :cascade do |t| + t.string "active_job_id" + t.text "arguments" + t.string "class_name", null: false + t.string "concurrency_key" + t.datetime "created_at", null: false + t.datetime "finished_at" + t.integer "priority", default: 0, null: false + t.string "queue_name", null: false + t.datetime "scheduled_at" + t.datetime "updated_at", null: false + t.index ["active_job_id"], name: "index_solid_queue_jobs_on_active_job_id" + t.index ["class_name"], name: "index_solid_queue_jobs_on_class_name" + t.index ["finished_at"], name: "index_solid_queue_jobs_on_finished_at" + t.index ["queue_name", "finished_at"], name: "index_solid_queue_jobs_for_filtering" + t.index ["scheduled_at", "finished_at"], name: "index_solid_queue_jobs_for_alerting" + end + + create_table "solid_queue_pauses", force: :cascade do |t| + t.datetime "created_at", null: false + t.string "queue_name", null: false + t.index ["queue_name"], name: "index_solid_queue_pauses_on_queue_name", unique: true + end + + create_table "solid_queue_processes", force: :cascade do |t| + t.datetime "created_at", null: false + t.string "hostname" + t.string "kind", null: false + t.datetime "last_heartbeat_at", null: false + t.text "metadata" + t.string "name", null: false + t.integer "pid", null: false + t.bigint "supervisor_id" + t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at" + t.index ["name", "supervisor_id"], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true + t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id" + end + + create_table "solid_queue_ready_executions", force: :cascade do |t| + t.datetime "created_at", null: false + t.bigint "job_id", null: false + t.integer "priority", default: 0, null: false + t.string "queue_name", null: false + t.index ["job_id"], name: "index_solid_queue_ready_executions_on_job_id", unique: true + t.index ["priority", "job_id"], name: "index_solid_queue_poll_all" + t.index ["queue_name", "priority", "job_id"], name: "index_solid_queue_poll_by_queue" + end + + create_table "solid_queue_recurring_executions", force: :cascade do |t| + t.datetime "created_at", null: false + t.bigint "job_id", null: false + t.datetime "run_at", null: false + t.string "task_key", null: false + t.index ["job_id"], name: "index_solid_queue_recurring_executions_on_job_id", unique: true + t.index ["task_key", "run_at"], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true + end + + create_table "solid_queue_recurring_tasks", force: :cascade do |t| + t.text "arguments" + t.string "class_name" + t.string "command", limit: 2048 + t.datetime "created_at", null: false + t.text "description" + t.string "key", null: false + t.integer "priority", default: 0 + t.string "queue_name" + t.string "schedule", null: false + t.boolean "static", default: true, null: false + t.datetime "updated_at", null: false + t.index ["key"], name: "index_solid_queue_recurring_tasks_on_key", unique: true + t.index ["static"], name: "index_solid_queue_recurring_tasks_on_static" + end + + create_table "solid_queue_scheduled_executions", force: :cascade do |t| + t.datetime "created_at", null: false + t.bigint "job_id", null: false + t.integer "priority", default: 0, null: false + t.string "queue_name", null: false + t.datetime "scheduled_at", null: false + t.index ["job_id"], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true + t.index ["scheduled_at", "priority", "job_id"], name: "index_solid_queue_dispatch_all" + end + + create_table "solid_queue_semaphores", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "expires_at", null: false + t.string "key", null: false + t.datetime "updated_at", null: false + t.integer "value", default: 1, null: false + t.index ["expires_at"], name: "index_solid_queue_semaphores_on_expires_at" + t.index ["key", "value"], name: "index_solid_queue_semaphores_on_key_and_value" + t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true + end + create_table "submissions", force: :cascade do |t| t.text "admin_notes" t.integer "challenge_id" @@ -158,6 +279,7 @@ t.datetime "created_at", null: false t.bigint "house_id", null: false t.datetime "last_synced_at" + t.string "name" t.integer "number", null: false t.datetime "updated_at", null: false t.index ["captain_id"], name: "index_teams_on_captain_id" @@ -227,6 +349,12 @@ add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "challenges", "users" add_foreign_key "location_pings", "teams" + add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade add_foreign_key "submissions", "challenges", on_delete: :nullify add_foreign_key "submissions", "teams", on_delete: :nullify add_foreign_key "submissions", "users", column: "reviewed_by_user_id", on_delete: :nullify diff --git a/test/fixtures/allowed_emails.yml b/test/fixtures/allowed_emails.yml index 763a75f..2d5e33b 100644 --- a/test/fixtures/allowed_emails.yml +++ b/test/fixtures/allowed_emails.yml @@ -1,6 +1,7 @@ # == Schema Information # # Table name: allowed_emails +# Database name: primary # # id :bigint not null, primary key # email :string not null diff --git a/test/fixtures/challenges.yml b/test/fixtures/challenges.yml index ded63c5..1490335 100644 --- a/test/fixtures/challenges.yml +++ b/test/fixtures/challenges.yml @@ -2,6 +2,7 @@ # == Schema Information # # Table name: challenges +# Database name: primary # # id :bigint not null, primary key # description :text diff --git a/test/fixtures/houses.yml b/test/fixtures/houses.yml index 840cc21..c23b56e 100644 --- a/test/fixtures/houses.yml +++ b/test/fixtures/houses.yml @@ -1,6 +1,7 @@ # == Schema Information # # Table name: houses +# Database name: primary # # id :bigint not null, primary key # color :string diff --git a/test/fixtures/location_pings.yml b/test/fixtures/location_pings.yml index 6564d6d..566dbb1 100644 --- a/test/fixtures/location_pings.yml +++ b/test/fixtures/location_pings.yml @@ -5,6 +5,7 @@ # == Schema Information # # Table name: location_pings +# Database name: primary # # id :bigint not null, primary key # latitude :decimal(10, 7) not null diff --git a/test/fixtures/notifications.yml b/test/fixtures/notifications.yml index cc47b2a..5b4feb6 100644 --- a/test/fixtures/notifications.yml +++ b/test/fixtures/notifications.yml @@ -1,6 +1,7 @@ # == Schema Information # # Table name: notifications +# Database name: primary # # id :bigint not null, primary key # link :string diff --git a/test/fixtures/submissions.yml b/test/fixtures/submissions.yml index c023118..77f2127 100644 --- a/test/fixtures/submissions.yml +++ b/test/fixtures/submissions.yml @@ -7,6 +7,7 @@ # == Schema Information # # Table name: submissions +# Database name: primary # # id :bigint not null, primary key # admin_notes :text diff --git a/test/fixtures/teams.yml b/test/fixtures/teams.yml index 6741c92..26b58ec 100644 --- a/test/fixtures/teams.yml +++ b/test/fixtures/teams.yml @@ -1,10 +1,12 @@ # == Schema Information # # Table name: teams +# Database name: primary # # id :bigint not null, primary key # airtag_slug :string # last_synced_at :datetime +# name :string # number :integer not null # created_at :datetime not null # updated_at :datetime not null diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index 81db979..3b5f280 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -1,6 +1,7 @@ # == Schema Information # # Table name: users +# Database name: primary # # id :bigint not null, primary key # avatar_url :string diff --git a/test/fixtures/zone_claims.yml b/test/fixtures/zone_claims.yml index 172161d..463f743 100644 --- a/test/fixtures/zone_claims.yml +++ b/test/fixtures/zone_claims.yml @@ -5,6 +5,7 @@ # == Schema Information # # Table name: zone_claims +# Database name: primary # # id :bigint not null, primary key # created_at :datetime not null diff --git a/test/fixtures/zones.yml b/test/fixtures/zones.yml index 063f229..5c10e89 100644 --- a/test/fixtures/zones.yml +++ b/test/fixtures/zones.yml @@ -4,6 +4,7 @@ # == Schema Information # # Table name: zones +# Database name: primary # # id :bigint not null, primary key # coordinates :string diff --git a/test/models/allowed_email_test.rb b/test/models/allowed_email_test.rb index 3b3334f..c41ff01 100644 --- a/test/models/allowed_email_test.rb +++ b/test/models/allowed_email_test.rb @@ -3,6 +3,7 @@ # == Schema Information # # Table name: allowed_emails +# Database name: primary # # id :bigint not null, primary key # email :string not null diff --git a/test/models/challenge_test.rb b/test/models/challenge_test.rb index 482d5ac..9a32bd8 100644 --- a/test/models/challenge_test.rb +++ b/test/models/challenge_test.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: challenges +# Database name: primary # # id :bigint not null, primary key # description :text diff --git a/test/models/house_test.rb b/test/models/house_test.rb index 97dfe9e..eff1a92 100644 --- a/test/models/house_test.rb +++ b/test/models/house_test.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: houses +# Database name: primary # # id :bigint not null, primary key # color :string diff --git a/test/models/location_ping_test.rb b/test/models/location_ping_test.rb index 2587852..0b5ab39 100644 --- a/test/models/location_ping_test.rb +++ b/test/models/location_ping_test.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: location_pings +# Database name: primary # # id :bigint not null, primary key # latitude :decimal(10, 7) not null diff --git a/test/models/notification_test.rb b/test/models/notification_test.rb index 7352263..ec5a5b1 100644 --- a/test/models/notification_test.rb +++ b/test/models/notification_test.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: notifications +# Database name: primary # # id :bigint not null, primary key # link :string diff --git a/test/models/submission_test.rb b/test/models/submission_test.rb index 1b740a0..ffc6dc5 100644 --- a/test/models/submission_test.rb +++ b/test/models/submission_test.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: submissions +# Database name: primary # # id :bigint not null, primary key # admin_notes :text diff --git a/test/models/team_test.rb b/test/models/team_test.rb index 523aada..b2ab3ae 100644 --- a/test/models/team_test.rb +++ b/test/models/team_test.rb @@ -1,10 +1,12 @@ # == Schema Information # # Table name: teams +# Database name: primary # # id :bigint not null, primary key # airtag_slug :string # last_synced_at :datetime +# name :string # number :integer not null # created_at :datetime not null # updated_at :datetime not null diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 86323ed..8acb151 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: users +# Database name: primary # # id :bigint not null, primary key # avatar_url :string diff --git a/test/models/zone_test.rb b/test/models/zone_test.rb index 60beca0..e64b2da 100644 --- a/test/models/zone_test.rb +++ b/test/models/zone_test.rb @@ -1,6 +1,7 @@ # == Schema Information # # Table name: zones +# Database name: primary # # id :bigint not null, primary key # coordinates :string