From 3ada6f271a39c6538ae2b6862122e57a4d4ba3b9 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 29 Oct 2025 15:41:17 +0100 Subject: [PATCH] Models for column positioning --- app/models/column.rb | 2 + app/models/concerns/column/positioned.rb | 35 +++++++++++ app/views/collections/show/_columns.html.erb | 2 +- .../20251029142418_add_position_to_columns.rb | 9 +++ db/schema.rb | 3 +- db/schema_cache.yml | 12 +++- test/models/column/positioned_test.rb | 62 +++++++++++++++++++ 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 app/models/concerns/column/positioned.rb create mode 100644 db/migrate/20251029142418_add_position_to_columns.rb create mode 100644 test/models/column/positioned_test.rb diff --git a/app/models/column.rb b/app/models/column.rb index 27bfa8a06..7585c39be 100644 --- a/app/models/column.rb +++ b/app/models/column.rb @@ -1,4 +1,6 @@ class Column < ApplicationRecord + include Positioned + belongs_to :collection, touch: true has_many :cards, dependent: :nullify diff --git a/app/models/concerns/column/positioned.rb b/app/models/concerns/column/positioned.rb new file mode 100644 index 000000000..a3864dab0 --- /dev/null +++ b/app/models/concerns/column/positioned.rb @@ -0,0 +1,35 @@ +module Column::Positioned + extend ActiveSupport::Concern + + included do + scope :sorted, -> { order(position: :asc) } + + before_create :set_position + end + + def move_left + if column = collection.columns.where("position < ?", position).sorted.last + swap_position_with(column) + end + end + + def move_right + if column = collection.columns.where("position > ?", position).sorted.first + swap_position_with(column) + end + end + + private + def set_position + max_position = collection.columns.maximum(:position) + self.position = max_position + 1 + end + + def swap_position_with(other_column) + transaction do + other_column.position, self.position = self.position, other_column.position + other_column.save! + save! + end + end +end diff --git a/app/views/collections/show/_columns.html.erb b/app/views/collections/show/_columns.html.erb index 585093dc6..8c12b164a 100644 --- a/app/views/collections/show/_columns.html.erb +++ b/app/views/collections/show/_columns.html.erb @@ -16,7 +16,7 @@ <%= render "collections/show/stream", collection: collection, page: page %>
- <%= render partial: "collections/show/column", collection: collection.columns, cached: true %> + <%= render partial: "collections/show/column", collection: collection.columns.sorted, cached: true %> <%= render "collections/show/closed", collection: collection %> <%= render "collections/show/menu/columns", collection: collection %> diff --git a/db/migrate/20251029142418_add_position_to_columns.rb b/db/migrate/20251029142418_add_position_to_columns.rb new file mode 100644 index 000000000..ba16d7831 --- /dev/null +++ b/db/migrate/20251029142418_add_position_to_columns.rb @@ -0,0 +1,9 @@ +class AddPositionToColumns < ActiveRecord::Migration[8.2] + def change + add_column :columns, :position, :integer, default: 0 + + execute "UPDATE columns SET position = 0" + + change_column_null :columns, :position, false + end +end diff --git a/db/schema.rb b/db/schema.rb index 71c233fcb..cef4d628e 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.2].define(version: 2025_10_29_104115) do +ActiveRecord::Schema[8.2].define(version: 2025_10_29_142418) do create_table "accesses", force: :cascade do |t| t.datetime "accessed_at" t.integer "collection_id", null: false @@ -191,6 +191,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_10_29_104115) do t.string "color", null: false t.datetime "created_at", null: false t.string "name", null: false + t.integer "position", default: 0, null: false t.datetime "updated_at", null: false t.index ["collection_id"], name: "index_columns_on_collection_id" end diff --git a/db/schema_cache.yml b/db/schema_cache.yml index f7da6da99..ad614480d 100644 --- a/db/schema_cache.yml +++ b/db/schema_cache.yml @@ -562,6 +562,16 @@ columns: - *5 - *6 - *10 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: position + cast_type: *3 + sql_type_metadata: *4 + 'null': false + default: 0 + default_function: + collation: + comment: - *9 comments: - *22 @@ -2818,4 +2828,4 @@ indexes: nulls_not_distinct: comment: valid: true -version: 20251029104115 +version: 20251029142418 diff --git a/test/models/column/positioned_test.rb b/test/models/column/positioned_test.rb new file mode 100644 index 000000000..4d4355cfc --- /dev/null +++ b/test/models/column/positioned_test.rb @@ -0,0 +1,62 @@ +require "test_helper" + +class Column::PositionedTest < ActiveSupport::TestCase + test "auto position new columns" do + collection = collections(:writebook) + max_position = collection.columns.maximum(:position) + + new_column = collection.columns.create!(name: "New Column", color: "#000000") + + assert_equal max_position + 1, new_column.position + end + + test "move column to the left" do + collection = collections(:writebook) + columns = collection.columns.sorted.to_a + + column_a = columns[0] + column_b = columns[1] + original_position_a = column_a.position + original_position_b = column_b.position + + column_b.move_left + + assert_equal original_position_b, column_a.reload.position + assert_equal original_position_a, column_b.reload.position + end + + test "move left when already at leftmost position" do + collection = collections(:writebook) + leftmost_column = collection.columns.sorted.first + original_position = leftmost_column.position + + leftmost_column.move_left + + assert_equal original_position, leftmost_column.reload.position + end + + test "move column to the right" do + collection = collections(:writebook) + columns = collection.columns.sorted.to_a + + column_a = columns[0] + column_b = columns[1] + original_position_a = column_a.position + original_position_b = column_b.position + + column_a.move_right + + assert_equal original_position_b, column_a.reload.position + assert_equal original_position_a, column_b.reload.position + end + + test "move right when already at rightmost position" do + collection = collections(:writebook) + rightmost_column = collection.columns.sorted.last + original_position = rightmost_column.position + + rightmost_column.move_right + + assert_equal original_position, rightmost_column.reload.position + end +end