Merge branch 'main' into fizzy-menu-everywhere

* main:
  Add a focus effect when hovering columns
  Fix up hotkeys
  test: backfill hotkeys helper tests
  refactor: remove unused routes associated with "resource :account"
  test: backfill account route tests to prepare for refactor
  Alphabetically order concerns (#992)
  Copy
  Don't show the filter if there aren't any tags
  Add clearer labels for sorting options
  Prevent deleting the last collection
  Don't show "All collections" when there's only the default collection
  Rename engagement columns
This commit is contained in:
Andy Smith
2025-08-28 12:24:14 -05:00
31 changed files with 159 additions and 63 deletions
+13
View File
@@ -119,6 +119,19 @@
}
}
.cards--related:not(:hover):not(.drag-and-drop__hover-container) {
.card {
box-shadow: 0 0 1px var(--color-ink-medium);
opacity: 0.9;
transition: box-shadow 450ms ease-in-out, opacity 150ms ease-in-out;
@media (prefers-color-scheme: dark) {
opacity: 0.8;
transition: opacity 150ms ease-in-out;
}
}
}
/* Column elements
/* ------------------------------------------------------------------------ */
@@ -1,4 +1,4 @@
class Accounts::EntropyConfigurationsController < ApplicationController
class Account::EntropyConfigurationsController < ApplicationController
def update
Entropy::Configuration.default.update!(entropy_configuration_params)
@@ -1,4 +1,4 @@
class Accounts::JoinCodesController < ApplicationController
class Account::JoinCodesController < ApplicationController
def show
render svg: RQRCode::QRCode.new(join_url(Account.sole.join_code)).as_svg(viewbox: true, fill: :white, color: :black)
end
@@ -1,4 +1,4 @@
class Accounts::SettingsController < ApplicationController
class Account::SettingsController < ApplicationController
include FilterScoped
def show
+13
View File
@@ -43,4 +43,17 @@ module FiltersHelper
concat tag.kbd(key)
end
end
def sorted_by_label(sort_value)
case sort_value
when "newest"
"Newest to oldest"
when "oldest"
"Oldest to newest"
when "latest"
"Recently updated"
else
sort_value.humanize
end
end
end
+10 -15
View File
@@ -1,19 +1,14 @@
module HotkeysHelper
# Pass in an array of chorded keys, e.g. ["ctrl", "shift", "J"]
def hotkey_label(hotkey)
hotkey.split(",").first if hotkey
case hotkey
when Array
hotkey.map { |key|
if key == "ctrl" && platform.mac?
""
elsif key == "enter"
platform.mac? ? "return" : "enter"
else
key
end
}.join()
else
hotkey.split(",").first if hotkey
end
hotkey.map do |key|
if key == "ctrl" && platform.mac?
""
elsif key == "enter"
platform.mac? ? "return" : "enter"
else
key
end.capitalize
end.join("+").gsub(/\+/, "")
end
end
+2 -2
View File
@@ -1,6 +1,6 @@
class User < ApplicationRecord
include Accessor, Attachable, Assignee, Mentionable, Named, Role, Searcher,
SignalUser, Staff, Transferable, Conversational, AiQuota
include Accessor, AiQuota, Assignee, Attachable, Mentionable, Named, Role,
Searcher, SignalUser, Staff, Transferable, Conversational
include Timelined # Depends on Accessor
has_one_attached :avatar
+1
View File
@@ -56,6 +56,7 @@ class User::Filtering
end
def show_tags?
return unless Tag.any?
expanded? || filter.tags.any?
end
@@ -1,5 +1,5 @@
<div class="settings__panel panel shadow center">
<h2 class="divider txt-large">Do or Die</h2>
<h2 class="divider txt-large">Sink or Swim</h2>
<p class="margin-none-block-start">Choose default settings for this account. You can override them in individual collections.</p>
<%= render "entropy/auto_close", model: account.default_entropy_configuration, url: account_entropy_configuration_path %>
</div>
@@ -5,13 +5,13 @@
navigable_list_actionable_items_value: true
} do %>
<h2 class="divider txt-large">People on the account</h2>
<%#= render "accounts/invite" # Scheduled work in https://3.basecamp.com/2914079/buckets/37331921/todos/8746162337 %>
<%#= render "account/invite" # Scheduled work in https://3.basecamp.com/2914079/buckets/37331921/todos/8746162337 %>
<div class="settings__user-filter">
<input placeholder="Filter…" class="input input--transparent full-width" type="search" autocorrect="off" autocomplete="off" data-1p-ignore="true" data-filter-target="input" data-action="input->filter#filter">
<ul class="settings__user-list" data-filter-target="list">
<%= render partial: "accounts/user", collection: users %>
<%= render partial: "account/user", collection: users %>
</ul>
<footer class="settings__user-list-tips">
@@ -12,6 +12,6 @@
<% end %>
<section class="settings">
<%= render "accounts/settings/users", users: @users %>
<%= render "accounts/settings/entropy_configuration", account: @account %>
<%= render "account/settings/users", users: @users %>
<%= render "account/settings/entropy_configuration", account: @account %>
</section>
@@ -5,7 +5,7 @@
<%= form.hidden_field :engagement, value: "on_deck" %>
<%= form.label :engagement, value: "on_deck", class: "btn", aria: { label: "Move to On Deck"} do %>
<%= form.radio_button :engagement, "on_deck", checked: @card.on_deck?, class: "for-screen-reader", data: { action: "change->form#submit" } %>
<span>Maybe?</span>
<span>On Deck</span>
<% end %>
<% end %>
@@ -20,7 +20,7 @@
<%= form.hidden_field :engagement, value: "doing" %>
<%= form.label :engagement, value: "doing", class: "btn", aria: { label: "Move to Doing"} do %>
<%= form.radio_button :engagement, "doing", checked: @card.doing?, class: "for-screen-reader", data: { action: "change->form#submit" } %>
<span style="min-inline-size: 8ch">Yes!</span>
<span style="min-inline-size: 8ch">Working On</span>
<% end %>
<% end %>
</div>
+1 -1
View File
@@ -36,7 +36,7 @@
dragover->drag-and-drop#dragOver
drop->drag-and-drop#drop
dragend->drag-and-drop#dragEnd" } do %>
<div class="card-columns">
<div class="card-columns" data-controller="related-element" data-related-element-highlight-class="cards--related">
<%= render "cards/index/engagement/on_deck", **@on_deck.to_h %>
<%= render "cards/index/engagement/considering", **@considering.to_h %>
<%= render "cards/index/engagement/doing", **@doing.to_h %>
@@ -1,9 +1,9 @@
<div class="cards__heading" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button class="cards__filter btn input input--select borderless overflow-ellipsis" data-action="click->dialog#open:stop">
<% if filter.stages.any? %>
Yes!: <%= filter.stages.map(&:name).to_sentence %>
Working On: <%= filter.stages.map(&:name).to_sentence %>
<% else %>
Yes!
Working On
<% end %>
</button>
@@ -1,4 +1,9 @@
<section id="considering-cards" class="cards cards--considering" data-drag-and-drop-target="container" data-drop-target="considering">
<section id="considering-cards" class="cards cards--considering"
data-drag-and-drop-target="container"
data-drop-target="considering"
data-related-element-target="related"
data-related-element-group-value="card-columns"
data-action="mouseover->related-element#highlight mouseout->related-element#unhighlight">
<div class="cards__decoration"></div>
<%= render "cards/index/add_card_button", user_filtering: @user_filtering %>
<% if page.used? %>
@@ -1,9 +1,14 @@
<section id="doing-cards" class="cards cards--doing" style="view-transition-name: cards-container;" data-drag-and-drop-target="container" data-drop-target="doing">
<section id="doing-cards" class="cards cards--doing"
data-drag-and-drop-target="container"
data-drop-target="doing"
data-related-element-target="related"
data-related-element-group-value="card-columns"
data-action="mouseover->related-element#highlight mouseout->related-element#unhighlight">
<% if workflow = filter.single_workflow %>
<%= render "cards/index/workflow_filter", workflow: workflow, filter: filter %>
<% else %>
<h2 class="cards__heading">
Yes!
Working On
</h2>
<% end %>
@@ -1,5 +1,10 @@
<section id="on-deck-cards" class="cards cards--on-deck" data-drag-and-drop-target="container" data-drop-target="on_deck">
<h2 class="cards__heading">Maybe?</h2>
<section id="on-deck-cards" class="cards cards--on-deck"
data-drag-and-drop-target="container"
data-drop-target="on_deck"
data-related-element-target="related"
data-related-element-group-value="card-columns"
data-action="mouseover->related-element#highlight mouseout->related-element#unhighlight">
<h2 class="cards__heading">On Deck</h2>
<% if page.used? %>
<%= render partial: "cards/display/preview", collection: page.records, as: :card, locals: { draggable: true }, cached: true %>
@@ -1,4 +1,4 @@
<%= turbo_frame_tag @collection, :entropy_configuration do %>
<h2 class="divider txt-large">Do or Die</h2>
<h2 class="divider txt-large">Sink or Swim</h2>
<%= render "entropy/auto_close", model: collection, url: collection_entropy_configuration_path(collection) %>
<% end %>
+6 -4
View File
@@ -1,6 +1,8 @@
<%= form_with model: collection, class: "txt-align-center margin-block-start-auto", method: :delete do |form| %>
<%= form.button class: "btn txt-negative borderless txt-small", data: { turbo_confirm: "Are you sure you want to permanently delete this Collection?" } do %>
<%= icon_tag "trash" %>
<span>Delete this Collection</span>
<% if Current.user.collections.many? %>
<%= form_with model: collection, class: "txt-align-center margin-block-start-auto", method: :delete do |form| %>
<%= form.button class: "btn txt-negative borderless txt-small", data: { turbo_confirm: "Are you sure you want to permanently delete this Collection?" } do %>
<%= icon_tag "trash" %>
<span>Delete this Collection</span>
<% end %>
<% end %>
<% end %>
+1 -1
View File
@@ -16,7 +16,7 @@
<div class="separator margin-block-half" style="--border-color: var(--color-ink-light); min-block-size: 3lh;"></div>
<div class="flex flex-column full-width" style="flex-basis: 48%">
<strong>Cards in Maybe?/Yes!</strong>
<strong>Cards in On Deck/Working On</strong>
<p class="txt-x-small margin-none-block-start">Back to The Stream after…</p>
<%= form_with model: model, url: url, data: { controller: "form" } do |form| %>
<%= render "entropy/knob",
@@ -1,13 +1,6 @@
<% clear_url_path = user_filtering.self_filter_path(user_filtering.as_params.except(:collection_ids)) %>
<% if Current.user.collections.one? %>
<%= link_to clear_url_path, class: "popup__group", id: "filter-collection-init", data: { filter_target: "item", navigable_list_target: "item" } do %>
<%= tag.div class: "popup__item btn" do %>
<span class="overflow-ellipsis"><%= Current.user.collections.first.name %></span>
<span class="txt-ink translucent flex-item-no-shrink flex-item-justify-end"><span class="txt-x-small txt-uppercase">GO TO</span> </span>
<% end %>
<% end %>
<% elsif Current.user.collections.many? %>
<% if Current.user.collections.many? %>
<li class="popup__group" data-filter-target="item" data-navigable-list-target="item" id="filter-collection-all">
<%= link_to clear_url_path, class: "btn txt-xx-small flex-item-no-shrink" do %>
<%= check_box_tag "filter_collections", nil, user_filtering.collections.blank?, class: "form-checkbox" %>
@@ -4,7 +4,7 @@
<div class="quick-filter position-relative <%= "default-value" if filter.sorted_by.latest? %>" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button class="btn input input--select flex-inline txt-x-small" data-action="click->dialog#open:stop">
<span class="overflow-ellipsis">
<%= filter.sorted_by.humanize %>
<%= sorted_by_label(filter.sorted_by) %>
</span>
</button>
@@ -26,7 +26,7 @@
<% end %>
<%= form.button type: "submit", class: "popup__item btn" do %>
<span class="overflow-ellipsis flex-item-grow"><%= sort.humanize %></span>
<span class="overflow-ellipsis flex-item-grow"><%= sorted_by_label(sort) %></span>
<% if filter.sorted_by == sort %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
<% end %>
@@ -1,6 +1,6 @@
<section id="doing-cards" class="cards cards--doing" style="view-transition-name: cards-container;">
<section id="doing-cards" class="cards cards--doing">
<h2 class="cards__heading">
Yes!
Working On
</h2>
<% if page.used? %>
@@ -1,6 +1,6 @@
<section id="considering-cards" class="cards cards--on-deck">
<h2 class="cards__heading">
Maybe?
On Deck
</h2>
<% if page.used? %>
+4 -7
View File
@@ -1,11 +1,8 @@
Rails.application.routes.draw do
resource :account do
resource :join_code, module: :accounts
scope module: :accounts do
resource :settings
resource :entropy_configuration
end
namespace :account do
resource :join_code
resource :settings
resource :entropy_configuration
end
resources :users do
@@ -1,6 +1,6 @@
require "test_helper"
class Accounts::EntropyConfigurationsControllerTest < ActionDispatch::IntegrationTest
class Account::EntropyConfigurationsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
end
@@ -1,6 +1,6 @@
require "test_helper"
class Accounts::SettingsControllerTest < ActionDispatch::IntegrationTest
class Account::SettingsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
end
+52
View File
@@ -0,0 +1,52 @@
require "test_helper"
class HotkeysHelperTest < ActionView::TestCase
include SetPlatform
test "mac modifier key" do
emulate_mac
assert_equal "⌘J", hotkey_label([ "", "J" ])
end
test "linux modifier key" do
emulate_linux
assert_equal "Ctrl+J", hotkey_label([ "ctrl", "J" ])
end
test "mac enter" do
emulate_mac
assert_equal "Return+J", hotkey_label([ "enter", "J" ])
end
test "linux enter" do
emulate_linux
assert_equal "Enter+J", hotkey_label([ "enter", "J" ])
end
test "mac hyper" do
emulate_mac
assert_equal "Hyper+J", hotkey_label([ "hyper", "J" ])
end
test "linux hyper" do
emulate_linux
assert_equal "Hyper+J", hotkey_label([ "hyper", "J" ])
end
private
def emulate_mac
stub_platform = ApplicationPlatform.new("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36")
self.stubs(:platform).returns(stub_platform)
end
def emulate_linux
stub_platform = ApplicationPlatform.new("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36")
self.stubs(:platform).returns(stub_platform)
end
end
+15
View File
@@ -0,0 +1,15 @@
require "test_helper"
class RouteTest < ActionDispatch::IntegrationTest
test "account/join_code" do
assert_recognizes({ controller: "account/join_codes", action: "show" }, "/account/join_code")
end
test "account/settings" do
assert_recognizes({ controller: "account/settings", action: "show" }, "/account/settings")
end
test "account/entropy_configuration" do
assert_recognizes({ controller: "account/entropy_configurations", action: "show" }, "/account/entropy_configuration")
end
end