From 626aa0ea8a4300a7089e85526c613db295219df4 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 15 Oct 2025 12:51:06 -0500 Subject: [PATCH 01/12] Don't animate expanding columns when restoring --- app/assets/stylesheets/card-columns.css | 5 +++++ .../collapsible_columns_controller.js | 20 ++++++++++++++++++- app/views/collections/show/_columns.html.erb | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/card-columns.css b/app/assets/stylesheets/card-columns.css index 324c02997..2ef0d65d2 100644 --- a/app/assets/stylesheets/card-columns.css +++ b/app/assets/stylesheets/card-columns.css @@ -72,6 +72,11 @@ scroll-snap-align: start; transition: var(--column-transition); + /* Disable transitions during restoration */ + &.no-transitions { + transition: none; + } + /* Make sure filter dialogs don't end up behind sticky headers */ &:has([open]) { z-index: 3; diff --git a/app/javascript/controllers/collapsible_columns_controller.js b/app/javascript/controllers/collapsible_columns_controller.js index 5002c46e2..735b090ba 100644 --- a/app/javascript/controllers/collapsible_columns_controller.js +++ b/app/javascript/controllers/collapsible_columns_controller.js @@ -1,11 +1,13 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { - static classes = [ "collapsed" ] + static classes = [ "collapsed", "noTransitions" ] static targets = [ "column", "button" ] connect() { + this.#disableTransitions() this.#restoreColumns() + requestAnimationFrame(() => this.#enableTransitions()) } toggle({ target }) { @@ -19,6 +21,22 @@ export default class extends Controller { } } + #disableTransitions() { + this.columnTargets.forEach(column => { + if (this.noTransitionsClass) { + column.classList.add(this.noTransitionsClass) + } + }) + } + + #enableTransitions() { + this.columnTargets.forEach(column => { + if (this.noTransitionsClass) { + column.classList.remove(this.noTransitionsClass) + } + }) + } + #toggleColumn(column) { this.#collapseAllExcept(column) diff --git a/app/views/collections/show/_columns.html.erb b/app/views/collections/show/_columns.html.erb index 06862a158..084f49fbe 100644 --- a/app/views/collections/show/_columns.html.erb +++ b/app/views/collections/show/_columns.html.erb @@ -8,7 +8,7 @@ dragenter->drag-and-strum#dragEnter drop->drag-and-drop#drop dragend->drag-and-drop#dragEnd" } do %> -
+
<%= render "collections/show/not_now", collection: collection %>
From a6c692affec63ba5b1b4ebe3fed188fe59d3bbad Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 15 Oct 2025 14:02:08 -0500 Subject: [PATCH 02/12] Just close as done --- app/assets/stylesheets/expander.css | 111 -------------------- app/views/cards/container/_closure.html.erb | 17 +-- 2 files changed, 3 insertions(+), 125 deletions(-) delete mode 100644 app/assets/stylesheets/expander.css diff --git a/app/assets/stylesheets/expander.css b/app/assets/stylesheets/expander.css deleted file mode 100644 index 0182f6497..000000000 --- a/app/assets/stylesheets/expander.css +++ /dev/null @@ -1,111 +0,0 @@ -/* https://nerdy.dev/open-and-close-transitions-for-the-details-element */ - -.expander { - --expander-height: calc(1em + 1lh); - --expander-width: 11ch; - --expander-radius: calc(var(--expander-height) / 2); - --expander-duration: 150ms; - - interpolate-size: allow-keywords; - position: relative; - z-index: 1; -} - -.expander__button { - --btn-border-radius: var(--expander-radius); - - align-items: center; - block-size: var(--expander-height); - cursor: pointer; - display: flex; - inline-size: var(--expander-width); - justify-content: flex-start; - padding-inline: 1.5ch; - position: relative; - white-space: nowrap; - z-index: 1; - - &::-webkit-details-marker { - display: none; - } - - [open] & { - filter: none; - } - - .icon { - font-size: 1ch; - } -} - -.expander__content { - --btn-border-radius: var(--expander-radius); - - align-items: stretch; - border-radius: var(--expander-radius); - flex-direction: column; - filter: none; - gap: 0; - padding-block-start: var(--expander-height); - - &:hover { - box-shadow: none; - } -} - -/* For browsers that support animation */ -@supports selector(details::details-content) { - .expander::details-content { - --btn-border-radius: var(--expander-radius); - - block-size: var(--expander-height); - border-radius: var(--expander-radius); - inset: 0 auto auto 0; - inline-size: var(--expander-width); - overflow: clip; - position: absolute; - transition: content-visibility var(--expander-duration) allow-discrete, block-size var(--expander-duration), inline-size var(--expander-duration); - } - - .expander[open]::details-content { - block-size: auto; - inline-size: auto; - } - - .expander__content { - min-inline-size: var(--expander-width); - } -} - -/* Fallback for browsers that don't support animation */ -@supports not selector(details::details-content) { - .expander__content { - border-radius: var(--expander-radius); - inset: 0 auto auto 0; - min-inline-size: var(--expander-width); - position: absolute; - - [open] & { - block-size: auto; - inline-size: auto; - } - } -} - -.expander__item { - --btn-background: var(--card-color); - --btn-border-radius: 0.3em; - --btn-border-size: 0; - --btn-font-weight: 500; - --btn-padding: var(--inline-space-half) var(--inline-space); - - inline-size: calc(100% + var(--inline-space-double)); - margin-inline-start: calc(-1 * var(--inline-space)); - justify-content: flex-start; - white-space: nowrap; - - /* &:hover, - &:focus-visible { - background-color: oklch(var(--lch-canvas) / 15%); - } */ -} diff --git a/app/views/cards/container/_closure.html.erb b/app/views/cards/container/_closure.html.erb index 772b86f5e..80a8751a5 100644 --- a/app/views/cards/container/_closure.html.erb +++ b/app/views/cards/container/_closure.html.erb @@ -7,19 +7,9 @@ <% end %>
<% else %> -
- - Close as - <%= icon_tag "caret-down" %> - -
- <% Closure::Reason.labels.each do |label| %> - <%= button_to card_closure_path(card, reason: label), class: "expander__item btn" do %> - <%= label %> - <% end %> - <% end %> -
-
+ <%= button_to card_closure_path(card, reason: "Done"), class: "btn borderless" do %> + Mark as Done + <% end %> <% if card.entropic? && card.open? %>
Closes as “Not Now” <%= local_datetime_tag(card.entropy.auto_clean_at, style: :indays) -%> if there’s no activity. @@ -27,4 +17,3 @@ <% end %> <% end %>
- From c1bcab96f07ee6ce4eb8f811e4fb9e53c763cb39 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 15 Oct 2025 14:10:38 -0500 Subject: [PATCH 03/12] They're all just *done* --- app/views/cards/display/common/_background.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/cards/display/common/_background.html.erb b/app/views/cards/display/common/_background.html.erb index 16ac8357e..c868c6c13 100644 --- a/app/views/cards/display/common/_background.html.erb +++ b/app/views/cards/display/common/_background.html.erb @@ -16,7 +16,7 @@ <% if card.closed? %>
- <%= card.closure.reason %> + Done <%= card.closed_at.strftime("%b %d, %Y") %>
From da413e75403e3617db5538339f5508bbaa23f2a6 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 15 Oct 2025 16:15:56 -0500 Subject: [PATCH 04/12] Remove closure reasons --- app/controllers/cards/closures_controller.rb | 2 +- app/helpers/events_helper.rb | 2 +- app/helpers/notifications_helper.rb | 7 +- app/helpers/webhooks_helper.rb | 2 +- app/models/account.rb | 1 - app/models/card/closeable.rb | 4 +- app/models/card/eventable/system_commenter.rb | 2 +- app/models/closure/reason.rb | 27 ------ app/models/notification_pusher.rb | 2 +- app/views/cards/container/_closure.html.erb | 2 +- app/views/cards/triage/_columns.html.erb | 2 +- .../20251015204327_remove_closure_reasons.rb | 5 + ...51015205212_remove_reason_from_closures.rb | 5 + db/schema.rb | 20 ++-- db/schema_cache.yml | 92 +++++++++++++------ db/untenanted_schema.rb | 14 ++- .../cards/closures_controller_test.rb | 4 +- test/fixtures/closures.yml | 3 +- .../card/eventable/system_commenter_test.rb | 2 +- 19 files changed, 112 insertions(+), 86 deletions(-) delete mode 100644 app/models/closure/reason.rb create mode 100644 db/migrate/20251015204327_remove_closure_reasons.rb create mode 100644 db/migrate/20251015205212_remove_reason_from_closures.rb diff --git a/app/controllers/cards/closures_controller.rb b/app/controllers/cards/closures_controller.rb index c2df13433..b89395cd5 100644 --- a/app/controllers/cards/closures_controller.rb +++ b/app/controllers/cards/closures_controller.rb @@ -2,7 +2,7 @@ class Cards::ClosuresController < ApplicationController include CardScoped def create - @card.close(user: Current.user, reason: params[:reason]) + @card.close(user: Current.user) render_card_replacement end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index b037c63c4..031236b1c 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -84,7 +84,7 @@ module EventsHelper when "card_published" "#{h event_creator_name(event) } added #{h title }".html_safe when "card_closed" - "#{h event_creator_name(event) } closed #{h title }".html_safe + "#{h event_creator_name(event) } marked #{h title } as done".html_safe when "card_reopened" "#{h event_creator_name(event) } reopened #{h title }".html_safe when "card_due_date_added" diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index cd9e005a9..2f0456f78 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -10,12 +10,7 @@ module NotificationsHelper name = event.creator.name case event_notification_action(event) - when "card_closed" - if event.card.closure - "Closed as “#{event.card.closure.reason}” by #{name}" - else - "Closed by #{name}" - end + when "card_closed" then "Marked as “Done” by #{name}" when "card_reopened" then "Reopened by #{name}" when "card_published" then "Added by #{name}" when "comment_created" then comment_notification_body(event) diff --git a/app/helpers/webhooks_helper.rb b/app/helpers/webhooks_helper.rb index dfb61c922..42c12c404 100644 --- a/app/helpers/webhooks_helper.rb +++ b/app/helpers/webhooks_helper.rb @@ -1,7 +1,7 @@ module WebhooksHelper ACTION_LABELS = { card_assigned: "Card assigned", - card_closed: "Card closed", + card_closed: "Card marked as Done", card_collection_changed: "Card collection changed", card_due_date_added: "Card due date added", card_due_date_changed: "Card due date changed", diff --git a/app/models/account.rb b/app/models/account.rb index 5e97480ce..c7c00f979 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -17,7 +17,6 @@ class Account < ApplicationRecord def setup_basic_template user = User.first - Closure::Reason.create_defaults Collection.create!(name: "Cards", creator: user, all_access: true) end end diff --git a/app/models/card/closeable.rb b/app/models/card/closeable.rb index f9b045239..51852c9a5 100644 --- a/app/models/card/closeable.rb +++ b/app/models/card/closeable.rb @@ -28,10 +28,10 @@ module Card::Closeable closure&.created_at end - def close(user: Current.user, reason: Closure::Reason.default) + def close(user: Current.user) unless closed? transaction do - create_closure! user: user, reason: reason + create_closure! user: user track_event :closed, creator: user end end diff --git a/app/models/card/eventable/system_commenter.rb b/app/models/card/eventable/system_commenter.rb index 65a6b8a36..29f981173 100644 --- a/app/models/card/eventable/system_commenter.rb +++ b/app/models/card/eventable/system_commenter.rb @@ -19,7 +19,7 @@ class Card::Eventable::SystemCommenter when "card_unassigned" "#{event.creator.name} unassigned from #{event.assignees.pluck(:name).to_sentence}." when "card_closed" - "Closed as ‘#{ card.closure.reason }’ by #{ event.creator.name }" + "Marked as “Done” by #{ event.creator.name }" when "card_reopened" "Reopened by #{ event.creator.name }" when "card_title_changed" diff --git a/app/models/closure/reason.rb b/app/models/closure/reason.rb deleted file mode 100644 index f071901c1..000000000 --- a/app/models/closure/reason.rb +++ /dev/null @@ -1,27 +0,0 @@ -class Closure::Reason < ApplicationRecord - DEFAULT_LABELS = [ - "Completed", - "Duplicate", - "Maybe later", - "Working as intended", - "Not now" - ] - - FALLBACK_LABEL = "Done" - - class << self - def labels - pluck(:label).presence || [ FALLBACK_LABEL ] - end - - def default - labels.first - end - - def create_defaults - DEFAULT_LABELS.each do |label| - create! label: label - end - end - end -end diff --git a/app/models/notification_pusher.rb b/app/models/notification_pusher.rb index a5784cf2e..000a80fff 100644 --- a/app/models/notification_pusher.rb +++ b/app/models/notification_pusher.rb @@ -59,7 +59,7 @@ class NotificationPusher ) when "card_closed" base_payload.merge( - body: card.closure ? "Closed as \"#{card.closure.reason}\" by #{event.creator.name}" : "Closed by #{event.creator.name}" + body: card.closure ? "Marked as “Done” by #{event.creator.name}" : "Closed by #{event.creator.name}" ) when "card_reopened" base_payload.merge( diff --git a/app/views/cards/container/_closure.html.erb b/app/views/cards/container/_closure.html.erb index 80a8751a5..bad02f3cd 100644 --- a/app/views/cards/container/_closure.html.erb +++ b/app/views/cards/container/_closure.html.erb @@ -7,7 +7,7 @@ <% end %>
<% else %> - <%= button_to card_closure_path(card, reason: "Done"), class: "btn borderless" do %> + <%= button_to card_closure_path(card), class: "btn borderless" do %> Mark as Done <% end %> <% if card.entropic? && card.open? %> diff --git a/app/views/cards/triage/_columns.html.erb b/app/views/cards/triage/_columns.html.erb index c77f06529..0222b48f6 100644 --- a/app/views/cards/triage/_columns.html.erb +++ b/app/views/cards/triage/_columns.html.erb @@ -17,7 +17,7 @@ <%= button_to_set_column card, column %> <% end %> - <%= button_to "Done", card_closure_path(card, reason: "Completed"), + <%= button_to "Done", card_closure_path(card), class: [ "card__column-name btn", { "card__column-name--current": card.closed? } ], role: "radio", aria: { checked: card.closed? }, diff --git a/db/migrate/20251015204327_remove_closure_reasons.rb b/db/migrate/20251015204327_remove_closure_reasons.rb new file mode 100644 index 000000000..6e376a222 --- /dev/null +++ b/db/migrate/20251015204327_remove_closure_reasons.rb @@ -0,0 +1,5 @@ +class RemoveClosureReasons < ActiveRecord::Migration[8.1] + def change + drop_table :closure_reasons + end +end diff --git a/db/migrate/20251015205212_remove_reason_from_closures.rb b/db/migrate/20251015205212_remove_reason_from_closures.rb new file mode 100644 index 000000000..1ca3f465d --- /dev/null +++ b/db/migrate/20251015205212_remove_reason_from_closures.rb @@ -0,0 +1,5 @@ +class RemoveReasonFromClosures < ActiveRecord::Migration[8.1] + def change + remove_column :closures, :reason + end +end diff --git a/db/schema.rb b/db/schema.rb index b6d4d1ffe..53935ecff 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2025_10_15_123003) do +ActiveRecord::Schema[8.1].define(version: 2025_10_15_205212) do create_table "accesses", force: :cascade do |t| t.datetime "accessed_at" t.integer "collection_id", null: false @@ -29,6 +29,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_15_123003) do t.integer "external_account_id" t.string "join_code" t.string "name", null: false + t.string "setup_status" t.datetime "updated_at", null: false t.index ["external_account_id"], name: "index_accounts_on_external_account_id", unique: true end @@ -162,16 +163,9 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_15_123003) do t.index ["filter_id"], name: "index_closers_filters_on_filter_id" end - create_table "closure_reasons", force: :cascade do |t| - t.datetime "created_at", null: false - t.string "label" - t.datetime "updated_at", null: false - end - create_table "closures", force: :cascade do |t| t.integer "card_id", null: false t.datetime "created_at", null: false - t.string "reason", null: false t.datetime "updated_at", null: false t.integer "user_id" t.index ["card_id", "created_at"], name: "index_closures_on_card_id_and_created_at" @@ -293,6 +287,15 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_15_123003) do t.index ["tag_id"], name: "index_filters_tags_on_tag_id" end + create_table "integrations", force: :cascade do |t| + t.datetime "created_at", null: false + t.text "data" + t.integer "owner_id", null: false + t.string "type" + t.datetime "updated_at", null: false + t.index ["owner_id"], name: "index_integrations_on_owner_id" + end + create_table "mentions", force: :cascade do |t| t.datetime "created_at", null: false t.integer "mentionee_id", null: false @@ -522,6 +525,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_15_123003) do add_foreign_key "conversation_messages", "conversations" add_foreign_key "conversations", "users" add_foreign_key "events", "collections" + add_foreign_key "integrations", "users", column: "owner_id" add_foreign_key "mentions", "users", column: "mentionee_id" add_foreign_key "mentions", "users", column: "mentioner_id" add_foreign_key "notification_bundles", "users" diff --git a/db/schema_cache.yml b/db/schema_cache.yml index 68e51777c..de42a9e4f 100644 --- a/db/schema_cache.yml +++ b/db/schema_cache.yml @@ -133,6 +133,16 @@ columns: default_function: collation: comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: setup_status + cast_type: *7 + sql_type_metadata: *8 + 'null': true + default: + default_function: + collation: + comment: - *9 action_text_rich_texts: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column @@ -534,34 +544,10 @@ columns: collation: comment: - *20 - closure_reasons: - - *5 - - *6 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: label - cast_type: *7 - sql_type_metadata: *8 - 'null': true - default: - default_function: - collation: - comment: - - *9 closures: - *23 - *5 - *6 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: reason - cast_type: *7 - sql_type_metadata: *8 - 'null': false - default: - default_function: - collation: - comment: - *9 - *25 collection_publications: @@ -875,6 +861,40 @@ columns: default_function: collation: comment: + integrations: + - *5 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: data + cast_type: *15 + sql_type_metadata: *16 + 'null': true + default: + default_function: + collation: + comment: + - *6 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: owner_id + cast_type: *3 + sql_type_metadata: *4 + 'null': false + default: + default_function: + collation: + comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: type + cast_type: *7 + sql_type_metadata: *8 + 'null': true + default: + default_function: + collation: + comment: + - *9 mentions: - *5 - *6 @@ -1466,7 +1486,6 @@ primary_keys: card_not_nows: id cards: id closers_filters: - closure_reasons: id closures: id collection_publications: id collections: id @@ -1480,6 +1499,7 @@ primary_keys: events: id filters: id filters_tags: + integrations: id mentions: id notification_bundles: id notifications: id @@ -1520,7 +1540,6 @@ data_sources: card_not_nows: true cards: true closers_filters: true - closure_reasons: true closures: true collection_publications: true collections: true @@ -1534,6 +1553,7 @@ data_sources: events: true filters: true filters_tags: true + integrations: true mentions: true notification_bundles: true notifications: true @@ -2063,7 +2083,6 @@ indexes: nulls_not_distinct: comment: valid: true - closure_reasons: [] closures: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: closures @@ -2451,6 +2470,23 @@ indexes: nulls_not_distinct: comment: valid: true + integrations: + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: integrations + name: index_integrations_on_owner_id + unique: false + columns: + - owner_id + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true mentions: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: mentions @@ -3197,4 +3233,4 @@ indexes: nulls_not_distinct: comment: valid: true -version: 20251015123003 +version: 20251015205212 diff --git a/db/untenanted_schema.rb b/db/untenanted_schema.rb index d7f090873..1272ff070 100644 --- a/db/untenanted_schema.rb +++ b/db/untenanted_schema.rb @@ -10,12 +10,23 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2025_09_24_190729) do +ActiveRecord::Schema[8.1].define(version: 2025_10_07_112917) do create_table "identities", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false end + create_table "magic_links", force: :cascade do |t| + t.string "code" + t.datetime "created_at", null: false + t.datetime "expires_at" + t.integer "membership_id", null: false + t.datetime "updated_at", null: false + t.index ["code"], name: "index_magic_links_on_code", unique: true + t.index ["expires_at"], name: "index_magic_links_on_expires_at" + t.index ["membership_id"], name: "index_magic_links_on_membership_id" + end + create_table "memberships", force: :cascade do |t| t.string "account_name", null: false t.datetime "created_at", null: false @@ -29,5 +40,6 @@ ActiveRecord::Schema[8.1].define(version: 2025_09_24_190729) do t.index ["user_tenant", "user_id"], name: "index_memberships_on_user_tenant_and_user_id" end + add_foreign_key "magic_links", "memberships" add_foreign_key "memberships", "identities" end diff --git a/test/controllers/cards/closures_controller_test.rb b/test/controllers/cards/closures_controller_test.rb index 1e8f85871..73c1181e4 100644 --- a/test/controllers/cards/closures_controller_test.rb +++ b/test/controllers/cards/closures_controller_test.rb @@ -9,11 +9,9 @@ class Cards::ClosuresControllerTest < ActionDispatch::IntegrationTest card = cards(:logo) assert_changes -> { card.reload.closed? }, from: false, to: true do - post card_closure_path(card, reason: "Scope too big") + post card_closure_path(card) assert_card_container_rerendered(card) end - - assert_equal "Scope too big", card.closure.reason end test "destroy" do diff --git a/test/fixtures/closures.yml b/test/fixtures/closures.yml index 8ddfbbe4c..cf9593601 100644 --- a/test/fixtures/closures.yml +++ b/test/fixtures/closures.yml @@ -1,4 +1,3 @@ shipping: card: shipping - user: kevin - reason: Closed + user: kevin \ No newline at end of file diff --git a/test/models/card/eventable/system_commenter_test.rb b/test/models/card/eventable/system_commenter_test.rb index 6f125f869..2008c031c 100644 --- a/test/models/card/eventable/system_commenter_test.rb +++ b/test/models/card/eventable/system_commenter_test.rb @@ -22,7 +22,7 @@ class Card::Eventable::SystemCommenterTest < ActiveSupport::TestCase end test "card_closed" do - assert_system_comment "Closed as ‘Done’ by David" do + assert_system_comment "Marked as “Done” by David" do @card.close end end From 85e69460dc8aad18fbe10cf5ddbd0ee7d8fe4cb5 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 15 Oct 2025 17:35:24 -0500 Subject: [PATCH 05/12] Move style out of .considering scope so it can apply in the filter view --- app/assets/stylesheets/card-columns.css | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/card-columns.css b/app/assets/stylesheets/card-columns.css index a471e80e1..a7c4139c1 100644 --- a/app/assets/stylesheets/card-columns.css +++ b/app/assets/stylesheets/card-columns.css @@ -411,6 +411,14 @@ } } + .card--drafted { + --card-color: var(--color-ink-medium) !important; + + border-color: var(--card-color) !important; + border-style: dashed !important; + box-shadow: none !important; + } + .card:has(.card__background img:not([src=""])) { .card__content, .card__meta, @@ -565,14 +573,6 @@ padding-block: 0 var(--card-padding-block); } - .card--drafted { - --card-color: var(--color-ink-medium) !important; - - border-color: var(--card-color) !important; - border-style: dashed !important; - box-shadow: none !important; - } - .card__people-label { display: none; } From e2c9df7c4398a32b8d4a26fb7028fa55318b26bf Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 15 Oct 2025 17:35:42 -0500 Subject: [PATCH 06/12] Include drafts in filter collection --- app/models/filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/filter.rb b/app/models/filter.rb index e3ff45510..6218dbbaa 100644 --- a/app/models/filter.rb +++ b/app/models/filter.rb @@ -18,7 +18,7 @@ class Filter < ApplicationRecord def cards @cards ||= begin - result = creator.accessible_cards.published + result = creator.accessible_cards.published_or_drafted_by(creator) result = result.indexed_by(indexed_by) result = result.sorted_by(sorted_by) result = result.where(id: card_ids) if card_ids.present? From 6460c40f29f26dfe0ab23cb1c2014b456670abac Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 15 Oct 2025 20:41:09 -0500 Subject: [PATCH 07/12] Update test --- test/models/filter_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/models/filter_test.rb b/test/models/filter_test.rb index 56a627883..a47e4ccba 100644 --- a/test/models/filter_test.rb +++ b/test/models/filter_test.rb @@ -34,7 +34,7 @@ class FilterTest < ActiveSupport::TestCase filter = users(:david).filters.new card_ids: [ cards(:logo, :layout).collect(&:id) ] cards(:logo).drafted! - assert_equal [ cards(:layout) ], filter.cards + assert_equal [ cards(:logo), cards(:layout) ], filter.cards end test "can't see cards in collections that aren't accessible" do From 739c53cffff0f85c5e9fbc3e4cb968dd676a48e5 Mon Sep 17 00:00:00 2001 From: Jason Fried Date: Thu, 16 Oct 2025 10:41:01 -0700 Subject: [PATCH 08/12] Updated global and local auto-close language. --- .../settings/_entropy_configuration.html.erb | 3 +- .../collections/edit/_auto_close.html.erb | 2 +- db/schema.rb | 6 +- db/schema_cache.yml | 82 ++++++------------- 4 files changed, 27 insertions(+), 66 deletions(-) diff --git a/app/views/account/settings/_entropy_configuration.html.erb b/app/views/account/settings/_entropy_configuration.html.erb index 910e10c5f..88dd539b2 100644 --- a/app/views/account/settings/_entropy_configuration.html.erb +++ b/app/views/account/settings/_entropy_configuration.html.erb @@ -1,6 +1,5 @@

Auto close

-

This is the default setting for this account, you can override it in each collections.

-

Cards automatically close as “Not now” if there is no activity for…

+

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 in each collection.

<%= render "entropy/auto_close", model: account.default_entropy_configuration, url: account_entropy_configuration_path %>
diff --git a/app/views/collections/edit/_auto_close.html.erb b/app/views/collections/edit/_auto_close.html.erb index 7f4e8295e..a8ba6cd59 100644 --- a/app/views/collections/edit/_auto_close.html.erb +++ b/app/views/collections/edit/_auto_close.html.erb @@ -1,7 +1,7 @@ <%= turbo_frame_tag @collection, :entropy_configuration do %>

Auto close

-

Cards automatically close as “Not now” if there is no activity for…

+

Fizzy doesn’t let stale cards stick around forever. Cards automatically close as “Not now” if no one updates, comments, or moves a card for…

<%= render "entropy/auto_close", model: collection, url: collection_entropy_configuration_path(collection) %>
<% end %> diff --git a/db/schema.rb b/db/schema.rb index 2ae9fd73e..3b33e336a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2025_10_16_153034) do +ActiveRecord::Schema[8.2].define(version: 2025_10_16_153034) do create_table "accesses", force: :cascade do |t| t.datetime "accessed_at" t.integer "collection_id", null: false @@ -404,10 +404,6 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_16_153034) do t.index ["user_id"], name: "index_sessions_on_user_id" end -# Could not dump table "sqlite_stat1" because of following StandardError -# Unknown type '' for column 'idx' - - create_table "steps", force: :cascade do |t| t.integer "card_id", null: false t.boolean "completed", default: false, null: false diff --git a/db/schema_cache.yml b/db/schema_cache.yml index b4e6b25f2..20cdb931d 100644 --- a/db/schema_cache.yml +++ b/db/schema_cache.yml @@ -474,12 +474,12 @@ columns: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: due_on - cast_type: &40 !ruby/object:ActiveRecord::Type::Date + cast_type: &38 !ruby/object:ActiveRecord::Type::Date precision: scale: limit: timezone: - sql_type_metadata: &41 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata + sql_type_metadata: &39 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata sql_type: date type: :date limit: @@ -511,7 +511,7 @@ columns: default_function: collation: comment: - - &39 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &37 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: title cast_type: *7 @@ -583,11 +583,11 @@ columns: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: all_access - cast_type: &35 !ruby/object:ActiveModel::Type::Boolean + cast_type: &33 !ruby/object:ActiveModel::Type::Boolean precision: scale: limit: - sql_type_metadata: &36 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata + sql_type_metadata: &34 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata sql_type: boolean type: :boolean limit: @@ -865,7 +865,7 @@ columns: - *9 filters_tags: - *20 - - &38 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &36 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: tag_id cast_type: *3 @@ -982,7 +982,7 @@ columns: - *9 - *18 period_highlights: - - &37 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &35 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: content cast_type: *15 @@ -1107,11 +1107,11 @@ columns: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: rowid - cast_type: &33 !ruby/object:ActiveModel::Type::Value + cast_type: !ruby/object:ActiveModel::Type::Value precision: scale: limit: - sql_type_metadata: &34 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata + sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata sql_type: '' type: limit: @@ -1185,50 +1185,19 @@ columns: - *9 - *32 - *18 - sqlite_stat1: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: idx - cast_type: *33 - sql_type_metadata: *34 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: stat - cast_type: *33 - sql_type_metadata: *34 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: tbl - cast_type: *33 - sql_type_metadata: *34 - 'null': true - default: - default_function: - collation: - comment: steps: - *23 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: completed - cast_type: *35 - sql_type_metadata: *36 + cast_type: *33 + sql_type_metadata: *34 'null': false default: false default_function: collation: comment: - - *37 + - *35 - *5 - *6 - *9 @@ -1236,12 +1205,12 @@ columns: - *23 - *5 - *6 - - *38 + - *36 - *9 tags: - *5 - *6 - - *39 + - *37 - *9 user_settings: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column @@ -1284,8 +1253,8 @@ columns: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: starts_at - cast_type: *40 - sql_type_metadata: *41 + cast_type: *38 + sql_type_metadata: *39 'null': false default: default_function: @@ -1294,11 +1263,11 @@ columns: - *9 - *18 users: - - &43 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &41 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: active - cast_type: *35 - sql_type_metadata: *36 + cast_type: *33 + sql_type_metadata: *34 'null': false default: true default_function: @@ -1347,8 +1316,8 @@ columns: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: watching - cast_type: *35 - sql_type_metadata: *36 + cast_type: *33 + sql_type_metadata: *34 'null': false default: true default_function: @@ -1378,7 +1347,7 @@ columns: comment: - *6 - *9 - - &42 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &40 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: webhook_id cast_type: *3 @@ -1432,9 +1401,9 @@ columns: collation: comment: - *9 - - *42 + - *40 webhooks: - - *43 + - *41 - *24 - *5 - *6 @@ -1523,7 +1492,6 @@ primary_keys: search_queries: id search_results: id sessions: id - sqlite_stat1: steps: id taggings: id tags: id @@ -1578,7 +1546,6 @@ data_sources: search_queries: true search_results: true sessions: true - sqlite_stat1: true steps: true taggings: true tags: true @@ -2930,7 +2897,6 @@ indexes: nulls_not_distinct: comment: valid: true - sqlite_stat1: [] steps: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: steps From f224656d1eac5caae965119e96011d18212b7861 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Thu, 16 Oct 2025 13:29:41 -0500 Subject: [PATCH 09/12] Prefer helper --- .../controllers/collapsible_columns_controller.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/javascript/controllers/collapsible_columns_controller.js b/app/javascript/controllers/collapsible_columns_controller.js index 735b090ba..6ca8ce015 100644 --- a/app/javascript/controllers/collapsible_columns_controller.js +++ b/app/javascript/controllers/collapsible_columns_controller.js @@ -1,13 +1,16 @@ import { Controller } from "@hotwired/stimulus" +import { nextFrame } from "helpers/timing_helpers"; export default class extends Controller { static classes = [ "collapsed", "noTransitions" ] static targets = [ "column", "button" ] - connect() { + async connect() { this.#disableTransitions() this.#restoreColumns() - requestAnimationFrame(() => this.#enableTransitions()) + + await nextFrame() + this.#enableTransitions() } toggle({ target }) { From e70209f02c3ab712972e531bbf1baed7d7f34aa1 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Thu, 16 Oct 2025 13:44:37 -0500 Subject: [PATCH 10/12] We can just apply it to `this.element` and adjust the CSS accordingly --- app/assets/stylesheets/card-columns.css | 2 +- .../controllers/collapsible_columns_controller.js | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/card-columns.css b/app/assets/stylesheets/card-columns.css index 2ef0d65d2..77749faa1 100644 --- a/app/assets/stylesheets/card-columns.css +++ b/app/assets/stylesheets/card-columns.css @@ -73,7 +73,7 @@ transition: var(--column-transition); /* Disable transitions during restoration */ - &.no-transitions { + .no-transitions & { transition: none; } diff --git a/app/javascript/controllers/collapsible_columns_controller.js b/app/javascript/controllers/collapsible_columns_controller.js index 6ca8ce015..fc64ba414 100644 --- a/app/javascript/controllers/collapsible_columns_controller.js +++ b/app/javascript/controllers/collapsible_columns_controller.js @@ -25,19 +25,11 @@ export default class extends Controller { } #disableTransitions() { - this.columnTargets.forEach(column => { - if (this.noTransitionsClass) { - column.classList.add(this.noTransitionsClass) - } - }) + this.element.classList.add(this.noTransitionsClass) } #enableTransitions() { - this.columnTargets.forEach(column => { - if (this.noTransitionsClass) { - column.classList.remove(this.noTransitionsClass) - } - }) + this.element.classList.remove(this.noTransitionsClass) } #toggleColumn(column) { From eae59342e903e61fa026407df6ff28bc0532d87b Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Thu, 16 Oct 2025 22:05:22 +0200 Subject: [PATCH 11/12] Add account creation script --- script/create-account.rb | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 script/create-account.rb diff --git a/script/create-account.rb b/script/create-account.rb new file mode 100755 index 000000000..d0cebc83a --- /dev/null +++ b/script/create-account.rb @@ -0,0 +1,106 @@ +#!/usr/bin/env ruby +# Usage: script/create-account "Company Name" "Owner Name" "owner@example.com" + +require_relative "../config/environment" + +# Parse arguments +if ARGV.size != 3 + puts "Usage: script/create-account " + puts "Example: script/create-account 'Acme Corp' 'John Doe' 'john@acme.com'" + exit 1 +end + +company_name, owner_name, owner_email = ARGV + +# Create a minimal Current context for the signup +Current.set( + ip_address: "127.0.0.1", + user_agent: "create-account script", + referrer: nil +) do + puts "Creating account..." + puts " Company: #{company_name}" + puts " Owner: #{owner_name}" + puts " Email: #{owner_email}" + puts + + # Step 1: Create the account in QueenBee + queenbee_account_attributes = { + skip_remote: true, + product_name: "fizzy", + name: company_name, + owner_name: owner_name, + owner_email: owner_email, + trial: true, + subscription: { + name: "FreeV1", + price: 0 + }, + remote_request: { + remote_address: Current.ip_address, + user_agent: Current.user_agent, + referrer: Current.referrer + } + } + + begin + queenbee_account = Queenbee::Remote::Account.create!(queenbee_account_attributes) + puts "✓ Account created in QueenBee" + rescue => error + puts "Error creating QueenBee account:" + puts " - #{error.message}" + exit 1 + end + + # Step 2: Create tenant with the QueenBee account ID + tenant_id = queenbee_account.id.to_s + + begin + ApplicationRecord.create_tenant(tenant_id) do + # Create account with admin user + account = Account.create_with_admin_user( + account: { + external_account_id: tenant_id, + name: company_name + }, + owner: { + name: owner_name, + email_address: owner_email + } + ) + + # Setup basic template + account.setup_basic_template + end + + puts "✓ Tenant created" + puts "✓ Account setup completed" + rescue => error + # Clean up QueenBee account if tenant creation fails + queenbee_account&.cancel + + puts "Error setting up tenant:" + puts " - #{error.message}" + exit 1 + end + + # Step 3: Get or create join code + ApplicationRecord.with_tenant(tenant_id) do + account = Account.sole + join_code = account.join_code + + puts "✓ Join code ready" + puts + puts "Account created successfully!" + puts "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + puts "Tenant: #{tenant_id}" + puts "Join Code: #{join_code}" + puts "Join URL: #{Rails.application.routes.url_helpers.join_url( + join_code: join_code, + script_name: tenant_id, + host: Rails.application.config.action_mailer.default_url_options[:host], + port: Rails.application.config.action_mailer.default_url_options[:port], + protocol: Rails.env.production? ? 'https' : 'http' + )}" + end +end From 622a687158bd7971d649bfb3e0a179ec97e4f630 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Thu, 16 Oct 2025 22:13:30 +0200 Subject: [PATCH 12/12] Set a password for the owner --- script/create-account.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/script/create-account.rb b/script/create-account.rb index d0cebc83a..cb7c74416 100755 --- a/script/create-account.rb +++ b/script/create-account.rb @@ -11,6 +11,7 @@ if ARGV.size != 3 end company_name, owner_name, owner_email = ARGV +password = SecureRandom.hex(16) # Create a minimal Current context for the signup Current.set( @@ -65,7 +66,8 @@ Current.set( }, owner: { name: owner_name, - email_address: owner_email + email_address: owner_email, + password: password } ) @@ -94,10 +96,13 @@ Current.set( puts "Account created successfully!" puts "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" puts "Tenant: #{tenant_id}" - puts "Join Code: #{join_code}" + puts "Company: #{company_name}" + puts "Owner: #{owner_name}" + puts "Email: #{owner_email}" + puts "Password: #{password}" puts "Join URL: #{Rails.application.routes.url_helpers.join_url( join_code: join_code, - script_name: tenant_id, + script_name: "/#{tenant_id}", host: Rails.application.config.action_mailer.default_url_options[:host], port: Rails.application.config.action_mailer.default_url_options[:port], protocol: Rails.env.production? ? 'https' : 'http'