From 15daa2bf05f1f42920c233bda26d66d3059ddf49 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Mon, 15 Dec 2025 15:26:22 +0100 Subject: [PATCH 1/5] Limit asignees to 10 per card --- app/models/assignment.rb | 11 +++++++++ app/models/card/assignable.rb | 8 ++++--- test/models/assignment_test.rb | 37 ++++++++++++++++++++++++++--- test/models/card/assignable_test.rb | 27 +++++++++++++++++++++ 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/app/models/assignment.rb b/app/models/assignment.rb index f60eb974db..02f81bc253 100644 --- a/app/models/assignment.rb +++ b/app/models/assignment.rb @@ -1,7 +1,18 @@ class Assignment < ApplicationRecord + LIMIT = 10 + belongs_to :account, default: -> { card.account } belongs_to :card, touch: true belongs_to :assignee, class_name: "User" belongs_to :assigner, class_name: "User" + + validate :within_limit, on: :create + + private + def within_limit + if card.assignments.count >= LIMIT + errors.add(:base, "Card already has the maximum of #{LIMIT} assignees") + end + end end diff --git a/app/models/card/assignable.rb b/app/models/card/assignable.rb index be0dd257e9..6320faeb37 100644 --- a/app/models/card/assignable.rb +++ b/app/models/card/assignable.rb @@ -24,10 +24,12 @@ def assigned? private def assign(user) - assignments.create! assignee: user, assigner: Current.user - watch_by user + assignment = assignments.create assignee: user, assigner: Current.user - track_event :assigned, assignee_ids: [ user.id ] + if assignment.persisted? + watch_by user + track_event :assigned, assignee_ids: [ user.id ] + end rescue ActiveRecord::RecordNotUnique # Already assigned end diff --git a/test/models/assignment_test.rb b/test/models/assignment_test.rb index ca8f09a34f..3ac309619d 100644 --- a/test/models/assignment_test.rb +++ b/test/models/assignment_test.rb @@ -1,7 +1,38 @@ require "test_helper" class AssignmentTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end + test "create" do + card = cards(:text) + assignment = card.assignments.create!(assignee: users(:david), assigner: users(:jason)) + + assert_equal users(:david), assignment.assignee + assert_equal users(:jason), assignment.assigner + assert_equal card, assignment.card + end + + test "create cannot exceed assignee limit" do + card = cards(:logo) + board = card.board + account = card.account + + card.assignments.delete_all + + Assignment::LIMIT.times do |i| + identity = Identity.create!(email_address: "limit_test_#{i}@example.com") + user = account.users.create!(identity: identity, name: "Limit Test User #{i}", role: :member) + user.accesses.find_or_create_by!(board: board) + card.assignments.create!(assignee: user, assigner: users(:david)) + end + + assert_equal Assignment::LIMIT, card.assignments.count + + identity = Identity.create!(email_address: "over_limit@example.com") + extra_user = account.users.create!(identity: identity, name: "Over Limit User", role: :member) + extra_user.accesses.find_or_create_by!(board: board) + + assignment = card.assignments.build(assignee: extra_user, assigner: users(:david)) + + assert_not assignment.valid? + assert_includes assignment.errors[:base], "Card already has the maximum of #{Assignment::LIMIT} assignees" + end end diff --git a/test/models/card/assignable_test.rb b/test/models/card/assignable_test.rb index 9d0c1f1c5e..6ee8a164d2 100644 --- a/test/models/card/assignable_test.rb +++ b/test/models/card/assignable_test.rb @@ -12,4 +12,31 @@ class Card::AssignableTest < ActiveSupport::TestCase assert cards(:layout).assigned_to?(users(:kevin)) assert cards(:layout).watched_by?(users(:kevin)) end + + test "toggle_assignment does not add assignee when at limit" do + card = cards(:logo) + board = card.board + account = card.account + + card.assignments.delete_all + + Assignment::LIMIT.times do |i| + identity = Identity.create!(email_address: "toggle_test_#{i}@example.com") + user = account.users.create!(identity: identity, name: "Toggle Test User #{i}", role: :member) + user.accesses.find_or_create_by!(board: board) + card.assignments.create!(assignee: user, assigner: users(:david)) + end + + identity = Identity.create!(email_address: "toggle_over@example.com") + extra_user = account.users.create!(identity: identity, name: "Toggle Over User", role: :member) + extra_user.accesses.find_or_create_by!(board: board) + + with_current_user(:david) do + assert_no_difference "card.assignments.count" do + card.toggle_assignment(extra_user) + end + end + + assert_not card.reload.assigned_to?(extra_user) + end end From 15cb1f7fe19896b1caf01889eb9a1d36a1d608a1 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Mon, 15 Dec 2025 16:00:11 +0100 Subject: [PATCH 2/5] Add UI to prevent assigning more than 10 asignees --- .../cards/assignments_controller.rb | 15 +++++++---- .../assignment_limit_controller.js | 26 +++++++++++++++++++ app/views/cards/assignments/new.html.erb | 15 ++++++++--- 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 app/javascript/controllers/assignment_limit_controller.js diff --git a/app/controllers/cards/assignments_controller.rb b/app/controllers/cards/assignments_controller.rb index 396fd3a5fb..3e9fe1d481 100644 --- a/app/controllers/cards/assignments_controller.rb +++ b/app/controllers/cards/assignments_controller.rb @@ -8,11 +8,16 @@ def new end def create - @card.toggle_assignment @board.users.active.find(params[:assignee_id]) - - respond_to do |format| - format.turbo_stream - format.json { head :no_content } + if @card.toggle_assignment @board.users.active.find(params[:assignee_id]) + respond_to do |format| + format.turbo_stream + format.json { head :no_content } + end + else + respond_to do |format| + format.turbo_stream + format.json { head :unprocessable_entity } + end end end end diff --git a/app/javascript/controllers/assignment_limit_controller.js b/app/javascript/controllers/assignment_limit_controller.js new file mode 100644 index 0000000000..06f630c599 --- /dev/null +++ b/app/javascript/controllers/assignment_limit_controller.js @@ -0,0 +1,26 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static values = { limit: Number, count: Number } + static targets = ["unassigned", "limitMessage"] + + connect() { + this.updateState() + } + + countValueChanged() { + this.updateState() + } + + updateState() { + const atLimit = this.countValue >= this.limitValue + + this.unassignedTargets.forEach(el => { + el.hidden = atLimit + }) + + if (this.hasLimitMessageTarget) { + this.limitMessageTarget.hidden = !atLimit + } + } +} diff --git a/app/views/cards/assignments/new.html.erb b/app/views/cards/assignments/new.html.erb index 6692a53f01..9267875cc5 100644 --- a/app/views/cards/assignments/new.html.erb +++ b/app/views/cards/assignments/new.html.erb @@ -1,10 +1,12 @@ <%= turbo_frame_tag @card, :assignment do %> <%= tag.div class: "max-width full-width", data: { action: "turbo:before-cache@document->dialog#close dialog:show@document->navigable-list#reset keydown->navigable-list#navigate filter:changed->navigable-list#reset", - controller: "filter navigable-list", + controller: "filter navigable-list assignment-limit", dialog_target: "dialog", navigable_list_focus_on_selection_value: false, - navigable_list_actionable_items_value: true } do %> + navigable_list_actionable_items_value: true, + assignment_limit_limit_value: Assignment::LIMIT, + assignment_limit_count_value: @card.assignments.count } do %>
Assign this to… @@ -19,7 +21,14 @@ <%= Current.user.name %> <% end %> <%= render collection: @assigned_to, partial: "user", locals: { card: @card } %> - <%= render collection: @users, partial: "user", locals: { card: @card } %> + + <% @users.each do |user| %> + + <%= render "user", card: @card, user: user %> + + <% end %> <% end %> <% end %> From 140399aa6c3e265a0fa33d329d75b08d47606821 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 15 Dec 2025 16:49:54 -0600 Subject: [PATCH 3/5] Style max notice --- app/assets/stylesheets/popup.css | 9 +++++++++ app/views/cards/assignments/new.html.erb | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/popup.css b/app/assets/stylesheets/popup.css index 0213d04cbe..189cfef5b9 100644 --- a/app/assets/stylesheets/popup.css +++ b/app/assets/stylesheets/popup.css @@ -36,6 +36,15 @@ } } + .popup__footer { + border-block-start: 1px solid var(--color-ink-lightest); + color: var(--card-color); + margin-block-start: var(--popup-item-padding-inline); + padding: var(--popup-item-padding-inline) var(--popup-item-padding-inline) 0; + text-align: center; + text-transform: initial; + } + .popup__title { font-weight: 800; white-space: nowrap; diff --git a/app/views/cards/assignments/new.html.erb b/app/views/cards/assignments/new.html.erb index 9267875cc5..dc6fe1166b 100644 --- a/app/views/cards/assignments/new.html.erb +++ b/app/views/cards/assignments/new.html.erb @@ -21,14 +21,15 @@ <%= Current.user.name %> <% end %> <%= render collection: @assigned_to, partial: "user", locals: { card: @card } %> - <% @users.each do |user| %> <%= render "user", card: @card, user: user %> <% end %> + + <% end %> <% end %> From b0f9960b8a0fae6a89f91e207a56b842f57d0f36 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Tue, 16 Dec 2025 13:36:40 +0100 Subject: [PATCH 4/5] Raise the limit to 100 --- app/models/assignment.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/assignment.rb b/app/models/assignment.rb index 02f81bc253..75ffde959b 100644 --- a/app/models/assignment.rb +++ b/app/models/assignment.rb @@ -1,5 +1,5 @@ class Assignment < ApplicationRecord - LIMIT = 10 + LIMIT = 100 belongs_to :account, default: -> { card.account } belongs_to :card, touch: true From 52bdc8d1a726c110062f988352a9fedb5f5dd1fc Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Tue, 16 Dec 2025 18:55:45 +0100 Subject: [PATCH 5/5] Fix typo --- app/views/cards/assignments/new.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/cards/assignments/new.html.erb b/app/views/cards/assignments/new.html.erb index dc6fe1166b..048bacf62a 100644 --- a/app/views/cards/assignments/new.html.erb +++ b/app/views/cards/assignments/new.html.erb @@ -29,7 +29,7 @@ <% end %> <% end %>