diff --git a/app/assets/stylesheets/popup.css b/app/assets/stylesheets/popup.css index 02ae8969e5..2f34436448 100644 --- a/app/assets/stylesheets/popup.css +++ b/app/assets/stylesheets/popup.css @@ -219,4 +219,28 @@ } } } + + /* Board menu column counts + /* -------------------------------------------------------------------------- */ + + .board-menu-counts { + display: inline-flex; + gap: 0.25ch; + margin-inline-start: auto; + padding-inline-start: 1ch; + flex-shrink: 0; + } + + .board-menu-count { + --card-color: var(--color-ink-light); + + background-color: color-mix(in srgb, var(--card-color) 20%, transparent); + border-radius: 0.3em; + color: color-mix(in srgb, var(--card-color) 90%, var(--color-ink)); + font-size: var(--text-xx-small); + font-weight: 600; + min-inline-size: 1.8em; + padding: 0.1em 0.4em; + text-align: center; + } } diff --git a/app/controllers/my/menus_controller.rb b/app/controllers/my/menus_controller.rb index 24fa2b085a..c970ec0c4e 100644 --- a/app/controllers/my/menus_controller.rb +++ b/app/controllers/my/menus_controller.rb @@ -1,7 +1,7 @@ class My::MenusController < ApplicationController def show @filters = Current.user.filters.all - @boards = Current.user.boards.ordered_by_recently_accessed + @boards = Current.user.boards.ordered_by_recently_accessed.includes(columns: :cards) @tags = Current.account.tags.all.alphabetically @users = Current.account.users.active.alphabetically @accounts = Current.identity.accounts.active diff --git a/app/helpers/my/menu_helper.rb b/app/helpers/my/menu_helper.rb index 753bad6a2c..25544355ca 100644 --- a/app/helpers/my/menu_helper.rb +++ b/app/helpers/my/menu_helper.rb @@ -19,7 +19,10 @@ def jump_field_tag def my_menu_board_item(board) my_menu_item("board", board) do - link_to(tag.span(board.name, class: "overflow-ellipsis"), board, class: "popup__btn btn") + link_to(board, class: "popup__btn btn") do + tag.span(board.name, class: "overflow-ellipsis") + + board_column_counts_tag(board) + end end end @@ -53,4 +56,28 @@ def my_menu_item(item, record) icon_tag(item, class: "popup__icon") + yield end end + + private + def board_column_counts_tag(board) + columns = board.columns.sorted + maybe_count = board.cards.awaiting_triage.size + + return tag.span if columns.empty? && maybe_count.zero? + + tag.span(class: "board-menu-counts") do + counts = [] + counts << board_count_tag(maybe_count, title: "Maybe?") if maybe_count > 0 + counts += columns.filter_map do |column| + count = column.cards.active.size + board_count_tag(count, color: column.color, title: column.name) if count > 0 + end + safe_join(counts) + end + end + + def board_count_tag(count, color: nil, title: nil) + formatted_count = count > 99 ? "99+" : count.to_s + style = "--card-color: #{color};" if color + tag.span(formatted_count, class: "board-menu-count", style: style, title: title) + end end diff --git a/test/helpers/my/menu_helper_test.rb b/test/helpers/my/menu_helper_test.rb new file mode 100644 index 0000000000..5694884eff --- /dev/null +++ b/test/helpers/my/menu_helper_test.rb @@ -0,0 +1,63 @@ +require "test_helper" + +class My::MenuHelperTest < ActionView::TestCase + setup do + @board = boards(:writebook) + end + + test "board_column_counts_tag shows awaiting triage count first" do + html = send(:board_column_counts_tag, @board) + + # First count should be awaiting triage (no color style, has "Maybe?" title) + first_count = html.match(/(\d+)<\/span>/) + assert first_count, "Should have a count without color for awaiting triage" + assert_equal @board.cards.awaiting_triage.count.to_s, first_count[1] + end + + test "board_column_counts_tag shows column counts with colors" do + html = send(:board_column_counts_tag, @board) + + # Verify that at least one column with cards shows its color + assert_match /--card-color:/, html + end + + test "board_column_counts_tag excludes columns with zero cards" do + html = send(:board_column_counts_tag, @board) + + # Count the number of colored badges in the output + colored_badges = html.scan(/--card-color:/).count + + # Count columns that have active cards + columns_with_cards = @board.columns.sorted.count { |c| c.cards.active.size > 0 } + + assert_equal columns_with_cards, colored_badges + end + + test "board_column_counts_tag returns empty span when no columns and no cards" do + board = Board.new + board.define_singleton_method(:columns) { Column.none } + board.define_singleton_method(:cards) { Card.none } + + html = send(:board_column_counts_tag, board) + + assert_equal "", html + end + + test "board_count_tag formats count over 99 as 99+" do + html = send(:board_count_tag, 150) + + assert_match "99+", html + end + + test "board_count_tag includes color style when provided" do + html = send(:board_count_tag, 5, color: "var(--color-card-3)") + + assert_match "--card-color: var(--color-card-3);", html + end + + test "board_count_tag omits style when no color provided" do + html = send(:board_count_tag, 5) + + assert_no_match(/style=/, html) + end +end