Merge branch 'main' into update-public-boards

This commit is contained in:
Jason Zimdars
2025-11-12 09:17:03 -06:00
committed by GitHub
55 changed files with 262 additions and 172 deletions
+4
View File
@@ -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;
+10 -7
View File
@@ -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 {
+12 -2
View File
@@ -6,12 +6,22 @@
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) {
--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;
+1 -1
View File
@@ -1,5 +1,5 @@
@layer components {
[data-controller^="tooltip"] {
[data-controller~="tooltip"] {
--tooltip-delay: 750ms;
--tooltip-duration: 150ms;
@@ -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"
@@ -1,5 +1,6 @@
class Account::JoinCodesController < ApplicationController
before_action :set_join_code
before_action :ensure_admin, only: %i[ update destroy ]
def show
end
@@ -1,6 +1,8 @@
class Boards::EntropiesController < ApplicationController
include BoardScoped
before_action :ensure_permission_to_admin_board
def update
@board.entropy.update!(entropy_params)
end
@@ -1,6 +1,8 @@
class Boards::PublicationsController < ApplicationController
include BoardScoped
before_action :ensure_permission_to_admin_board
def create
@board.publish
end
+3 -11
View File
@@ -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
@@ -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
+1 -1
View File
@@ -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
+6
View File
@@ -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
@@ -1,6 +1,8 @@
class Memberships::EmailAddressesController < ApplicationController
require_untenanted_access
layout "public"
before_action :set_membership
rate_limit to: 5, within: 1.hour, only: :create
+2 -2
View File
@@ -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 ("&larr;" + 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
+1 -1
View File
@@ -1,7 +1,7 @@
module ClipboardHelper
def button_to_copy_to_clipboard(url, &)
tag.button class: "btn", data: {
controller: "copy-to-clipboard", action: "copy-to-clipboard#copy",
controller: "copy-to-clipboard tooltip", action: "copy-to-clipboard#copy",
copy_to_clipboard_success_class: "btn--success", copy_to_clipboard_content_value: url
}, &
end
+1 -1
View File
@@ -18,7 +18,7 @@ module User::Role
admin? || other == self
end
def can_administer?(other = nil)
def can_administer?(other)
admin? && other != self
end
+5 -2
View File
@@ -2,8 +2,11 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to account_join_code_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">&larr; <strong>Invite link</strong></span>
<%= link_to account_join_code_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %>
<span class="overflow-ellipsis flex align-center">
<strong>Back to Invite link</strong>
<kbd class="txt-x-small margin-inline-start hide-on-touch">&larr;</kbd>
</span>
<% end %>
</div>
<% end %>
+7 -4
View File
@@ -2,8 +2,11 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to account_settings_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">&larr; <strong>Account Settings</strong></span>
<%= link_to account_settings_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %>
<span class="overflow-ellipsis flex align-center">
<strong>Back to Account Settings</strong>
<kbd class="txt-x-small margin-inline-start hide-on-touch">&larr;</kbd>
</span>
<% end %>
</div>
<% end %>
@@ -18,7 +21,7 @@
<div class="flex align-center gap-half">
<input type="text" class="input flex-item-grow" value="<%= url %>" readonly>
<% 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" %>
@@ -62,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 %>
<span>Change limit</span>
<% end %>
+1 -1
View File
@@ -4,5 +4,5 @@
<p class="margin-none-block-start">Fizzy doesnt let stale cards stick around forever. Cards automatically close as “Not Now” without activity for specific period of time. <em>This is the default, global setting — you can override it on each board.</em></p>
</header>
<%= 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? %>
</div>
+2 -2
View File
@@ -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| %>
<strong class="full-width"><%= 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? %></strong>
<strong class="full-width"><%= 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? %></strong>
<% 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" %>
<span class="for-screen-reader">Save changes</span>
+1 -1
View File
@@ -3,7 +3,7 @@
<% content_for :header do %>
<h1 class="header__title">
<%= @page_title %>
<% unless Current.user.can_administer?%>
<% unless Current.user.admin? %>
<div class="txt-normal font-weight-normal">Only admins can change these settings</div>
<% end %>
</h1>
@@ -2,12 +2,7 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong><%= @board.name %></strong>
</span>
<% end %>
<%= link_back_to_board(@board) %>
</div>
<h1 class="header__title divider divider--fade full-width">
@@ -2,12 +2,7 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong><%= @board.name %></strong>
</span>
<% end %>
<%= link_back_to_board(@board) %>
</div>
<h1 class="header__title divider divider--fade full-width">
+1 -6
View File
@@ -2,12 +2,7 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to @column.board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong><%= @column.board.name %></strong>
</span>
<% end %>
<%= link_back_to_board(@column.board) %>
</div>
<h1 class="header__title divider divider--fade full-width">
@@ -2,12 +2,7 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong><%= @board.name %></strong>
</span>
<% end %>
<%= link_back_to_board(@board) %>
</div>
<h1 class="header__title divider divider--fade full-width">
+1 -6
View File
@@ -2,12 +2,7 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong><%= @board.name %></strong>
</span>
<% end %>
<%= link_back_to_board(@board) %>
</div>
<h1 class="header__title">
+2 -1
View File
@@ -1,6 +1,7 @@
<div class="cards__new-column position-relative" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside" data-turbo-permanent>
<button class="btn btn--circle txt-x-small borderless" data-action="click->dialog#open:stop" aria-label="Add a new column, etc.">
<button class="btn btn--circle txt-x-small borderless" data-controller="tooltip" data-action="click->dialog#open:stop">
<%= icon_tag "add", class: "translucent" %>
<span class="for-screen-reader">Add a column</span>
</button>
<dialog class="popup panel flex-column gap-half fill-white shadow txt-small margin-block-double" data-dialog-target="dialog">
<%= render "boards/show/menu/column_form", board: board, column: Column.new, label: "Add column" %>
+8 -5
View File
@@ -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 %>
<span>Post this comment</span>
<span class="hide-on-touch">
<span class="flex-inline align-center gap">
<%= form.button class: "comment__submit btn btn--reversed",
data: { form_target: "submit" }, disabled: true do %>
<span>Post</span>
<% end %>
<span class="hide-on-touch txt-subtle">
<kbd class="txt-x-small"><%= hotkey_label([ "ctrl", "enter" ]) %></kbd>
</span>
<% end %>
</span>
<span data-form-target="cancel" hidden></span>
<% end %>
</div>
+5 -2
View File
@@ -10,7 +10,10 @@
</div>
<% end %>
<%= link_to "Edit", edit_card_path(card), class: "btn btn--reversed fit-content", style: "--btn-background: #{card.color}" %>
<%= link_to edit_card_path(card), class: "btn fit-content txt-small", data: { controller: "hotkey", action: "keydown.e@document->hotkey#click" } do %>
<span>Edit</span>
<kbd class="txt-x-small hide-on-touch">e</kbd>
<% end %>
<% end %>
<% else %>
<%= form_with model: card, id: "card_form", data: { controller: "autoresize auto-save" } do |form| %>
@@ -23,7 +26,7 @@
<% end %>
</h1>
<%= 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) %>
+5 -2
View File
@@ -9,13 +9,16 @@
<% end %>
</h1>
<%= 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 type: :submit, class: "btn btn--reversed fit-content txt-small", data: { form_target: "submit" } do %>
<span>Save changes</span>
<kbd class="txt-x-small hide-on-touch"><%= hotkey_label([ "ctrl", "enter" ]) %></kdb>
<% end %>
<%= link_to "Close editor and discard changes", @card, data: { form_target: "cancel" }, hidden: true %>
<% end %>
<% end %>
+4 -4
View File
@@ -1,13 +1,13 @@
<div id="filter-settings-save-toggle">
<% if filter.persisted? %>
<%= button_to filter_path(filter), method: :delete, class: "btn txt-x-small btn--reversed", form_class: "inline" do %>
<%= button_to filter_path(filter), method: :delete, class: "btn txt-x-small btn--reversed", data: { controller: "tooltip" }, form_class: "inline" do %>
<%= icon_tag "bookmark-outline" %>
<span class="for-screen-reader">Remove saved filter</span>
<span class="for-screen-reader">Delete custom view</span>
<% end %>
<% else %>
<%= button_to filters_path(filter.as_params), method: :post, class: "btn txt-x-small", form_class: "inline" do %>
<%= button_to filters_path(filter.as_params), method: :post, class: "btn txt-x-small", data: { controller: "tooltip" }, form_class: "inline" do %>
<%= icon_tag "bookmark-outline" %>
<span class="for-screen-reader">Save filter</span>
<span class="for-screen-reader">Save custom view</span>
<% end %>
<% end %>
</div>
+1 -1
View File
@@ -3,7 +3,7 @@
<div class="filters__manage gap-half">
<%= render "filters/filter_toggle", filter: filter %>
<%= link_to no_filtering_url, class: "btn btn--remove txt-x-small", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click"} do %>
<%= link_to no_filtering_url, class: "btn btn--remove txt-x-small", data: { controller: "hotkey tooltip", action: "keydown.esc@document->hotkey#click"} do %>
<%= icon_tag "close" %>
<span class="for-screen-reader">Clear all</span>
<% end %>
+4 -2
View File
@@ -1,11 +1,13 @@
<% filter = user_filtering.filter %>
<% if user_filtering.expanded? %>
<button type="button" class="btn txt-x-small btn--reversed" aria-selected="true" aria-label="Close filter options" data-action="toggle-class#toggle toggle-enable#toggle">
<button type="button" class="btn txt-x-small btn--reversed" aria-selected="true" data-action="toggle-class#toggle toggle-enable#toggle" data-controller="tooltip">
<%= icon_tag "filter" %>
<span class="for-screen-reader">Close filter options</span>
</button>
<% else %>
<button type="button" class="btn txt-x-small filter-toggle" aria-label="Expand filter options" data-action="toggle-class#toggle toggle-enable#toggle">
<button type="button" class="btn txt-x-small filter-toggle" data-action="toggle-class#toggle toggle-enable#toggle" data-controller="tooltip">
<%= icon_tag "filter" %>
<span class="for-screen-reader">Expand filter options</span>
</button>
<% end %>
@@ -1,14 +1,17 @@
<% @page_title = "Confirm your new email address" %>
<% content_for :header do %>
<div class="header__title">
<%= 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 %>
<span class="overflow-ellipsis">&larr; <strong>My profile</strong></span>
<div class="header__actions header__actions--start">
<%= 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 %>
<span class="overflow-ellipsis flex align-center">
<strong>Back to My profile</strong>
<kbd class="txt-x-small margin-inline-start hide-on-touch">&larr;</kbd>
</span>
<% end %>
</div>
<% end %>
<div class="panel shadow center flex flex-column gap-half" style="--panel-size: 45ch;">
<div class="panel panel--centered flex flex-column gap-half">
<h1 class="txt-x-large font-weight-black margin-none">Check your email</h1>
<p class="margin-none">We just sent an email to <strong><%= params[:email_address] %></strong></p>
<p class="margin-none-block-start">Hit the link in the email to confirm this is the email address you want to use with Fizzy going forward.</p>
@@ -1,28 +1,30 @@
<% @page_title = "Change your email" %>
<% content_for :header do %>
<div class="header__title">
<%= 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 %>
<span class="overflow-ellipsis">&larr; <strong>My profile</strong></span>
<div class="header__actions header__actions--start">
<%= 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 %>
<span class="overflow-ellipsis flex align-center">
<strong>Back to My profile</strong>
<kbd class="txt-x-small margin-inline-start hide-on-touch">&larr;</kbd>
</span>
<% end %>
</div>
<% end %>
<div class="panel shadow center flex flex-column gap" style="--panel-size: 45ch;">
<header>
<h1 class="txt-x-large font-weight-black margin-none">
<%= @page_title %>
</h1>
<p class="margin-none">Enter your new email address, then hit the link to confirm it</p>
</header>
<div class="panel panel--centered flex flex-column gap-half">
<h1 class="txt-x-large font-weight-black margin-none">
<%= @page_title %>
</h1>
<%= form_with url: email_addresses_path(membership_id: @membership.id), method: :post, class: "flex flex-column gap", data: { controller: "form", turbo: false } do |form| %>
<div class="flex align-center gap">
<%= 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| %>
<div class="flex align-center gap-haf">
<label class="flex align-center gap input input--actor">
<%= form.email_field :email_address, class: "input full-width", autocomplete: "email", placeholder: "New email address", autofocus: true, required: true %>
</label>
</div>
<p class="margin-none">Enter your new email address, then hit the link to confirm it</p>
<button type="submit" class="btn btn--link center" data-form-target="submit">
<span>Continue</span>
<%= icon_tag "arrow-right" %>
-3
View File
@@ -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" %>
</div>
<%= yield %>
+18 -18
View File
@@ -1,20 +1,5 @@
<%= turbo_frame_tag "my_menu", target: "_top" do %>
<%= render "my/menus/jump" do %>
<%= collapsible_nav_section "Saved filters", id: "my-filters" do %>
<%= form_with url: cards_path, method: :get, data: { controller: "form" } do |form| %>
<li class="popup__item overflow-ellipsis" data-navigable-list-target="item" data-filter-target="item" id="filter-custom-create">
<%= icon_tag "bookmark", class: "popup__icon" %>
<%= link_to cards_path(expand_all: true), class: "popup__btn btn" do %>
<span>Add a filter</span>
<% end %>
</li>
<% @filters.each do |filter| %>
<%= my_menu_filter_item(filter) %>
<% end %>
<% end %>
<% end %>
<%= collapsible_nav_section "Boards" do %>
<li class="popup__item" data-filter-target="item" data-navigable-list-target="item">
<%= icon_tag "add", class: "popup__icon" %>
@@ -28,6 +13,21 @@
<% end %>
<% end %>
<%= collapsible_nav_section "Custom views", id: "my-filters" do %>
<%= form_with url: cards_path, method: :get, data: { controller: "form" } do |form| %>
<li class="popup__item overflow-ellipsis" data-navigable-list-target="item" data-filter-target="item" id="filter-custom-create">
<%= icon_tag "bookmark", class: "popup__icon" %>
<%= link_to cards_path(expand_all: true), class: "popup__btn btn" do %>
<span>Create a custom view</span>
<% end %>
</li>
<% @filters.each do |filter| %>
<%= my_menu_filter_item(filter) %>
<% end %>
<% end %>
<% end %>
<%= collapsible_nav_section "Tags" do %>
<% @tags.each do |tag| %>
<%= my_menu_tag_item(tag) %>
@@ -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 %>
@@ -65,6 +65,6 @@
<% end %>
<footer class="nav__footer">
<strong><%= link_to "Fizzy", "https://www.fizzy.do", class: "txt-ink", target: "_blank" %></strong> is designed, built, and backed by <strong class="txt-nowrap"><%= icon_tag "37signals", class: "v-align-middle", target: "_blank" %> <a href="https://37signals.com" target="_blank" class="txt-ink">37signals</a></strong>
<strong><%= link_to "Fizzy", "https://www.fizzy.do", class: "txt-ink", target: "_blank" %></strong> is designed, built, and backed by <strong class="txt-nowrap"><%= icon_tag "37signals", class: "v-align-middle", target: "_blank" %> <a href="https://37signals.com" target="_blank" class="txt-ink">37signals</a></strong>
</footer>
<% end %>
+4 -4
View File
@@ -14,10 +14,10 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to published_board_url(@card.board), class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %>
<span class="flex align-center">
<strong class="overflow-ellipsis">Back to <%= @card.board.name %></strong>
<kbd class="txt-x-small hide-on-touch margin-inline-start">&larr;</kbd>
<%= link_to published_board_url(@card.board), class: "btn borderless txt-medium", style: "view-transistion-name: card-board-title;", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %>
<span class="overflow-ellipsis flex align-center">
<strong>Back to <%= @card.board.name %></strong>
<kbd class="txt-x-small margin-inline-start hide-on-touch">&larr;</kbd>
</span>
<% end %>
</div>
+2 -2
View File
@@ -1,4 +1,4 @@
<div class="txt-align-center center margin-block-double txt-subtle txt-small">
<strong><%= link_to "Fizzy", "https://www.fizzy.do", class: "txt-subtle" %></strong> is designed, built, and backed by <strong class="txt-nowrap"><%= icon_tag "37signals", class: "v-align-middle" %> <a href="https://37signals.com" target="_blank" class="txt-subtle">37signals</a></strong>.
<div class="pad-inline txt-align-center center margin-block-double txt-subtle txt-small">
<strong><%= link_to "Fizzy", "https://www.fizzy.do", class: "txt-subtle" %></strong> is designed, built, and backed by <strong class="txt-nowrap"><%= icon_tag "37signals", class: "v-align-middle" %> <a href="https://37signals.com" target="_blank" class="txt-subtle">37signals</a></strong>.
Need help? <%= mail_to "support@fizzy.do", "Send us an email", class: "txt-link" %>
</div>
+1 -1
View File
@@ -3,7 +3,7 @@
<div class="panel panel--centered flex flex-column gap-half <%= "shake" if flash[:alert] %>">
<header>
<h1 class="txt-x-large font-weight-black margin-none"><%= @page_title %></h1>
<p class="margin-none-block-start txt-medium">Then enter the verfifcation code included in the email below:</p>
<p class="margin-none-block-start txt-medium">Then enter the verification code included in the email below:</p>
</header>
<%= form_with url: session_magic_link_path, method: :post, html: { data: { controller: token_list("form", "auto-submit" => params[:code].present?)} } do |form| %>
+2 -2
View File
@@ -1,9 +1,9 @@
<% @page_title = "Enter your email" %>
<div class="panel panel--centered flex flex-column gap-half" style="--panel-size: 65ch;">
<div class="panel panel--centered flex flex-column gap-half">
<h1 class="txt-x-large font-weight-black margin-block-end">Lets get you in to Fizzy</h1>
<%= form_with url: session_path, class: "flex flex-column gap txt-medium" do |form| %>
<%= form_with url: session_path, class: "flex flex-column gap-half txt-medium" do |form| %>
<div class="flex align-center gap">
<label class="flex align-center gap input input--actor">
<%= form.email_field :email_address, required: true, class: "input txt-large full-width", autofocus: true, autocomplete: "username", placeholder: "Enter your email address…" %>
+4 -4
View File
@@ -12,18 +12,18 @@
<div class="flex align-center gap">
<div data-controller="dialog" data-dialog-modal-value="true" class="flex-inline">
<%= tag.button class: "btn", data: { action: "dialog#open" } do %>
<%= tag.button class: "btn", data: { action: "dialog#open", controller: "tooltip" } do %>
<%= icon_tag "qr-code" %>
<span class="for-screen-reader">Show auto-login QR code</span>
<span class="for-screen-reader">Display auto-login QR code</span>
<% end %>
<dialog class="dialog panel shadow" data-dialog-target="dialog">
<%= qr_code_image(url) %>
<form method="dialog" class="margin-block-start flex justify-center">
<button class="btn">
<button class="btn" data-controller="tooltip">
<%= icon_tag "remove" %>
<span class="for-screen-reader">Close</span>
<span class="for-screen-reader">Close dialog</span>
</button>
</form>
</dialog>
+11
View File
@@ -1,5 +1,16 @@
<% @page_title = "Edit your profile" %>
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to user_path(@user), class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %>
<span class="overflow-ellipsis flex align-center">
<strong>Back to profile</strong>
<kbd class="txt-x-small margin-inline-start hide-on-touch">&larr;</kbd>
</span>
<% end %>
</div>
<% end %>
<div class="panel shadow center" style="--panel-size: 45ch;">
<div class="flex flex-column gap txt-medium">
<%= form_with model: @user, method: :patch, class: "flex flex-column gap", data: { controller: "form upload-preview" } do |form| %>
+4 -4
View File
@@ -5,9 +5,9 @@
<section class="panel shadow txt-align-center" style="--panel-size: 45ch;">
<div class="flex flex-column gap position-relative">
<% 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" %>
<span class="for-screen-reader">Edit</span>
<span class="for-screen-reader">Edit profile</span>
<% end %>
<% end %>
@@ -27,9 +27,9 @@
</div>
<div class="flex-inline center justify-center flex-wrap gap">
<%= 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" } %>
</div>
</div>
+4 -4
View File
@@ -2,10 +2,10 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to @webhook, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong><%= @page_title %></strong>
<%= link_to @webhook, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %>
<span class="overflow-ellipsis flex align-center">
<strong>Back to <%= @page_title %></strong>
<kbd class="txt-x-small margin-inline-start hide-on-touch">&larr;</kbd>
</span>
<% end %>
</div>
+7 -18
View File
@@ -2,35 +2,24 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to @board, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong><%= @board.name %></strong>
</span>
<% end %>
<%= link_back_to_board(@board) %>
</div>
<h1 class="header__title"><%= @page_title %></h1>
<div class="header__actions header__actions--end">
<%= link_to new_board_webhook_path, class: "btn" do %>
<%= icon_tag "add" %>
<span class="for-screen-reader">Create a new webhook</span>
<% end %>
</div>
<% end %>
<%= tag.section class: "panel shadow center webhooks" do %>
<% if @page.records.any? %>
<ul class="flex flex-column align-start gap margin-none unpad webhooks-list">
<ul class="flex flex-column align-start gap margin-none-block-start unpad webhooks-list">
<%= render partial: "webhooks/webhook", collection: @page.records %>
</ul>
<% else %>
<p>Webhooks can notify another application when something happens in this Fizzy board. Youll choose which events to subscribe to and provide a URL to receive the data.</p>
<p>For example, you could create a webhook that posts to a Campfire chat in Basecamp when new cards are added to Fizzy.</p>
<%= link_to new_board_webhook_path, class: "btn btn--link" do %>
<%= icon_tag "add" %>
<span>Set up a webhook</span>
<% end %>
<% end %>
<%= link_to new_board_webhook_path, class: "btn btn--link" do %>
<%= icon_tag "add" %>
<span>Set up a new webhook</span>
<% end %>
<% end %>
+1 -6
View File
@@ -2,12 +2,7 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to board_webhooks_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong>Webhooks</strong>
</span>
<% end %>
<%= link_back_to_board(@board) %>
</div>
<h1 class="header__title"><%= @page_title %></h1>
+4 -4
View File
@@ -2,10 +2,10 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_to board_webhooks_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="overflow-ellipsis">
&larr;
<strong>Webhooks</strong>
<%= link_to board_webhooks_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.left@document->hotkey#click" } do %>
<span class="overflow-ellipsis flex align-center">
<strong>Back to Webhooks</strong>
<kbd class="txt-x-small margin-inline-start hide-on-touch">&larr;</kbd>
</span>
<% end %>
</div>
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
+1 -1
View File
@@ -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']"