Merge branch 'main' into column-transition-performance
* main: Set a password for the owner Add account creation script We can just apply it to `this.element` and adjust the CSS accordingly Prefer helper Updated global and local auto-close language. Update test Include drafts in filter collection Move style out of .considering scope so it can apply in the filter view Remove closure reasons They're all just *done* Just close as done Don't animate expanding columns when restoring
This commit is contained in:
@@ -68,6 +68,10 @@
|
||||
transition: inline-size 200ms ease-out;
|
||||
will-change: inline-size;
|
||||
|
||||
.no-transitions & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
&:not(.is-collapsed) {
|
||||
padding-inline: var(--column-padding-expanded);
|
||||
}
|
||||
@@ -408,6 +412,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,
|
||||
@@ -558,14 +570,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;
|
||||
}
|
||||
|
||||
@@ -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%);
|
||||
} */
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ module EventsHelper
|
||||
when "card_published"
|
||||
"#{h event_creator_name(event) } added <span style='color: var(--card-color)'>#{h title }</span>".html_safe
|
||||
when "card_closed"
|
||||
"#{h event_creator_name(event) } closed <span style='color: var(--card-color)'>#{h title }</span>".html_safe
|
||||
"#{h event_creator_name(event) } marked <span style='color: var(--card-color)'>#{h title }</span> as done".html_safe
|
||||
when "card_reopened"
|
||||
"#{h event_creator_name(event) } reopened <span style='color: var(--card-color)'>#{h title }</span>".html_safe
|
||||
when "card_due_date_added"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
import { nextFrame } from "helpers/timing_helpers";
|
||||
|
||||
export default class extends Controller {
|
||||
static classes = [ "collapsed" ]
|
||||
static classes = [ "collapsed", "noTransitions" ]
|
||||
static targets = [ "column", "button" ]
|
||||
static values = {
|
||||
collection: String
|
||||
}
|
||||
|
||||
connect() {
|
||||
async connect() {
|
||||
this.#disableTransitions()
|
||||
this.#restoreColumns()
|
||||
|
||||
await nextFrame()
|
||||
this.#enableTransitions()
|
||||
}
|
||||
|
||||
toggle({ target }) {
|
||||
@@ -22,6 +27,14 @@ export default class extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
#disableTransitions() {
|
||||
this.element.classList.add(this.noTransitionsClass)
|
||||
}
|
||||
|
||||
#enableTransitions() {
|
||||
this.element.classList.remove(this.noTransitionsClass)
|
||||
}
|
||||
|
||||
#toggleColumn(column) {
|
||||
this.#collapseAllExcept(column)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,7 +19,7 @@ class Card::Eventable::SystemCommenter
|
||||
when "card_unassigned"
|
||||
"#{event.creator.name} <strong>unassigned</strong> from #{event.assignees.pluck(:name).to_sentence}."
|
||||
when "card_closed"
|
||||
"<strong>Closed</strong> as ‘#{ card.closure.reason }’ by #{ event.creator.name }"
|
||||
"<strong>Marked</strong> as “Done” by #{ event.creator.name }"
|
||||
when "card_reopened"
|
||||
"<strong>Reopened</strong> by #{ event.creator.name }"
|
||||
when "card_title_changed"
|
||||
|
||||
@@ -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
|
||||
@@ -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?
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<div class="settings__panel settings__panel--entropy panel shadow center">
|
||||
<h2 class="divider txt-large">Auto close</h2>
|
||||
<p class="margin-none-block-start">This is the default setting for this account, you can override it in each collections.</p>
|
||||
<p class="margin-none-block-start">Cards automatically close as “Not now” if there is no activity for…</p>
|
||||
<p class="margin-none-block-start">Fizzy doesn’t 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 in each collection.</em></p>
|
||||
<%= render "entropy/auto_close", model: account.default_entropy_configuration, url: account_entropy_configuration_path %>
|
||||
</div>
|
||||
|
||||
@@ -7,19 +7,9 @@
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<details class="expander" data-controller="details" data-details-target="details" data-action="keydown.esc->details#close:stop click@document->details#closeOnClickOutside">
|
||||
<summary class="expander__button btn borderless">
|
||||
Close as
|
||||
<%= icon_tag "caret-down" %>
|
||||
</summary>
|
||||
<div class="expander__content btn borderless">
|
||||
<% Closure::Reason.labels.each do |label| %>
|
||||
<%= button_to card_closure_path(card, reason: label), class: "expander__item btn" do %>
|
||||
<span class="overflow-ellipsis"><%= label %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</details>
|
||||
<%= button_to card_closure_path(card), class: "btn borderless" do %>
|
||||
<span class="overflow-ellipsis">Mark as Done</span>
|
||||
<% end %>
|
||||
<% if card.entropic? && card.open? %>
|
||||
<div class="card-perma__closure-message">
|
||||
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 %>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<% if card.closed? %>
|
||||
<div class="card__closed">
|
||||
<span class="card__closed-title" data-text="<%= card.closure.reason %>"><%= card.closure.reason %></span>
|
||||
<span class="card__closed-title" data-text="Done">Done</span>
|
||||
<strong class="card__closed-date"><%= card.closed_at.strftime("%b %d, %Y") %></strong>
|
||||
<span class="card__closed-by-line">by <span class="card__closed-by"><%= card.closed_by.familiar_name %></span></span>
|
||||
</div>
|
||||
|
||||
@@ -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? },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%= turbo_frame_tag @collection, :entropy_configuration do %>
|
||||
<div class="margin-block-end">
|
||||
<h2 class="divider txt-large">Auto close</h2>
|
||||
<p class="margin-none-block-start">Cards automatically close as “Not now” if there is no activity for…</p>
|
||||
<p class="margin-none-block-start">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…</p>
|
||||
<%= render "entropy/auto_close", model: collection, url: collection_entropy_configuration_path(collection) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
dragenter->drag-and-strum#dragEnter
|
||||
drop->drag-and-drop#drop
|
||||
dragend->drag-and-drop#dragEnd" } do %>
|
||||
<div class="card-columns" data-controller="collapsible-columns" data-collapsible-columns-collection-value="<%= collection.id %>" data-collapsible-columns-collapsed-class="is-collapsed">
|
||||
<div class="card-columns" data-controller="collapsible-columns" data-collapsible-columns-collection-value="<%= collection.id %>" data-collapsible-columns-collapsed-class="is-collapsed" data-collapsible-columns-no-transitions-class="no-transitions">
|
||||
<div class="card-columns__left">
|
||||
<%= render "collections/show/not_now", collection: collection %>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class RemoveClosureReasons < ActiveRecord::Migration[8.1]
|
||||
def change
|
||||
drop_table :closure_reasons
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class RemoveReasonFromClosures < ActiveRecord::Migration[8.1]
|
||||
def change
|
||||
remove_column :closures, :reason
|
||||
end
|
||||
end
|
||||
Generated
+12
-12
@@ -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
|
||||
@@ -29,6 +29,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_16_153034) 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_16_153034) 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"
|
||||
@@ -294,6 +288,15 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_16_153034) 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
|
||||
@@ -404,10 +407,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
|
||||
@@ -527,6 +526,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_16_153034) 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"
|
||||
|
||||
+87
-85
@@ -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
|
||||
@@ -474,12 +484,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 +521,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
|
||||
@@ -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:
|
||||
@@ -583,11 +569,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 +851,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
|
||||
@@ -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
|
||||
@@ -982,7 +1002,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 +1127,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 +1205,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 +1225,12 @@ columns:
|
||||
- *23
|
||||
- *5
|
||||
- *6
|
||||
- *38
|
||||
- *36
|
||||
- *9
|
||||
tags:
|
||||
- *5
|
||||
- *6
|
||||
- *39
|
||||
- *37
|
||||
- *9
|
||||
user_settings:
|
||||
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
|
||||
@@ -1284,8 +1273,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 +1283,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 +1336,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 +1367,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 +1421,9 @@ columns:
|
||||
collation:
|
||||
comment:
|
||||
- *9
|
||||
- *42
|
||||
- *40
|
||||
webhooks:
|
||||
- *43
|
||||
- *41
|
||||
- *24
|
||||
- *5
|
||||
- *6
|
||||
@@ -1497,7 +1486,6 @@ primary_keys:
|
||||
card_not_nows: id
|
||||
cards: id
|
||||
closers_filters:
|
||||
closure_reasons: id
|
||||
closures: id
|
||||
collection_publications: id
|
||||
collections: id
|
||||
@@ -1511,6 +1499,7 @@ primary_keys:
|
||||
events: id
|
||||
filters: id
|
||||
filters_tags:
|
||||
integrations: id
|
||||
mentions: id
|
||||
notification_bundles: id
|
||||
notifications: id
|
||||
@@ -1523,7 +1512,6 @@ primary_keys:
|
||||
search_queries: id
|
||||
search_results: id
|
||||
sessions: id
|
||||
sqlite_stat1:
|
||||
steps: id
|
||||
taggings: id
|
||||
tags: id
|
||||
@@ -1552,7 +1540,6 @@ data_sources:
|
||||
card_not_nows: true
|
||||
cards: true
|
||||
closers_filters: true
|
||||
closure_reasons: true
|
||||
closures: true
|
||||
collection_publications: true
|
||||
collections: true
|
||||
@@ -1566,6 +1553,7 @@ data_sources:
|
||||
events: true
|
||||
filters: true
|
||||
filters_tags: true
|
||||
integrations: true
|
||||
mentions: true
|
||||
notification_bundles: true
|
||||
notifications: true
|
||||
@@ -1578,7 +1566,6 @@ data_sources:
|
||||
search_queries: true
|
||||
search_results: true
|
||||
sessions: true
|
||||
sqlite_stat1: true
|
||||
steps: true
|
||||
taggings: true
|
||||
tags: true
|
||||
@@ -2096,7 +2083,6 @@ indexes:
|
||||
nulls_not_distinct:
|
||||
comment:
|
||||
valid: true
|
||||
closure_reasons: []
|
||||
closures:
|
||||
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
|
||||
table: closures
|
||||
@@ -2502,6 +2488,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
|
||||
@@ -2930,7 +2933,6 @@ indexes:
|
||||
nulls_not_distinct:
|
||||
comment:
|
||||
valid: true
|
||||
sqlite_stat1: []
|
||||
steps:
|
||||
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
|
||||
table: steps
|
||||
|
||||
+13
-1
@@ -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
|
||||
|
||||
Executable
+111
@@ -0,0 +1,111 @@
|
||||
#!/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 <company_name> <owner_name> <owner_email>"
|
||||
puts "Example: script/create-account 'Acme Corp' 'John Doe' 'john@acme.com'"
|
||||
exit 1
|
||||
end
|
||||
|
||||
company_name, owner_name, owner_email = ARGV
|
||||
password = SecureRandom.hex(16)
|
||||
|
||||
# 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,
|
||||
password: password
|
||||
}
|
||||
)
|
||||
|
||||
# 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 "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}",
|
||||
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
|
||||
@@ -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
|
||||
|
||||
Vendored
+1
-2
@@ -1,4 +1,3 @@
|
||||
shipping:
|
||||
card: shipping
|
||||
user: kevin
|
||||
reason: Closed
|
||||
user: kevin
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user