From 1190ae101d3035702f5fefbbfc0cc93a5dfae34b Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 14:14:49 -0600 Subject: [PATCH 01/26] Ensure sign-in screens adapt on mobile --- app/assets/stylesheets/panels.css | 6 +++++- app/views/sessions/_footer.html.erb | 2 +- app/views/sessions/new.html.erb | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/panels.css b/app/assets/stylesheets/panels.css index 9b0345096..d57e3aa29 100644 --- a/app/assets/stylesheets/panels.css +++ b/app/assets/stylesheets/panels.css @@ -6,7 +6,11 @@ color: var(--color-ink); inline-size: var(--panel-size, 60ch); max-inline-size: 100%; - padding: var(--panel-padding, calc(var(--block-space) * 2)); + padding: var(--panel-padding, var(--block-space)); + + @media (min-width: 640px) { + padding: var(--panel-padding, var(--block-space-double)); + } } .panel--centered { diff --git a/app/views/sessions/_footer.html.erb b/app/views/sessions/_footer.html.erb index f0b4fe55a..6ad8f5972 100644 --- a/app/views/sessions/_footer.html.erb +++ b/app/views/sessions/_footer.html.erb @@ -1,4 +1,4 @@ -
+
<%= link_to "Fizzy", "https://www.fizzy.do", class: "txt-subtle" %> is designed, built, and backed by <%= icon_tag "37signals", class: "v-align-middle" %> 37signals. Need help? <%= mail_to "support@fizzy.do", "Send us an email", class: "txt-link" %>
\ No newline at end of file diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb index b4d0ae45b..f90fe2fb3 100644 --- a/app/views/sessions/new.html.erb +++ b/app/views/sessions/new.html.erb @@ -1,6 +1,6 @@ <% @page_title = "Enter your email" %> -
+

Let’s get you in to Fizzy

<%= form_with url: session_path, class: "flex flex-column gap txt-medium" do |form| %> From 90967374d30a74cc3646b2ed08be4c094b5e1ffe Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 14:18:46 -0600 Subject: [PATCH 02/26] =?UTF-8?q?Add=20`=E2=84=A2`=20for=20Fizzy=20and=203?= =?UTF-8?q?7signals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/my/menus/show.html.erb | 2 +- app/views/sessions/_footer.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/my/menus/show.html.erb b/app/views/my/menus/show.html.erb index fccbde638..5a0ab7ee4 100644 --- a/app/views/my/menus/show.html.erb +++ b/app/views/my/menus/show.html.erb @@ -65,6 +65,6 @@ <% end %>
- <%= link_to "Fizzy", "https://www.fizzy.do", class: "txt-ink", target: "_blank" %> is designed, built, and backed by <%= icon_tag "37signals", class: "v-align-middle", target: "_blank" %> 37signals + <%= link_to "Fizzy™", "https://www.fizzy.do", class: "txt-ink", target: "_blank" %> is designed, built, and backed by <%= icon_tag "37signals", class: "v-align-middle", target: "_blank" %> 37signals™
<% end %> diff --git a/app/views/sessions/_footer.html.erb b/app/views/sessions/_footer.html.erb index 6ad8f5972..596a359e3 100644 --- a/app/views/sessions/_footer.html.erb +++ b/app/views/sessions/_footer.html.erb @@ -1,4 +1,4 @@
- <%= link_to "Fizzy", "https://www.fizzy.do", class: "txt-subtle" %> is designed, built, and backed by <%= icon_tag "37signals", class: "v-align-middle" %> 37signals. + <%= link_to "Fizzy™", "https://www.fizzy.do", class: "txt-subtle" %> is designed, built, and backed by <%= icon_tag "37signals", class: "v-align-middle" %> 37signals™. Need help? <%= mail_to "support@fizzy.do", "Send us an email", class: "txt-link" %>
\ No newline at end of file From 5b6a22a961c7bf5b88206553d9fa9ee257369613 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 14:21:52 -0600 Subject: [PATCH 03/26] Try a differnt approch to responsive panels Improves upon 1190ae10 --- app/assets/stylesheets/panels.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/panels.css b/app/assets/stylesheets/panels.css index d57e3aa29..a82dc1665 100644 --- a/app/assets/stylesheets/panels.css +++ b/app/assets/stylesheets/panels.css @@ -9,13 +9,19 @@ padding: var(--panel-padding, var(--block-space)); @media (min-width: 640px) { + --panel-size: 100%; + padding: var(--panel-padding, var(--block-space-double)); } } .panel--centered { --panel-border-size: 0; - --panel-size: 42ch; + --panel-size: 100%; + + @media (min-width: 640px) { + --panel-size: 42ch; + } #main:has(&) { display: grid; From 699bc1a13b2b43ac1d3de7f1036459a3ec7c161b Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 14:30:25 -0600 Subject: [PATCH 04/26] Use `ArrowLeft` for back button navigation --- app/helpers/boards_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index 7c83c5799..3fde0d2ad 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -1,8 +1,8 @@ module BoardsHelper def link_back_to_board(board) link_to board, class: "btn borderless txt-medium", - data: { controller: "hotkey", action: "keydown.b@document->hotkey#click click->turbo-navigation#backIfSamePath" } do - tag.span ("←" + tag.strong(board.name, class: "overflow-ellipsis") + tag.kbd("b", class: "txt-x-small margin-inline-start hide-on-touch")).html_safe + data: { controller: "hotkey", action: "keydown.left@document->hotkey#click click->turbo-navigation#backIfSamePath" } do + tag.span(tag.strong("Back to #{board.name}", class: "overflow-ellipsis") + tag.kbd("←", class: "txt-x-small margin-inline-start hide-on-touch"), class: "flex align-center").html_safe end end From bdde877de32f3ef33cf269c31edd00dc39a614f5 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 15:13:30 -0600 Subject: [PATCH 05/26] Apply the new hotkey and style to all back buttons Also updates design for email address change screens --- app/views/account/join_codes/edit.html.erb | 7 +++-- app/views/account/join_codes/show.html.erb | 7 +++-- .../boards/columns/closeds/show.html.erb | 7 +---- .../boards/columns/not_nows/show.html.erb | 7 +---- app/views/boards/columns/show.html.erb | 7 +---- .../boards/columns/streams/show.html.erb | 7 +---- app/views/boards/edit.html.erb | 7 +---- .../email_addresses/create.html.erb | 11 +++++--- .../memberships/email_addresses/new.html.erb | 26 ++++++++++--------- app/views/public/cards/show.html.erb | 8 +++--- app/views/sessions/new.html.erb | 2 +- app/views/users/edit.html.erb | 11 ++++++++ app/views/webhooks/edit.html.erb | 8 +++--- app/views/webhooks/index.html.erb | 25 +++++------------- app/views/webhooks/new.html.erb | 7 +---- app/views/webhooks/show.html.erb | 8 +++--- 16 files changed, 68 insertions(+), 87 deletions(-) diff --git a/app/views/account/join_codes/edit.html.erb b/app/views/account/join_codes/edit.html.erb index de53bf8c8..14958a44c 100644 --- a/app/views/account/join_codes/edit.html.erb +++ b/app/views/account/join_codes/edit.html.erb @@ -2,8 +2,11 @@ <% content_for :header do %>
- <%= link_to account_join_code_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - Invite link + <%= link_to account_join_code_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %> + + Back to Invite link + + <% end %>
<% end %> diff --git a/app/views/account/join_codes/show.html.erb b/app/views/account/join_codes/show.html.erb index a1efbcc5a..9a6b30475 100644 --- a/app/views/account/join_codes/show.html.erb +++ b/app/views/account/join_codes/show.html.erb @@ -2,8 +2,11 @@ <% content_for :header do %>
- <%= link_to account_settings_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - Account Settings + <%= link_to account_settings_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %> + + Back to Account Settings + + <% end %>
<% end %> diff --git a/app/views/boards/columns/closeds/show.html.erb b/app/views/boards/columns/closeds/show.html.erb index 9aeb21044..a67c45c3b 100644 --- a/app/views/boards/columns/closeds/show.html.erb +++ b/app/views/boards/columns/closeds/show.html.erb @@ -2,12 +2,7 @@ <% content_for :header do %>
- <%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - - ← - <%= @board.name %> - - <% end %> + <%= link_back_to_board(@board) %>

diff --git a/app/views/boards/columns/not_nows/show.html.erb b/app/views/boards/columns/not_nows/show.html.erb index 4bd6a0524..379d92dea 100644 --- a/app/views/boards/columns/not_nows/show.html.erb +++ b/app/views/boards/columns/not_nows/show.html.erb @@ -2,12 +2,7 @@ <% content_for :header do %>
- <%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - - ← - <%= @board.name %> - - <% end %> + <%= link_back_to_board(@board) %>

diff --git a/app/views/boards/columns/show.html.erb b/app/views/boards/columns/show.html.erb index 56fca64ff..83931aaff 100644 --- a/app/views/boards/columns/show.html.erb +++ b/app/views/boards/columns/show.html.erb @@ -2,12 +2,7 @@ <% content_for :header do %>
- <%= link_to @column.board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - - ← - <%= @column.board.name %> - - <% end %> + <%= link_back_to_board(@column.board) %>

diff --git a/app/views/boards/columns/streams/show.html.erb b/app/views/boards/columns/streams/show.html.erb index 59e929464..141584283 100644 --- a/app/views/boards/columns/streams/show.html.erb +++ b/app/views/boards/columns/streams/show.html.erb @@ -2,12 +2,7 @@ <% content_for :header do %>
- <%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - - ← - <%= @board.name %> - - <% end %> + <%= link_back_to_board(@board) %>

diff --git a/app/views/boards/edit.html.erb b/app/views/boards/edit.html.erb index 7428e0bb9..36beee3c8 100644 --- a/app/views/boards/edit.html.erb +++ b/app/views/boards/edit.html.erb @@ -2,12 +2,7 @@ <% content_for :header do %>
- <%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - - ← - <%= @board.name %> - - <% end %> + <%= link_back_to_board(@board) %>

diff --git a/app/views/memberships/email_addresses/create.html.erb b/app/views/memberships/email_addresses/create.html.erb index 53851105d..44711b5cf 100644 --- a/app/views/memberships/email_addresses/create.html.erb +++ b/app/views/memberships/email_addresses/create.html.erb @@ -1,14 +1,17 @@ <% @page_title = "Confirm your new email address" %> <% content_for :header do %> -
- <%= link_to edit_user_path(@membership.user, script_name: "/#{@tenant}"), class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - My profile +
+ <%= link_to edit_user_path(@membership.user, script_name: "/#{@tenant}"), class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %> + + Back to My profile + + <% end %>
<% end %> -
+

Check your email

We just sent an email to <%= params[:email_address] %>

Hit the link in the email to confirm this is the email address you want to use with Fizzy going forward.

diff --git a/app/views/memberships/email_addresses/new.html.erb b/app/views/memberships/email_addresses/new.html.erb index e99596471..4b493d667 100644 --- a/app/views/memberships/email_addresses/new.html.erb +++ b/app/views/memberships/email_addresses/new.html.erb @@ -1,28 +1,30 @@ <% @page_title = "Change your email" %> <% content_for :header do %> -
- <%= link_to edit_user_path(@membership.user, script_name: "/#{@membership.tenant}"), class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> - My profile +
+ <%= link_to edit_user_path(@membership.user, script_name: "/#{@membership.tenant}"), class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %> + + Back to My profile + + <% end %>
<% end %> -
-
-

- <%= @page_title %> -

-

Enter your new email address, then hit the link to confirm it

-
+
+

+ <%= @page_title %> +

- <%= form_with url: email_addresses_path(membership_id: @membership.id), method: :post, class: "flex flex-column gap", data: { controller: "form", turbo: false } do |form| %> -
+ <%= form_with url: email_addresses_path(membership_id: @membership.id), method: :post, class: "flex flex-column gap-half", data: { controller: "form", turbo: false } do |form| %> +
+

Enter your new email address, then hit the link to confirm it

+ <%= render "boards/show/menu/column_form", board: board, column: Column.new, label: "Add column" %> diff --git a/app/views/filters/settings/_toggle.html.erb b/app/views/filters/settings/_toggle.html.erb index 1f6459e0a..d1e7d10bf 100644 --- a/app/views/filters/settings/_toggle.html.erb +++ b/app/views/filters/settings/_toggle.html.erb @@ -1,11 +1,13 @@ <% filter = user_filtering.filter %> <% if user_filtering.expanded? %> - <% else %> - <% end %> diff --git a/app/views/users/_transfer.html.erb b/app/views/users/_transfer.html.erb index b260a152e..8befea9ce 100644 --- a/app/views/users/_transfer.html.erb +++ b/app/views/users/_transfer.html.erb @@ -12,18 +12,18 @@
- <%= tag.button class: "btn", data: { action: "dialog#open" } do %> + <%= tag.button class: "btn", data: { action: "dialog#open", controller: "tooltip" } do %> <%= icon_tag "qr-code" %> - Show auto-login QR code + Display auto-login QR code <% end %> <%= qr_code_image(url) %>
-
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index d5cbd8865..eec6444a0 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -5,9 +5,9 @@
<% if Current.user == @user %> - <%= link_to edit_user_path(@user), class: "user-edit-link btn" do %> + <%= link_to edit_user_path(@user), class: "user-edit-link btn", data: { controller: "tooltip" } do %> <%= icon_tag "pencil" %> - Edit + Edit profile <% end %> <% end %> @@ -27,9 +27,9 @@
- <%= link_to "Which cards are assigned to #{me_or_you}?", + <%= link_to "Which cards are assigned to #{me_or_you}?", cards_path(assignee_ids: [ @user.id ], sorted_by: "newest"), class: "btn", data: { turbo_frame: "_top" } %> - <%= link_to "Which cards were added by #{me_or_you}?", + <%= link_to "Which cards were added by #{me_or_you}?", cards_path(creator_ids: [ @user.id ], sorted_by: "newest"), class: "btn", data: { turbo_frame: "_top" } %>
From df858fcdd0fb7ee33abffd325cdd85c37eec739d Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 15:55:23 -0600 Subject: [PATCH 11/26] Rework to ensure button adopts card color Removes unecessary field class, visually downplays edit button --- app/assets/stylesheets/cards.css | 17 ++++++++++------- app/views/cards/container/_title.html.erb | 4 ++-- app/views/cards/edit.html.erb | 4 ++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/cards.css b/app/assets/stylesheets/cards.css index 4b0569b54..2591332d9 100644 --- a/app/assets/stylesheets/cards.css +++ b/app/assets/stylesheets/cards.css @@ -179,17 +179,20 @@ display: none; } - & ~ .btn { - --btn-background: var(--card-color); - --btn-color: var(--color-ink-inverted); - } - } - - .card-field__description { lexxy-toolbar { border-block-start: 1px solid var(--color-ink-light); margin-block-start: var(--block-space-half) !important; } + + & ~ .btn.btn--reversed { + --btn-background: var(--card-color); + --btn-color: var(--color-ink-inverted); + } + + & ~ .btn { + --btn-border-color: var(--card-color); + --btn-color: var(--card-color); + } } .card__stages { diff --git a/app/views/cards/container/_title.html.erb b/app/views/cards/container/_title.html.erb index de52542fe..814370bed 100644 --- a/app/views/cards/container/_title.html.erb +++ b/app/views/cards/container/_title.html.erb @@ -10,7 +10,7 @@
<% end %> - <%= link_to "Edit", edit_card_path(card), class: "btn btn--reversed fit-content", style: "--btn-background: #{card.color}" %> + <%= link_to "Edit", edit_card_path(card), class: "btn fit-content txt-small" %> <% end %> <% else %> <%= form_with model: card, id: "card_form", data: { controller: "autoresize auto-save" } do |form| %> @@ -23,7 +23,7 @@ <% end %>

- <%= form.rich_textarea :description, class: "card-field__description rich-text-content", + <%= form.rich_textarea :description, class: "card__description rich-text-content", placeholder: "Add some notes, context, pictures, or video about this…", data: { action: "lexxy:change->auto-save#change focusout->auto-save#submit" } do %> <%= general_prompts(card.board) %> diff --git a/app/views/cards/edit.html.erb b/app/views/cards/edit.html.erb index 1a1eda843..15de68c0b 100644 --- a/app/views/cards/edit.html.erb +++ b/app/views/cards/edit.html.erb @@ -9,13 +9,13 @@ <% end %> - <%= form.rich_textarea :description, class: "card-field__description rich-text-content", + <%= form.rich_textarea :description, class: "card__description rich-text-content", placeholder: "Add some notes, context, pictures, or video about this…", data: { local_save_target: "input", action: "lexxy:change->local-save#save turbo:morph-element->local-save#restoreContent keydown.ctrl+enter->form#submit:prevent keydown.meta+enter->form#submit:prevent keydown.esc->form#cancel:stop" } do %> <%= general_prompts(@card.board) %> <% end %> - <%= form.button "Save changes", type: :submit, class: "btn btn--reversed fit-content", style: "--btn-background: #{@card.color}" %> + <%= form.button "Save changes", type: :submit, class: "btn btn--reversed fit-content txt-small" %> <%= link_to "Close editor and discard changes", @card, data: { form_target: "cancel" }, hidden: true %> <% end %> <% end %> From 66708e7f4c99381147308240b7dfea8b3b29583c Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 16:02:06 -0600 Subject: [PATCH 12/26] Hotkeys and loading state --- app/views/cards/container/_title.html.erb | 5 ++++- app/views/cards/edit.html.erb | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/views/cards/container/_title.html.erb b/app/views/cards/container/_title.html.erb index 814370bed..98b0b3bb6 100644 --- a/app/views/cards/container/_title.html.erb +++ b/app/views/cards/container/_title.html.erb @@ -10,7 +10,10 @@
<% end %> - <%= link_to "Edit", edit_card_path(card), class: "btn fit-content txt-small" %> + <%= link_to edit_card_path(card), class: "btn fit-content txt-small", data: { controller: "hotkey", action: "keydown.e@document->hotkey#click" } do %> + Edit + e + <% end %> <% end %> <% else %> <%= form_with model: card, id: "card_form", data: { controller: "autoresize auto-save" } do |form| %> diff --git a/app/views/cards/edit.html.erb b/app/views/cards/edit.html.erb index 15de68c0b..58b2d7bde 100644 --- a/app/views/cards/edit.html.erb +++ b/app/views/cards/edit.html.erb @@ -15,7 +15,10 @@ <%= general_prompts(@card.board) %> <% end %> - <%= form.button "Save changes", type: :submit, class: "btn btn--reversed fit-content txt-small" %> + <%= form.button type: :submit, class: "btn btn--reversed fit-content txt-small", data: { form_target: "submit" } do %> + Save changes + <%= hotkey_label([ "ctrl", "enter" ]) %> + <% end %> <%= link_to "Close editor and discard changes", @card, data: { form_target: "cancel" }, hidden: true %> <% end %> <% end %> From 776a8e3c0afcf25238b221754af3f572ce4b49bd Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 16:08:27 -0600 Subject: [PATCH 13/26] Typo --- app/views/sessions/magic_links/show.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/sessions/magic_links/show.html.erb b/app/views/sessions/magic_links/show.html.erb index 69a9f6cbd..e358f3284 100644 --- a/app/views/sessions/magic_links/show.html.erb +++ b/app/views/sessions/magic_links/show.html.erb @@ -3,7 +3,7 @@
">

<%= @page_title %>

-

Then enter the verfifcation code included in the email below:

+

Then enter the verification code included in the email below:

<%= form_with url: session_magic_link_path, method: :post, html: { data: { controller: token_list("form", "auto-submit" => params[:code].present?)} } do |form| %> From 7274661313682d65bd85d5445e16a9d1c6d367d5 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 16:30:07 -0600 Subject: [PATCH 14/26] Special case blank slate for public --- app/assets/stylesheets/card-columns.css | 4 ++++ app/views/public/boards/show/_stream.html.erb | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/app/assets/stylesheets/card-columns.css b/app/assets/stylesheets/card-columns.css index 51816ae4d..ef3e5b37a 100644 --- a/app/assets/stylesheets/card-columns.css +++ b/app/assets/stylesheets/card-columns.css @@ -400,6 +400,10 @@ box-shadow: none !important; color: color-mix(in srgb, var(--card-color) 70%, var(--color-canvas)); + .cards--considering & { + background-color: var(--card-bg-color) !important; + } + p { font-size: var(--text-small); overflow: hidden; diff --git a/app/views/public/boards/show/_stream.html.erb b/app/views/public/boards/show/_stream.html.erb index 1a902c087..55c4b363b 100644 --- a/app/views/public/boards/show/_stream.html.erb +++ b/app/views/public/boards/show/_stream.html.erb @@ -11,6 +11,10 @@ <%= with_automatic_pagination "the-stream", @page do %> <%= render "public/boards/columns/list", cards: page.records %> <% end %> + <% else %> +
+

No cards here

+
<% end %>
From 7d095c87b18d37f3b8a6aa84e30081f643cde6f2 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 17:54:56 -0600 Subject: [PATCH 15/26] Simplify label, try showing the hotkey outside the button when we have room --- app/views/cards/comments/_new.html.erb | 13 ++++++++----- test/system/smoke_test.rb | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/views/cards/comments/_new.html.erb b/app/views/cards/comments/_new.html.erb index 46e6333b0..cd46d7cb4 100644 --- a/app/views/cards/comments/_new.html.erb +++ b/app/views/cards/comments/_new.html.erb @@ -13,13 +13,16 @@ data: { local_save_target: "input", action: "lexxy:change->form#disableSubmitWhenInvalid lexxy:change->local-save#save turbo:morph-element->local-save#restoreContent" } do %> <%= general_prompts(@card.board) %> <% end %> - <%= form.button class: "comment__submit btn btn--reversed flex-item-justify-start", - data: { form_target: "submit" }, disabled: true do %> - Post this comment - + + + <%= form.button class: "comment__submit btn btn--reversed", + data: { form_target: "submit" }, disabled: true do %> + Post + <% end %> + <%= hotkey_label([ "ctrl", "enter" ]) %> - <% end %> + <% end %>
diff --git a/test/system/smoke_test.rb b/test/system/smoke_test.rb index 76c601024..d899aa450 100644 --- a/test/system/smoke_test.rb +++ b/test/system/smoke_test.rb @@ -27,7 +27,7 @@ class SmokeTest < ApplicationSystemTestCase assert_selector "figcaption input[placeholder='moon.jpg']" end - click_on "Post this comment" + click_on "Post" within("action-text-attachment") do assert_selector "a img[src*='/rails/active_storage']" From 10567b20d3c92bbec1da5104e649b123053319bd Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 11 Nov 2025 18:05:46 -0600 Subject: [PATCH 16/26] Reduce dialpad to 3, copy edits to places --- app/views/my/menus/_jump.html.erb | 3 --- app/views/my/menus/show.html.erb | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/views/my/menus/_jump.html.erb b/app/views/my/menus/_jump.html.erb index 1ee906715..281f45c90 100644 --- a/app/views/my/menus/_jump.html.erb +++ b/app/views/my/menus/_jump.html.erb @@ -17,9 +17,6 @@ <%= filter_hotkey_link "Home", root_path, 1, "home" %> <%= filter_hotkey_link "Assigned to me", cards_path(assignee_ids: [Current.user.id]), 2, "clipboard" %> <%= filter_hotkey_link "Added by me", cards_path(creator_ids: [Current.user.id]), 3, "person-add" %> - <%= filter_hotkey_link "Notifications", notifications_path, 4, "bell" %> - <%= filter_hotkey_link "My Profile", user_path(Current.user), 5, "person" %> - <%= filter_hotkey_link "Settings", account_settings_path, 6, "settings" %>
<%= yield %> diff --git a/app/views/my/menus/show.html.erb b/app/views/my/menus/show.html.erb index c56ba3315..b722aa658 100644 --- a/app/views/my/menus/show.html.erb +++ b/app/views/my/menus/show.html.erb @@ -47,10 +47,10 @@ <% end %> <% end %> - <%= collapsible_nav_section "Jump to…" do %> + <%= collapsible_nav_section "Settings" do %> <%= filter_place_menu_item account_settings_path, "Account Settings", "settings" %> <%= filter_place_menu_item user_path(Current.user), "My Profile", "person" %> - <%= filter_place_menu_item notifications_path, "Notifications", "bell" %> + <%= filter_place_menu_item notifications_path, "All notifications", "bell" %> <%= filter_place_menu_item notifications_settings_path, "Notification Settings", "settings" %> <%= tag.li class: "popup__item", data: { filter_target: "item", navigable_list_target: "item" } do %> From d36011dcbf45f6bee19717122435a7fa04be15c8 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:12:18 +0100 Subject: [PATCH 17/26] Fix: invalidate http cache when selecting tags --- app/controllers/cards/taggings_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/cards/taggings_controller.rb b/app/controllers/cards/taggings_controller.rb index 50f58e34b..23bac85e3 100644 --- a/app/controllers/cards/taggings_controller.rb +++ b/app/controllers/cards/taggings_controller.rb @@ -3,7 +3,7 @@ class Cards::TaggingsController < ApplicationController def new @tags = Tag.all.alphabetically - fresh_when @tags + fresh_when etag: [ @tags, @card.tags ] end def create From 7bfcfb57c7158bfa14cd6b4b0e7e35f2b7b26e36 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:17:21 +0100 Subject: [PATCH 18/26] Fix: invalidate http cache when selecting assignees --- app/controllers/cards/assignments_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/cards/assignments_controller.rb b/app/controllers/cards/assignments_controller.rb index cac32efde..fdd2a86dc 100644 --- a/app/controllers/cards/assignments_controller.rb +++ b/app/controllers/cards/assignments_controller.rb @@ -3,7 +3,7 @@ class Cards::AssignmentsController < ApplicationController def new @users = @board.users.active.alphabetically - fresh_when @users + fresh_when etag: [ @users, @card.assignees ] end def create From dba3b2d65d5891ea1731da52508451bcef8151d9 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:25:51 +0100 Subject: [PATCH 19/26] Remove nil param --- app/models/user/role.rb | 2 +- app/views/account/join_codes/show.html.erb | 4 ++-- app/views/account/settings/_entropy.html.erb | 2 +- app/views/account/settings/_name.html.erb | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/models/user/role.rb b/app/models/user/role.rb index a327ffcf6..a9574a6ef 100644 --- a/app/models/user/role.rb +++ b/app/models/user/role.rb @@ -18,7 +18,7 @@ module User::Role admin? || other == self end - def can_administer?(other = nil) + def can_administer? admin? && other != self end diff --git a/app/views/account/join_codes/show.html.erb b/app/views/account/join_codes/show.html.erb index 9a6b30475..c528bb07d 100644 --- a/app/views/account/join_codes/show.html.erb +++ b/app/views/account/join_codes/show.html.erb @@ -21,7 +21,7 @@
- <% if Current.user.can_administer? %> + <% if Current.user.admin? %> <%= button_to account_join_code_path, method: :delete, class: "btn btn--circle txt-small", data: { turbo_confirm: "Are you sure you want to generate a new link? The previous code will stop working." } do %> <%= icon_tag "refresh" %> @@ -65,7 +65,7 @@ This code has been used <%= @join_code.usage_count %>/<%= @join_code.usage_limit %> times (<%= @join_code.active? ? @join_code.usage_limit - @join_code.usage_count : "none" %> remaining) - <% if Current.user.can_administer? %> + <% if Current.user.admin? %> <%= link_to edit_account_join_code_path, class: @join_code.active? ? "txt-link" : "txt-negative txt-underline" do %> Change limit <% end %> diff --git a/app/views/account/settings/_entropy.html.erb b/app/views/account/settings/_entropy.html.erb index 034d8128f..11ff9b326 100644 --- a/app/views/account/settings/_entropy.html.erb +++ b/app/views/account/settings/_entropy.html.erb @@ -4,5 +4,5 @@

Fizzy doesn’t let stale cards stick around forever. Cards automatically close as “Not Now” without activity for specific period of time. This is the default, global setting — you can override it on each board.

- <%= render "entropy/auto_close", model: account.entropy, url: account_entropy_path, disabled: !Current.user.can_administer? %> + <%= render "entropy/auto_close", model: account.entropy, url: account_entropy_path, disabled: !Current.user.admin? %>
diff --git a/app/views/account/settings/_name.html.erb b/app/views/account/settings/_name.html.erb index 3c4f68f03..e3b12f325 100644 --- a/app/views/account/settings/_name.html.erb +++ b/app/views/account/settings/_name.html.erb @@ -1,7 +1,7 @@ <%= form_with model: account, url: account_settings_path, method: :put, scope: :account, data: { controller: "form" }, class: "flex gap-half" do |form| %> - <%= form.text_field :name, required: true, class: "input input--transparent full-width txt-medium", placeholder: "Account name…", data: { action: "input->form#disableSubmitWhenInvalid" }, readonly: !Current.user.can_administer? %> + <%= form.text_field :name, required: true, class: "input input--transparent full-width txt-medium", placeholder: "Account name…", data: { action: "input->form#disableSubmitWhenInvalid" }, readonly: !Current.user.admin? %> - <% if Current.user.can_administer? %> + <% if Current.user.admin? %> <%= form.button class: "btn btn--circle btn--link txt-medium", data: { form_target: "submit" }, disabled: form.object do %> <%= icon_tag "arrow-right" %> Save changes From e67cdd986f8281f84f1ea241ac9e35ed5b4fa1b2 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:30:29 +0100 Subject: [PATCH 20/26] Only admins can manage join codes --- app/controllers/account/join_codes_controller.rb | 1 + .../accounts/join_codes_controller_test.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/app/controllers/account/join_codes_controller.rb b/app/controllers/account/join_codes_controller.rb index 026c692f9..562ffaa0d 100644 --- a/app/controllers/account/join_codes_controller.rb +++ b/app/controllers/account/join_codes_controller.rb @@ -1,5 +1,6 @@ class Account::JoinCodesController < ApplicationController before_action :set_join_code + before_action :ensure_admin, only: %i[ update destroy ] def show end diff --git a/test/controllers/accounts/join_codes_controller_test.rb b/test/controllers/accounts/join_codes_controller_test.rb index 438904d2a..ee80e4193 100644 --- a/test/controllers/accounts/join_codes_controller_test.rb +++ b/test/controllers/accounts/join_codes_controller_test.rb @@ -23,4 +23,18 @@ class Account::JoinCodesControllerTest < ActionDispatch::IntegrationTest assert_equal 5, Account::JoinCode.sole.usage_limit assert_redirected_to account_join_code_path end + + test "update requires admin" do + logout_and_sign_in_as :david + + put account_join_code_path, params: { account_join_code: { usage_limit: 5 } } + assert_response :forbidden + end + + test "destroy requires admin" do + logout_and_sign_in_as :david + + delete account_join_code_path + assert_response :forbidden + end end From b7729401b4568479dc2028b494ffa46a909fcd6a Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:33:41 +0100 Subject: [PATCH 21/26] Only admins can edit entropy --- app/controllers/account/entropies_controller.rb | 2 ++ test/controllers/accounts/entropies_controller_test.rb | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/app/controllers/account/entropies_controller.rb b/app/controllers/account/entropies_controller.rb index f45f3ef5f..00d195584 100644 --- a/app/controllers/account/entropies_controller.rb +++ b/app/controllers/account/entropies_controller.rb @@ -1,4 +1,6 @@ class Account::EntropiesController < ApplicationController + before_action :ensure_admin + def update Account.sole.entropy.update!(entropy_params) redirect_to account_settings_path, notice: "Account updated" diff --git a/test/controllers/accounts/entropies_controller_test.rb b/test/controllers/accounts/entropies_controller_test.rb index 9fda2c1ed..ef23851de 100644 --- a/test/controllers/accounts/entropies_controller_test.rb +++ b/test/controllers/accounts/entropies_controller_test.rb @@ -12,4 +12,11 @@ class Account::EntropiesControllerTest < ActionDispatch::IntegrationTest assert_redirected_to account_settings_path end + + test "update requires admin" do + logout_and_sign_in_as :david + + put account_entropy_path, params: { entropy: { auto_postpone_period: 1.day } } + assert_response :forbidden + end end From cc612479f0751329c06526cbf653143cc38292bb Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:35:58 +0100 Subject: [PATCH 22/26] Add missing test --- test/controllers/accounts/settings_controller_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/controllers/accounts/settings_controller_test.rb b/test/controllers/accounts/settings_controller_test.rb index 9a9a45d3b..50078dd32 100644 --- a/test/controllers/accounts/settings_controller_test.rb +++ b/test/controllers/accounts/settings_controller_test.rb @@ -15,4 +15,11 @@ class Account::SettingsControllerTest < ActionDispatch::IntegrationTest assert_equal "New Account Name", Account.sole.name assert_redirected_to account_settings_path end + + test "update requires admin" do + logout_and_sign_in_as :david + + put account_settings_path, params: { account: { name: "New Account Name" } } + assert_response :forbidden + end end From 9903a37243c0e881fd75c2da7480d624e6ed187d Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:37:06 +0100 Subject: [PATCH 23/26] Fix tests --- app/models/user/role.rb | 2 +- app/views/account/settings/show.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/user/role.rb b/app/models/user/role.rb index a9574a6ef..e12bf0191 100644 --- a/app/models/user/role.rb +++ b/app/models/user/role.rb @@ -18,7 +18,7 @@ module User::Role admin? || other == self end - def can_administer? + def can_administer?(other) admin? && other != self end diff --git a/app/views/account/settings/show.html.erb b/app/views/account/settings/show.html.erb index 9df7b9889..57f3913bf 100644 --- a/app/views/account/settings/show.html.erb +++ b/app/views/account/settings/show.html.erb @@ -3,7 +3,7 @@ <% content_for :header do %>

<%= @page_title %> - <% unless Current.user.can_administer?%> + <% unless Current.user.admin? %>
Only admins can change these settings
<% end %>

From 9c77ad6029cbdb620fe99edae681842910150654 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:41:46 +0100 Subject: [PATCH 24/26] Admins can't edit the board in general --- app/controllers/boards_controller.rb | 14 +++----------- test/controllers/boards_controller_test.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index 4bff8dde4..70383e139 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -2,7 +2,7 @@ class BoardsController < ApplicationController include FilterScoped before_action :set_board, except: %i[ new create ] - before_action :ensure_permission_to_change_accesses, only: %i[ update ] + before_action :ensure_permission_to_admin_board, only: %i[ update ] def show if @filter.used?(ignore_boards: true) @@ -48,20 +48,12 @@ class BoardsController < ApplicationController @board = Current.user.boards.find params[:id] end - def ensure_permission_to_change_accesses - if trying_to_change_accesses? && !Current.user.can_administer_board?(@board) + def ensure_permission_to_admin_board + unless Current.user.can_administer_board?(@board) head :forbidden end end - def trying_to_change_accesses? - all_access_changed? || grantees_changed? - end - - def all_access_changed? - params[:board]&.key?(:all_access) - end - def grantees_changed? params.key?(:user_ids) end diff --git a/test/controllers/boards_controller_test.rb b/test/controllers/boards_controller_test.rb index 14f573780..41cbb557e 100644 --- a/test/controllers/boards_controller_test.rb +++ b/test/controllers/boards_controller_test.rb @@ -122,4 +122,18 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest assert_response :forbidden assert_equal original_users, board.reload.users.sort end + + test "non-admin cannot change board name on board they don't own" do + logout_and_sign_in_as :jz + + board = boards(:writebook) + original_name = board.name + + patch board_path(board), params: { + board: { name: "Hacked Board Name" } + } + + assert_response :forbidden + assert_equal original_name, board.reload.name + end end From 39c88cf1886a9974e7146d6cb4bc831a12b05574 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:45:09 +0100 Subject: [PATCH 25/26] Ensure only admins/creators can touch the board entropy --- app/controllers/boards/entropies_controller.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/controllers/boards/entropies_controller.rb b/app/controllers/boards/entropies_controller.rb index 153dafe5f..493b2ac74 100644 --- a/app/controllers/boards/entropies_controller.rb +++ b/app/controllers/boards/entropies_controller.rb @@ -1,11 +1,19 @@ class Boards::EntropiesController < ApplicationController include BoardScoped + before_action :ensure_permission_to_admin_board + def update @board.entropy.update!(entropy_params) end private + def ensure_permission_to_admin_board + unless Current.user.can_administer_board?(@board) + head :forbidden + end + end + def entropy_params params.expect(board: [ :auto_postpone_period ]) end From 872537f02ca8e110610ceec10f5854627489b871 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 12 Nov 2025 11:48:35 +0100 Subject: [PATCH 26/26] Ensure only admins/creators can publish boards --- .../boards/entropies_controller.rb | 6 ----- .../boards/publications_controller.rb | 2 ++ app/controllers/concerns/board_scoped.rb | 6 +++++ .../boards/entropies_controller_test.rb | 11 ++++++++ .../boards/publications_controller_test.rb | 25 ++++++++++++++++++- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/app/controllers/boards/entropies_controller.rb b/app/controllers/boards/entropies_controller.rb index 493b2ac74..e07272e9b 100644 --- a/app/controllers/boards/entropies_controller.rb +++ b/app/controllers/boards/entropies_controller.rb @@ -8,12 +8,6 @@ class Boards::EntropiesController < ApplicationController end private - def ensure_permission_to_admin_board - unless Current.user.can_administer_board?(@board) - head :forbidden - end - end - def entropy_params params.expect(board: [ :auto_postpone_period ]) end diff --git a/app/controllers/boards/publications_controller.rb b/app/controllers/boards/publications_controller.rb index b7cda07ee..9435bd8bf 100644 --- a/app/controllers/boards/publications_controller.rb +++ b/app/controllers/boards/publications_controller.rb @@ -1,6 +1,8 @@ class Boards::PublicationsController < ApplicationController include BoardScoped + before_action :ensure_permission_to_admin_board + def create @board.publish end diff --git a/app/controllers/concerns/board_scoped.rb b/app/controllers/concerns/board_scoped.rb index 2772c7d01..f4f3a3e2b 100644 --- a/app/controllers/concerns/board_scoped.rb +++ b/app/controllers/concerns/board_scoped.rb @@ -9,4 +9,10 @@ module BoardScoped def set_board @board = Current.user.boards.find(params[:board_id]) end + + def ensure_permission_to_admin_board + unless Current.user.can_administer_board?(@board) + head :forbidden + end + end end diff --git a/test/controllers/boards/entropies_controller_test.rb b/test/controllers/boards/entropies_controller_test.rb index 02338f393..111fe1513 100644 --- a/test/controllers/boards/entropies_controller_test.rb +++ b/test/controllers/boards/entropies_controller_test.rb @@ -13,4 +13,15 @@ class Boards::EntropiesControllerTest < ActionDispatch::IntegrationTest assert_turbo_stream action: :replace, target: dom_id(@board, :entropy) end + + test "update requires board admin permission" do + logout_and_sign_in_as :jz + + original_period = @board.entropy.auto_postpone_period + + put board_entropy_path(@board, format: :turbo_stream), params: { board: { auto_postpone_period: 1.day } } + + assert_response :forbidden + assert_equal original_period, @board.entropy.reload.auto_postpone_period + end end diff --git a/test/controllers/boards/publications_controller_test.rb b/test/controllers/boards/publications_controller_test.rb index 6ce770858..568c21571 100644 --- a/test/controllers/boards/publications_controller_test.rb +++ b/test/controllers/boards/publications_controller_test.rb @@ -21,9 +21,32 @@ class Boards::PublicationsControllerTest < ActionDispatch::IntegrationTest assert @board.published? assert_changes -> { @board.reload.published? }, from: true, to: false do - delete board_publication_path(@board, format: :turbo_streamn) + delete board_publication_path(@board, format: :turbo_stream) end assert_turbo_stream action: :replace, target: dom_id(@board, :publication) end + + test "publish requires board admin permission" do + logout_and_sign_in_as :jz + + assert_not @board.published? + + post board_publication_path(@board, format: :turbo_stream) + + assert_response :forbidden + assert_not @board.reload.published? + end + + test "unpublish requires board admin permission" do + logout_and_sign_in_as :jz + + @board.publish + assert @board.published? + + delete board_publication_path(@board, format: :turbo_stream) + + assert_response :forbidden + assert @board.reload.published? + end end