diff --git a/app/controllers/buckets_controller.rb b/app/controllers/buckets_controller.rb index 63357bb92..32df270e0 100644 --- a/app/controllers/buckets_controller.rb +++ b/app/controllers/buckets_controller.rb @@ -33,7 +33,8 @@ class BucketsController < ApplicationController end def bucket_params - params.expect(bucket: [ :name, :all_access ]).with_defaults(all_access: false) + params.expect(bucket: [ :name, :all_access, :workflow_id ]).with_defaults(all_access: false) + params.require(:bucket).permit(:name, :all_access, :workflow_id) end def grantees diff --git a/app/controllers/workflows_controller.rb b/app/controllers/workflows_controller.rb index edc89a008..390cedcdb 100644 --- a/app/controllers/workflows_controller.rb +++ b/app/controllers/workflows_controller.rb @@ -12,7 +12,7 @@ class WorkflowsController < ApplicationController def create @workflow = Current.account.workflows.create! workflow_params # FIXME: this should definitely change. - [ "Maybe?", "Not now", "Done" ].each { |name| @workflow.stages.create! name: name } + [ "Triage", "In progress", "On Hold", "Review" ].each { |name| @workflow.stages.create! name: name } redirect_to workflows_path end diff --git a/app/models/bubble.rb b/app/models/bubble.rb index 441071f5a..c90ac1075 100644 --- a/app/models/bubble.rb +++ b/app/models/bubble.rb @@ -56,7 +56,7 @@ class Bubble < ApplicationRecord end def assign_initial_stage - if workflow_stage = bucket.account.workflows.first&.stages&.first + if workflow_stage = bucket.workflow&.stages&.first self.stage = workflow_stage save! touch: false track_event :staged, stage_id: workflow_stage.id, stage_name: workflow_stage.name diff --git a/app/models/bucket.rb b/app/models/bucket.rb index 16f1f1d63..2fcf38e13 100644 --- a/app/models/bucket.rb +++ b/app/models/bucket.rb @@ -3,21 +3,19 @@ class Bucket < ApplicationRecord belongs_to :account belongs_to :creator, class_name: "User", default: -> { Current.user } + belongs_to :workflow, optional: true has_many :bubbles, dependent: :destroy has_many :tags, -> { distinct }, through: :bubbles validates_presence_of :name - after_create :ensure_workflow_exists + after_save :update_bubbles_workflow, if: :saved_change_to_workflow_id? scope :alphabetically, -> { order(name: :asc) } private - def ensure_workflow_exists - unless account.workflows.exists? - workflow = account.workflows.create!(name: "Default Workflow") - [ "Maybe?", "Not now", "Done" ].each { |name| workflow.stages.create!(name: name) } - end + def update_bubbles_workflow + bubbles.update_all(stage_id: workflow&.stages&.first&.id) end end diff --git a/app/views/buckets/edit.html.erb b/app/views/buckets/edit.html.erb index 2f024c221..ab671d304 100644 --- a/app/views/buckets/edit.html.erb +++ b/app/views/buckets/edit.html.erb @@ -67,7 +67,7 @@ <% end %> - @@ -75,3 +75,12 @@ <%= link_to "Cancel and go back", bubbles_path(bucket_ids: [ @bucket ]), data: { form_target: "cancel", turbo_frame: "_top" }, hidden: true %> <% end %> + +
+ Choose a workflow +

Use a workflow to track progress in this collection.

+ <%= form_with model: @bucket, local: true, data: { controller: "form" } do |form| %> + <%= form.select :workflow_id, Current.account.workflows.map { |w| [w.name, w.id] }, { include_blank: "Choose…" }, class: "input input--select full-width", data: { action: "change->form#submit" } %> + <% end %> +

<%= link_to "Manage workflows", workflows_path, class: "btn btn--plain txt-link" %>

+
diff --git a/db/migrate/20250327191456_add_workflow_to_buckets.rb b/db/migrate/20250327191456_add_workflow_to_buckets.rb new file mode 100644 index 000000000..4efe294f2 --- /dev/null +++ b/db/migrate/20250327191456_add_workflow_to_buckets.rb @@ -0,0 +1,5 @@ +class AddWorkflowToBuckets < ActiveRecord::Migration[8.1] + def change + add_reference :buckets, :workflow, null: true, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 6a65a7709..ef0105d28 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: 2025_03_18_005138) do +ActiveRecord::Schema[8.1].define(version: 2025_03_27_191456) do create_table "accesses", force: :cascade do |t| t.integer "bucket_id", null: false t.datetime "created_at", null: false @@ -123,8 +123,10 @@ ActiveRecord::Schema[8.1].define(version: 2025_03_18_005138) do t.integer "creator_id", null: false t.string "name", null: false t.datetime "updated_at", null: false + t.integer "workflow_id" t.index ["account_id"], name: "index_buckets_on_account_id" t.index ["creator_id"], name: "index_buckets_on_creator_id" + t.index ["workflow_id"], name: "index_buckets_on_workflow_id" end create_table "buckets_filters", id: false, force: :cascade do |t| @@ -220,7 +222,6 @@ ActiveRecord::Schema[8.1].define(version: 2025_03_18_005138) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "user_id", null: false - t.index ["bubble_id", "user_id"], name: "index_pins_on_bubble_id_and_user_id", unique: true t.index ["bubble_id"], name: "index_pins_on_bubble_id" t.index ["user_id"], name: "index_pins_on_user_id" end @@ -324,6 +325,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_03_18_005138) do 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 "bubbles", "workflow_stages", column: "stage_id" + add_foreign_key "buckets", "workflows" add_foreign_key "events", "bubbles" add_foreign_key "events", "event_summaries", column: "summary_id" add_foreign_key "messages", "bubbles" diff --git a/db/schema_cache.yml b/db/schema_cache.yml index f0550d59e..40d083029 100644 --- a/db/schema_cache.yml +++ b/db/schema_cache.yml @@ -519,6 +519,16 @@ columns: default_function: collation: comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: workflow_id + cast_type: *1 + sql_type_metadata: *2 + 'null': true + default: + default_function: + collation: + comment: buckets_filters: - *18 - *21 @@ -1355,6 +1365,22 @@ indexes: comment: valid: true buckets: + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: buckets + name: index_buckets_on_workflow_id + unique: false + columns: + - workflow_id + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: buckets name: index_buckets_on_creator_id @@ -1710,23 +1736,6 @@ indexes: comment: valid: true pins: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: pins - name: index_pins_on_bubble_id_and_user_id - unique: true - columns: - - bubble_id - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: pins name: index_pins_on_user_id @@ -2062,4 +2071,4 @@ indexes: nulls_not_distinct: comment: valid: true -version: 20250318005138 +version: 20250327191456 diff --git a/test/controllers/buckets_controller_test.rb b/test/controllers/buckets_controller_test.rb index 3882e0480..0202e75cf 100644 --- a/test/controllers/buckets_controller_test.rb +++ b/test/controllers/buckets_controller_test.rb @@ -27,11 +27,17 @@ class BucketsControllerTest < ActionDispatch::IntegrationTest end test "update" do - patch bucket_url(buckets(:writebook)), params: { bucket: { name: "Writebook bugs" }, user_ids: users(:david, :jz).pluck(:id) } + patch bucket_url(buckets(:writebook)), params: { + bucket: { + name: "Writebook bugs", + all_access: false + }, + user_ids: users(:david, :jz).pluck(:id) + } assert_redirected_to bubbles_path(bucket_ids: [ buckets(:writebook) ]) assert_equal "Writebook bugs", buckets(:writebook).reload.name - assert_equal users(:david, :jz), buckets(:writebook).users + assert_equal users(:david, :jz).sort, buckets(:writebook).users.sort assert_not buckets(:writebook).all_access? end diff --git a/test/fixtures/buckets.yml b/test/fixtures/buckets.yml index b9c31d24a..d15ada078 100644 --- a/test/fixtures/buckets.yml +++ b/test/fixtures/buckets.yml @@ -3,3 +3,4 @@ writebook: account: 37s creator: david all_access: true + workflow: qa diff --git a/test/fixtures/workflow/stages.yml b/test/fixtures/workflow/stages.yml index 1067c0e33..7563597fc 100644 --- a/test/fixtures/workflow/stages.yml +++ b/test/fixtures/workflow/stages.yml @@ -1,11 +1,15 @@ -qa_maybe: - name: Maybe? +qa_triage: + name: Triage workflow: qa -qa_not_now: - name: Not now +qa_in_progress: + name: In progress workflow: qa -qa_done: - name: Done +qa_on_hold: + name: On Hold + workflow: qa + +qa_review: + name: Review workflow: qa diff --git a/test/integration/bubble_messages.rb b/test/integration/bubble_messages.rb index f61751f4f..8b4ceea8c 100644 --- a/test/integration/bubble_messages.rb +++ b/test/integration/bubble_messages.rb @@ -34,14 +34,14 @@ class BubbleMessagesTest < ActionDispatch::IntegrationTest assert_equal "assigned", bubble.messages.last.messageable.events.last.action # Stage it - post bucket_bubble_stagings_url(buckets(:writebook), bubble), params: { stage_id: workflow_stages(:qa_maybe).id } + post bucket_bubble_stagings_url(buckets(:writebook), bubble), params: { stage_id: workflow_stages(:qa_triage).id } assert_equal 3, bubble.messages.count assert_predicate bubble.messages.last, :event_summary? assert_equal 2, bubble.messages.last.event_summary.events.count assert_equal "staged", bubble.messages.last.messageable.events.last.action # Unstage it - post bucket_bubble_stagings_url(buckets(:writebook), bubble), params: { stage_id: workflow_stages(:qa_maybe).id } + post bucket_bubble_stagings_url(buckets(:writebook), bubble), params: { stage_id: workflow_stages(:qa_triage).id } assert_equal 3, bubble.messages.count assert_predicate bubble.messages.last, :event_summary? assert_equal 3, bubble.messages.last.event_summary.events.count diff --git a/test/models/filter_test.rb b/test/models/filter_test.rb index 20740f6fd..b5df283ad 100644 --- a/test/models/filter_test.rb +++ b/test/models/filter_test.rb @@ -5,10 +5,11 @@ class FilterTest < ActiveSupport::TestCase Current.set session: sessions(:david) do @new_bucket = accounts("37s").buckets.create! name: "Inaccessible Bucket" @new_bubble = @new_bucket.bubbles.create! + @new_bubble.update!(stage: workflow_stages(:qa_triage)) bubbles(:layout).capture Comment.new(body: "I hate haggis") bubbles(:logo).capture Comment.new(body: "I love haggis") - bubbles(:logo).update(stage: workflow_stages(:qa_maybe)) + bubbles(:logo).update(stage: workflow_stages(:qa_triage)) end assert_not_includes users(:kevin).filters.new.bubbles, @new_bubble @@ -19,7 +20,7 @@ class FilterTest < ActiveSupport::TestCase filter = users(:david).filters.new creator_ids: [ users(:david).id ], tag_ids: [ tags(:mobile).id ] assert_equal [ bubbles(:layout) ], filter.bubbles - filter = users(:david).filters.new stage_ids: [ workflow_stages(:qa_maybe).id ] + filter = users(:david).filters.new stage_ids: [ workflow_stages(:qa_triage).id ] assert_equal [ bubbles(:logo), @new_bubble ], filter.bubbles filter = users(:david).filters.new assignment_status: "unassigned", bucket_ids: [ @new_bucket.id ] @@ -105,8 +106,8 @@ class FilterTest < ActiveSupport::TestCase test "summary" do assert_equal "Most discussed, tagged #Mobile, and assigned to JZ ", filters(:jz_assignments).summary - filters(:jz_assignments).update!(stages: workflow_stages(:qa_maybe, :qa_not_now)) - assert_equal "Most discussed, tagged #Mobile, assigned to JZ, and staged in Maybe? or Not now ", filters(:jz_assignments).summary + filters(:jz_assignments).update!(stages: workflow_stages(:qa_triage, :qa_in_progress)) + assert_equal "Most discussed, tagged #Mobile, assigned to JZ, and staged in Triage or In progress ", filters(:jz_assignments).summary filters(:jz_assignments).update!(stages: [], assignees: [], tags: [], buckets: [ buckets(:writebook) ]) assert_equal "Most discussed in Writebook", filters(:jz_assignments).summary