Merge branch 'main' into disable-column-restore-transition
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
source "https://rubygems.org"
|
||||
git_source(:bc) { |repo| "https://github.com/basecamp/#{repo}" }
|
||||
|
||||
# gem "rails", github: "rails/rails", branch: "main"
|
||||
gem "rails", github: "flavorjones/rails", branch: "flavorjones/improve-sqlite-url-support"
|
||||
gem "rails", github: "rails/rails", branch: "main"
|
||||
|
||||
# Assets & front end
|
||||
gem "importmap-rails"
|
||||
|
||||
+57
-57
@@ -1,6 +1,6 @@
|
||||
GIT
|
||||
remote: https://github.com/basecamp/activerecord-tenanted
|
||||
revision: f412b25ff0096064a4763dc0c8d21b6593678137
|
||||
revision: 82eff28e0d2f857f14cf8d9c94eaa6c37cec58bb
|
||||
specs:
|
||||
activerecord-tenanted (0.5.0)
|
||||
activerecord (>= 8.1.beta)
|
||||
@@ -40,33 +40,33 @@ GIT
|
||||
zeitwerk (~> 2)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/flavorjones/rails.git
|
||||
revision: aa964b6012c614909d8524a497e3e79b956b3ade
|
||||
branch: flavorjones/improve-sqlite-url-support
|
||||
remote: https://github.com/rails/rails.git
|
||||
revision: 1ca947883d1975487e14208e8159726654fc20d1
|
||||
branch: main
|
||||
specs:
|
||||
actioncable (8.1.0.beta1)
|
||||
actionpack (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
actioncable (8.2.0.alpha)
|
||||
actionpack (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (8.1.0.beta1)
|
||||
actionpack (= 8.1.0.beta1)
|
||||
activejob (= 8.1.0.beta1)
|
||||
activerecord (= 8.1.0.beta1)
|
||||
activestorage (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
actionmailbox (8.2.0.alpha)
|
||||
actionpack (= 8.2.0.alpha)
|
||||
activejob (= 8.2.0.alpha)
|
||||
activerecord (= 8.2.0.alpha)
|
||||
activestorage (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
mail (>= 2.8.0)
|
||||
actionmailer (8.1.0.beta1)
|
||||
actionpack (= 8.1.0.beta1)
|
||||
actionview (= 8.1.0.beta1)
|
||||
activejob (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
actionmailer (8.2.0.alpha)
|
||||
actionpack (= 8.2.0.alpha)
|
||||
actionview (= 8.2.0.alpha)
|
||||
activejob (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
mail (>= 2.8.0)
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (8.1.0.beta1)
|
||||
actionview (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
actionpack (8.2.0.alpha)
|
||||
actionview (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
nokogiri (>= 1.8.5)
|
||||
rack (>= 2.2.4)
|
||||
rack-session (>= 1.0.1)
|
||||
@@ -74,36 +74,36 @@ GIT
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
useragent (~> 0.16)
|
||||
actiontext (8.1.0.beta1)
|
||||
actiontext (8.2.0.alpha)
|
||||
action_text-trix (~> 2.1.15)
|
||||
actionpack (= 8.1.0.beta1)
|
||||
activerecord (= 8.1.0.beta1)
|
||||
activestorage (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
actionpack (= 8.2.0.alpha)
|
||||
activerecord (= 8.2.0.alpha)
|
||||
activestorage (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
actionview (8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
activejob (8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
activejob (8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
activerecord (8.1.0.beta1)
|
||||
activemodel (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
activemodel (8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
activerecord (8.2.0.alpha)
|
||||
activemodel (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (8.1.0.beta1)
|
||||
actionpack (= 8.1.0.beta1)
|
||||
activejob (= 8.1.0.beta1)
|
||||
activerecord (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
activestorage (8.2.0.alpha)
|
||||
actionpack (= 8.2.0.alpha)
|
||||
activejob (= 8.2.0.alpha)
|
||||
activerecord (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
marcel (~> 1.0)
|
||||
activesupport (8.1.0.beta1)
|
||||
activesupport (8.2.0.alpha)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||
@@ -116,23 +116,23 @@ GIT
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0, >= 2.0.5)
|
||||
uri (>= 0.13.1)
|
||||
rails (8.1.0.beta1)
|
||||
actioncable (= 8.1.0.beta1)
|
||||
actionmailbox (= 8.1.0.beta1)
|
||||
actionmailer (= 8.1.0.beta1)
|
||||
actionpack (= 8.1.0.beta1)
|
||||
actiontext (= 8.1.0.beta1)
|
||||
actionview (= 8.1.0.beta1)
|
||||
activejob (= 8.1.0.beta1)
|
||||
activemodel (= 8.1.0.beta1)
|
||||
activerecord (= 8.1.0.beta1)
|
||||
activestorage (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
rails (8.2.0.alpha)
|
||||
actioncable (= 8.2.0.alpha)
|
||||
actionmailbox (= 8.2.0.alpha)
|
||||
actionmailer (= 8.2.0.alpha)
|
||||
actionpack (= 8.2.0.alpha)
|
||||
actiontext (= 8.2.0.alpha)
|
||||
actionview (= 8.2.0.alpha)
|
||||
activejob (= 8.2.0.alpha)
|
||||
activemodel (= 8.2.0.alpha)
|
||||
activerecord (= 8.2.0.alpha)
|
||||
activestorage (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 8.1.0.beta1)
|
||||
railties (8.1.0.beta1)
|
||||
actionpack (= 8.1.0.beta1)
|
||||
activesupport (= 8.1.0.beta1)
|
||||
railties (= 8.2.0.alpha)
|
||||
railties (8.2.0.alpha)
|
||||
actionpack (= 8.2.0.alpha)
|
||||
activesupport (= 8.2.0.alpha)
|
||||
irb (~> 1.13)
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m.6 22.4c.6.6 1.5.6 2.1 0l5.3-5.3 3 3c.5.4 1.1.7 1.8.7h.3c.7-.1 1.4-.5 1.8-1.2.9-1.7 1.3-3.6 1-5.5l5.9-3.7c1.2-.7 1.5-2.2.8-3.4-.1-.2-.2-.3-.3-.4l-5.8-5.8c-.4-.5-1-.7-1.7-.7s-1.3.2-1.8.7c-.1 0-.2.2-.3.4l-3.7 5.9c-1.9-.3-3.8 0-5.5 1-1.2.7-1.6 2.2-.9 3.4.1.2.2.4.4.5l3 3-5.4 5.4c-.6.6-.6 1.5 0 2zm3.8-11.8c-.1 0-.1-.1-.1-.1v-.4c0-.1.1-.2.2-.3 1.4-.8 3.1-1.1 4.7-.7l.7.2 4.5-7h.1c.1-.2.2-.2.3-.2h.3l5.7 5.8c.2.2.1.5 0 .6l-7 4.5.2.7c.4 1.6.1 3.3-.7 4.7 0 .1-.3.2-.4.2 0 .1-.3 0-.4-.1z"/></svg>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m23.5 19.5-6.2-6.2 4.8-3.1c.7-.4.9-1.3.5-2 0 0-.2-.3-.2-.3l-5.8-5.8c-.6-.6-1.5-.6-2.1 0s-.2.3-.2.3l-3.1 4.9-6.8-7c-.4-.3-1-.3-1.4 0-.4.4-.4 1 0 1.4l7 7s0 0 0 0l5.8 5.8s0 0 0 0l6.4 6.4c.4.4 1 .4 1.4 0s.4-1 0-1.4z"/><path d="m4.8 9.6c-.7.4-.9 1.3-.5 2 0 0 .2.3.2.3l3 3s-.1 0-.2.1l-5.9 5.9c-.6.6-.6 1.5 0 2.1s1.5.6 2.1 0l5.9-5.9s0 0 0-.1l3.1 3.1c.3.3.7.4 1.2.4s.9-.3 1.1-.7c.6-1 .9-2 1-3.1l-7.9-8.1c-1 0-2.1.4-3 1z"/></svg>
|
||||
|
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 489 B |
@@ -3,14 +3,15 @@
|
||||
--row-gap: 0.2lh;
|
||||
|
||||
background-color: var(--color-terminal-bg);
|
||||
block-size: var(--footer-height);
|
||||
block-size: calc(var(--footer-height) + env(safe-area-inset-bottom));
|
||||
color: var(--color-terminal-text);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 0.9em;
|
||||
inset: auto 0 0 0;
|
||||
max-block-size: 100%;
|
||||
padding: var(--block-space) calc(var(--tray-size) + calc(var(--inline-space) * 3)) calc(var(--block-space) + env(safe-area-inset-bottom));
|
||||
padding-block: var(--block-space) calc(var(--block-space) + env(safe-area-inset-bottom));
|
||||
padding-inline: calc(var(--tray-size) + calc(var(--inline-space) * 3));
|
||||
place-content: center;
|
||||
position: fixed;
|
||||
z-index: var(--z-terminal);
|
||||
|
||||
@@ -234,6 +234,7 @@
|
||||
transition: opacity 300ms ease-in-out;
|
||||
opacity: 0;
|
||||
|
||||
&:focus-visible,
|
||||
.cards:hover & {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -273,15 +274,17 @@
|
||||
font-weight: inherit;
|
||||
font-size: inherit;
|
||||
line-height: var(--column-width-collapsed);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
.cards.is-collapsed & {
|
||||
max-inline-size: 50vh;
|
||||
writing-mode: vertical-rl;
|
||||
}
|
||||
|
||||
.cards:not(.is-collapsed, .cards--considering) & {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-inline-size: calc(100% - var(--column-width-collapsed) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,6 +598,10 @@
|
||||
border: 1px solid var(--border-color);
|
||||
text-align: center;
|
||||
|
||||
&:has(dialog[open]) {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.separator--horizontal {
|
||||
margin-block: var(--block-space);
|
||||
}
|
||||
@@ -621,32 +628,28 @@
|
||||
}
|
||||
|
||||
.collection-tools__watching {
|
||||
--btn-size: 32px;
|
||||
--gap: 0.5ch;
|
||||
|
||||
display: flex;
|
||||
gap: 0.5ch;
|
||||
gap: var(--gap);
|
||||
inline-size: 100%;
|
||||
margin-block: var(--block-space-half);
|
||||
overflow: hidden;
|
||||
place-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
aspect-ratio: 0.5;
|
||||
background: linear-gradient(to right, var(--color-canvas), transparent);
|
||||
content: "";
|
||||
inset-block: 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
.collection-tools__watching-dialog {
|
||||
--panel-padding: 2ch;
|
||||
--panel-size: 100%;
|
||||
|
||||
&:before {
|
||||
inset-inline-start: 0;
|
||||
}
|
||||
|
||||
&:after {
|
||||
inset-inline-end: 0;
|
||||
rotate: 180deg;
|
||||
}
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--gap);
|
||||
inset-block-start: 0;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* On Deck (Not Now)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
@layer components {
|
||||
.pagination-link {
|
||||
display: block;
|
||||
flex: 1;
|
||||
min-block-size: 1.5rem;
|
||||
pointer-events: none;
|
||||
text-decoration: none;
|
||||
|
||||
&.pagination-link--active-when-observed {
|
||||
block-size: 0;
|
||||
min-block-size: 0;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
height: 0px;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
&[aria-busy="true"] {
|
||||
.spinner {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ class Columns::Cards::Drops::StreamsController < ApplicationController
|
||||
|
||||
def create
|
||||
@card.send_back_to_triage
|
||||
set_page_and_extract_portion_from @collection.cards.awaiting_triage.reverse_chronologically
|
||||
set_page_and_extract_portion_from @collection.cards.awaiting_triage.by_last_activity.with_golden_first
|
||||
|
||||
render turbo_stream: turbo_stream.replace("the-stream", partial: "collections/show/stream", method: :morph, locals: { collection: @card.collection, page: @page })
|
||||
end
|
||||
|
||||
@@ -3,9 +3,9 @@ class Prompts::CardsController < ApplicationController
|
||||
|
||||
def index
|
||||
@cards = if filter_param.present?
|
||||
prepending_exact_matches_by_id(published_cards.mentioning(params[:filter]))
|
||||
prepending_exact_matches_by_id(search_cards)
|
||||
else
|
||||
@cards = published_cards.latest
|
||||
published_cards.latest
|
||||
end
|
||||
|
||||
if stale? etag: @cards
|
||||
@@ -18,8 +18,15 @@ class Prompts::CardsController < ApplicationController
|
||||
params[:filter]
|
||||
end
|
||||
|
||||
def search_cards
|
||||
published_cards
|
||||
.mentioning(params[:filter])
|
||||
.reverse_chronologically
|
||||
.limit(MAX_RESULTS)
|
||||
end
|
||||
|
||||
def published_cards
|
||||
Current.user.accessible_cards.published.limit(MAX_RESULTS)
|
||||
Current.user.accessible_cards.published
|
||||
end
|
||||
|
||||
def prepending_exact_matches_by_id(cards)
|
||||
|
||||
@@ -35,7 +35,12 @@ module AccessesHelper
|
||||
tag.div(class: "collection-tools__watching") do
|
||||
safe_join([
|
||||
safe_join(displayed_watchers.map { |watcher| avatar_tag(watcher) }),
|
||||
(tag.span("+#{overflow_count}", class: "overflow-count") if overflow_count > 0)
|
||||
(tag.div(data: { controller: "dialog", action: "keydown.esc->dialog#close click@document->dialog#closeOnClickOutside" }) do
|
||||
concat tag.button("+#{overflow_count}", class: "overflow-count btn btn--circle borderless", data: { action: "dialog#open" }, aria: { label: "Show #{overflow_count} more watchers" })
|
||||
concat(tag.dialog(class: "collection-tools__watching-dialog dialog panel", data: { dialog_target: "dialog" }, aria: { hidden: "true" }) do
|
||||
safe_join(watchers.map { |watcher| avatar_tag(watcher) })
|
||||
end)
|
||||
end if overflow_count > 0)
|
||||
].compact)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,7 +14,7 @@ module ColumnsHelper
|
||||
"drag-and-drop-refresh": true,
|
||||
controller: "frame",
|
||||
action: "turbo:before-frame-render->frame#morphRender turbo:before-morph-element->frame#morphReload"
|
||||
|
||||
options[:refresh] = :morph if src.present?
|
||||
turbo_frame_tag(id, src: src, data: data, **options, &block)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,13 +5,15 @@ module PaginationHelper
|
||||
|
||||
def link_to_next_page(namespace, page, activate_when_observed: false, label: default_pagination_label(activate_when_observed), data: {}, **attributes)
|
||||
if page.before_last? && !params[:previous]
|
||||
pagination_link(namespace, page.number + 1, label: label, activate_when_observed: activate_when_observed, data: data, class: "btn txt-small center-block center", **attributes)
|
||||
attributes[:class] = class_names(attributes[:class], "btn txt-small center-block center": !activate_when_observed)
|
||||
pagination_link(namespace, page.number + 1, label: label, activate_when_observed: activate_when_observed, data: data, **attributes)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_link(namespace, page_number, activate_when_observed: false, label: default_pagination_label(activate_when_observed), url_params: {}, data: {}, **attributes)
|
||||
link_to label, url_for(params.permit!.to_h.merge(page: page_number, **url_params)),
|
||||
"aria-label": "Load page #{page_number}",
|
||||
id: "#{namespace}-pagination-link-#{page_number}",
|
||||
class: class_names(attributes.delete(:class), "pagination-link", { "pagination-link--active-when-observed" => activate_when_observed }),
|
||||
data: {
|
||||
frame: pagination_frame_id_for(namespace, page_number),
|
||||
@@ -62,9 +64,11 @@ module PaginationHelper
|
||||
private
|
||||
def pagination_list(name, tag_element: :div, paginate_on_scroll: false, **properties, &block)
|
||||
classes = properties.delete(:class)
|
||||
properties[:id] ||= "#{name}-pagination-list"
|
||||
tag.public_send tag_element,
|
||||
class: token_list(name, "display-contents", classes),
|
||||
data: { controller: "pagination", pagination_paginate_on_intersection_value: paginate_on_scroll },
|
||||
**properties,
|
||||
&block
|
||||
end
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ import { nextFrame } from "helpers/timing_helpers";
|
||||
export default class extends Controller {
|
||||
static classes = [ "collapsed", "noTransitions" ]
|
||||
static targets = [ "column", "button" ]
|
||||
static values = {
|
||||
collection: String
|
||||
}
|
||||
|
||||
async connect() {
|
||||
this.#disableTransitions()
|
||||
@@ -84,6 +87,6 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
#localStorageKeyFor(column) {
|
||||
return `expand-${column.getAttribute("id")}`
|
||||
return `expand-${this.collectionValue}-${column.getAttribute("id")}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,4 +43,11 @@ export default class extends Controller {
|
||||
closeOnClickOutside({ target }) {
|
||||
if (!this.element.contains(target)) this.close()
|
||||
}
|
||||
|
||||
preventCloseOnMorphing(event) {
|
||||
if (event.detail?.attributeName === "open") {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Controller } from "@hotwired/stimulus"
|
||||
import { debounce, nextFrame } from "helpers/timing_helpers";
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = [ "cancel", "submit" ]
|
||||
static targets = [ "cancel", "submit", "input" ]
|
||||
|
||||
static values = {
|
||||
debounceTimeout: { type: Number, default: 300 }
|
||||
@@ -16,6 +16,17 @@ export default class extends Controller {
|
||||
this.element.requestSubmit()
|
||||
}
|
||||
|
||||
preventEmptySubmit(event) {
|
||||
const input = this.hasInputTarget ? this.inputTarget : null
|
||||
|
||||
if (input) {
|
||||
const value = (input.value || "").trim()
|
||||
if (value.length === 0) {
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debouncedSubmit(event) {
|
||||
this.submit(event)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,6 @@ export default class extends Controller {
|
||||
#replacePaginationLinkWithFrame(linkElement) {
|
||||
const turboFrame = this.#buildTurboFrameFor(linkElement)
|
||||
this.#insertTurboFrameAtPosition(linkElement, turboFrame)
|
||||
linkElement.remove()
|
||||
}
|
||||
|
||||
#buildTurboFrameFor(linkElement) {
|
||||
|
||||
@@ -36,11 +36,13 @@ class Notification::Bundle < ApplicationRecord
|
||||
end
|
||||
|
||||
def deliver
|
||||
processing!
|
||||
user.in_time_zone do
|
||||
processing!
|
||||
|
||||
Notification::BundleMailer.notification(self).deliver if deliverable?
|
||||
Notification::BundleMailer.notification(self).deliver if deliverable?
|
||||
|
||||
delivered!
|
||||
delivered!
|
||||
end
|
||||
end
|
||||
|
||||
def deliver_later
|
||||
|
||||
@@ -23,6 +23,7 @@ class Search
|
||||
"null as comment_body",
|
||||
"collections.name as collection_name",
|
||||
"cards.creator_id",
|
||||
"cards.created_at",
|
||||
"bm25(cards_search_index, 10.0, 2.0) AS score"
|
||||
].join(","))
|
||||
|
||||
@@ -35,10 +36,11 @@ class Search
|
||||
"snippet(comments_search_index, 0, '#{HIGHLIGHT_OPENING_MARK}', '#{HIGHLIGHT_CLOSING_MARK}', '...', 20) AS comment_body",
|
||||
"collections.name as collection_name",
|
||||
"comments.creator_id",
|
||||
"comments.created_at",
|
||||
"bm25(comments_search_index, 1.0) AS score"
|
||||
].join(","))
|
||||
|
||||
union_sql = "(#{cards.to_sql} UNION #{comments.to_sql}) as search_results"
|
||||
Search::Result.from(union_sql).order(score: :desc)
|
||||
Search::Result.from(union_sql).order(created_at: :desc)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,18 +50,37 @@ class User::DayTimeline
|
||||
end
|
||||
|
||||
private
|
||||
TIMELINEABLE_ACTIONS = %w[
|
||||
card_assigned
|
||||
card_unassigned
|
||||
card_published
|
||||
card_closed
|
||||
card_reopened
|
||||
card_due_date_added
|
||||
card_due_date_changed
|
||||
card_due_date_removed
|
||||
card_collection_changed
|
||||
comment_created
|
||||
]
|
||||
|
||||
def first_day_with_activity_this_week?
|
||||
day.monday? || (earliest_time.present? && earliest_time < day.beginning_of_week(:monday))
|
||||
end
|
||||
|
||||
def filtered_events
|
||||
@filtered_events ||= begin
|
||||
events = Event.where(collection: collections)
|
||||
events = timelineable_events
|
||||
events = events.where(creator_id: filter.creators.ids) if filter.creators.present?
|
||||
events
|
||||
end
|
||||
end
|
||||
|
||||
def timelineable_events
|
||||
Event
|
||||
.where(collection: collections)
|
||||
.where(action: TIMELINEABLE_ACTIONS)
|
||||
end
|
||||
|
||||
def collections
|
||||
filter.collections.presence || user.collections
|
||||
end
|
||||
|
||||
@@ -15,7 +15,6 @@ class Webhook < ApplicationRecord
|
||||
card_due_date_removed
|
||||
card_published
|
||||
card_reopened
|
||||
card_title_changed
|
||||
card_unassigned
|
||||
card_unstaged
|
||||
comment_created
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="settings__panel settings__panel--entropy panel shadow center">
|
||||
<h2 class="divider txt-large">Sink or Swim</h2>
|
||||
<p class="margin-none-block-start">Choose default settings for this account. You can override them in individual collections.</p>
|
||||
<h2 class="divider txt-large">Auto close</h2>
|
||||
<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>
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
<div class="flex flex-column flex-item-grow max-inline-size">
|
||||
<header class="card__header">
|
||||
<div class="card__collection flex align-start">
|
||||
<span class="card__id"><%= card.id %></span>
|
||||
<span class="card__id">
|
||||
<span class="for-screen-reader">Card number</span>
|
||||
<%= card.id %>
|
||||
</span>
|
||||
<span class="card__collection-name">
|
||||
<span class="overflow-ellipsis"><%= card.collection.name %></span>
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<label class="card__collection">
|
||||
<span class="card__id"><%= card.id %></span>
|
||||
<span class="card__id">
|
||||
<span class="for-screen-reader">Card number</span>
|
||||
<%= card.id %>
|
||||
</span>
|
||||
<span class="card__collection-name">
|
||||
<span class="overflow-ellipsis"><%= card.collection.name %></span>
|
||||
</span>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
<li class="step">
|
||||
<input type="checkbox" class="step__checkbox" disabled>
|
||||
<%= form_with model: [card, Step.new], url: card_steps_path(card) do |form| %>
|
||||
<%= form.text_field :content, class: "input step__content hide-focus-ring", placeholder: "Add a step…", required: true, autocomplete: "off" %>
|
||||
<%= form_with model: [card, Step.new], url: card_steps_path(card), data: { controller: "form", action: "submit->form#preventEmptySubmit" } do |form| %>
|
||||
<%= form.text_field :content, class: "input step__content hide-focus-ring", placeholder: "Add a step…", autocomplete: "off", data: { form_target: "input" }, aria: { label: "Add a step" } %>
|
||||
<% end %>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
@@ -7,18 +7,18 @@
|
||||
} do %>
|
||||
|
||||
<svg viewBox="0 0 200 100">
|
||||
<path id="top-half" fill="transparent" d="M 20,100 A 80,80 0 0,1 180,100"/>
|
||||
<path id="<%= dom_id(card, "bubble-top-half") %>" fill="transparent" d="M 20,100 A 80,80 0 0,1 180,100"/>
|
||||
<text text-anchor="middle" fill="currentColor">
|
||||
<textPath href="#top-half" startOffset="50%" dominant-baseline="middle" data-bubble-target="top"></textPath>
|
||||
<textPath href="#<%= dom_id(card, "bubble-top-half") %>" startOffset="50%" dominant-baseline="middle" data-bubble-target="top"></textPath>
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
<span class="bubble__number" data-bubble-target="center"></span>
|
||||
|
||||
<svg viewBox="0 0 200 100">
|
||||
<path id="bottom-half" d="M 20,0 A 80,80 0 0,0 180,0" fill="transparent"/>
|
||||
<path id="<%= dom_id(card, "bubble-bottom-half") %>" d="M 20,0 A 80,80 0 0,0 180,0" fill="transparent"/>
|
||||
<text text-anchor="middle" fill="currentColor">
|
||||
<textPath href="#bottom-half" startOffset="50%" dominant-baseline="middle" data-bubble-target="bottom"></textPath>
|
||||
<textPath href="#<%= dom_id(card, "bubble-bottom-half") %>" startOffset="50%" dominant-baseline="middle" data-bubble-target="bottom"></textPath>
|
||||
</text>
|
||||
</svg>
|
||||
<% end %>
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
</span>
|
||||
|
||||
<span class="card__meta-text card__meta-text--updated overflow-ellipsis">
|
||||
<%= icon_tag "refresh--meta" %>
|
||||
<%= icon_tag "refresh--meta", aria: { label: "Last updated" } %>
|
||||
<%= local_datetime_tag(card.last_active_at, style: :daysago) %>
|
||||
</span>
|
||||
|
||||
<span class="card__meta-text card__meta-text--assignees overflow-ellipsis">
|
||||
<%= icon_tag "arrow-right" if card.assignees.any? %>
|
||||
<%= icon_tag("arrow-right", aria: { label: "Assigned to" }) if card.assignees.any? %>
|
||||
<%= card.assignees.map { |assignee| h assignee.familiar_name }.to_sentence(two_words_connector: " / ", last_word_connector: " / ").html_safe %>
|
||||
</span>
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<%= turbo_stream.replace(dom_id(@column), partial: "collections/show/column", method: :morph, locals: { column: @column }) %>
|
||||
<%= turbo_stream.replace(dom_id(@column), partial: "collections/show/column", method: :morph, locals: { column: @column }) %>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%= turbo_frame_tag @collection, :entropy_configuration do %>
|
||||
<div>
|
||||
<h2 class="divider txt-large">Sink or Swim</h2>
|
||||
<p class="margin-none-block-start">Cards automatically move to "Not now" after…</p>
|
||||
<div class="margin-block-end">
|
||||
<h2 class="divider txt-large">Auto close</h2>
|
||||
<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-collapsed-class="is-collapsed" data-collapsible-columns-no-transitions-class="no-transitions">
|
||||
<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>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<header class="cards__expander">
|
||||
<h2 class="cards__expander-title position-relative">The Stream</h2>
|
||||
<%= link_to collection_columns_stream_path(collection), class: "btn btn--circle txt-x-small borderless cards__expander-button", data: { turbo_frame: "_top" } do %>
|
||||
<%= link_to collection_columns_stream_path(collection), class: "cards__expander-button btn btn--circle txt-x-small borderless", data: { turbo_frame: "_top" } do %>
|
||||
<%= icon_tag "expand", class: "translucent" %>
|
||||
<span class="for-screen-reader">Expand column</span>
|
||||
<% end %>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="cards__expander-menu" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside" data-turbo-permanent>
|
||||
<div id="<%= dom_id(column, :menu) %>" class="cards__expander-menu" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
|
||||
<button class="btn btn--circle txt-x-small borderless" data-action="click->dialog#open">
|
||||
<%= icon_tag "menu-dots-horizontal", class: "translucent" %>
|
||||
<span class="for-screen-reader">Column options</span>
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<div data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
|
||||
<button id="<%= dom_id(column, :manage_menu) %>" class="popup__btn btn" data-action="click->dialog#open:stop" data-clicker-target="clickable" hidden aria-hidden>Save changes</button>
|
||||
<dialog class="popup panel flex-column gap-half fill-white shadow txt-small margin-block-double" data-dialog-target="dialog">
|
||||
<dialog class="popup panel flex-column gap-half fill-white shadow txt-small margin-block-double" data-dialog-target="dialog" data-action="turbo:before-morph-attribute->dialog#preventCloseOnMorphing">
|
||||
<%= render "collections/show/menu/column_form", collection: column.collection, column: column, label: "Save changes" %>
|
||||
</dialog>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<%= form_with model: [collection, column], data: { controller: "form", action: "turbo:submit-end->dialog#close turbo:submit-end->form#reset" } do |form| %>
|
||||
<%= form.text_field :name, class: "input", placeholder: "Name this column", value: column.name,
|
||||
required: true, autocomplete: "off", pattern: ".*\\S.*", title: "Column name cannot be blank", data: { action: "focus->form#select" } %>
|
||||
|
||||
<div class="color-picker__colors">
|
||||
<% ([Card::DEFAULT_COLOR] + Card::COLORS).each do |color| %>
|
||||
<label class="btn txt-small borderless" style="--btn-background: <%= color %>" title="<%= color %>">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= link_to column_path, class: "btn btn--circle txt-x-small borderless cards__expander-button", data: { turbo_frame: "_top" } do %>
|
||||
<%= link_to column_path, class: "cards__expander-button btn btn--circle txt-x-small borderless", data: { turbo_frame: "_top" } do %>
|
||||
<%= icon_tag "expand", class: "translucent" %>
|
||||
<span class="for-screen-reader">Expand column</span>
|
||||
<% end %>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<%= link_to event.notifiable_target,
|
||||
id: dom_id(event, "timelined"),
|
||||
class: "event event--#{ event.action } #{ "golden-effect" if event.card.golden? } center-block flex flex-column full-width align-start justify-start position-relative",
|
||||
style: "--card-color: #{ card.closed? ? "var(--color-card-complete)" : card.color };",
|
||||
target: "_top",
|
||||
@@ -8,7 +9,10 @@
|
||||
action: "mouseover->related-element#highlight mouseout->related-element#unhighlight" } do %>
|
||||
<div class="card__header flex align-start gap full-width">
|
||||
<h4 class="card__collection flex-inline align-start">
|
||||
<span class="card__id"><%= card.id %></span>
|
||||
<span class="card__id">
|
||||
<span class="for-screen-reader">Card number</span>
|
||||
<%= card.id %>
|
||||
</span>
|
||||
<span class="card__collection-name">
|
||||
<span class="overflow-ellipsis"><%= event.collection.name %></span>
|
||||
</span>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
filter_show: user_filtering.show_collections?,
|
||||
multi_selection_combobox_no_selection_label_value: user_filtering.selected_collections_label } do %>
|
||||
<button type="button" class="btn borderless btn--plain events__filter-select" data-action="click->dialog#toggle:stop">
|
||||
<span class="overflow-ellipsis" data-multi-selection-combobox-target="label">
|
||||
<span data-multi-selection-combobox-target="label">
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<strong class="popup__title">Collection…</strong>
|
||||
|
||||
<% if user_filtering.collections.many? %>
|
||||
<%= text_field_tag nil, nil, placeholder: "Filter…", class: "input input--transparent txt-small font-weight-normal", autofocus: true,
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small font-weight-normal", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<%= tag.aside \
|
||||
class: class_names("filters flex align-center gap-half justify-center center margin-block-end", { "filters--expanded": user_filtering.expanded? }),
|
||||
data: {
|
||||
controller: "toggle-class filter-settings",
|
||||
toggle_class_toggle_class: "filters--expanded",
|
||||
filter_settings_filters_set_class: "filters--has-filters-set",
|
||||
filter_settings_no_filtering_url_value: no_filtering_url,
|
||||
filter_settings_refresh_url_value: settings_refresh_path,
|
||||
filter_settings_cards_url_value: cards_path,
|
||||
turbo_permanent: true } do %>
|
||||
controller: "toggle-class filter-settings",
|
||||
toggle_class_toggle_class: "filters--expanded",
|
||||
filter_settings_filters_set_class: "filters--has-filters-set",
|
||||
filter_settings_no_filtering_url_value: no_filtering_url,
|
||||
filter_settings_refresh_url_value: settings_refresh_path,
|
||||
filter_settings_cards_url_value: cards_path,
|
||||
turbo_permanent: true } do %>
|
||||
<%= form_with url: filter_url, method: :get, class: "", data: {
|
||||
controller: "form",
|
||||
turbo_frame: "cards_container",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<%= collapsible_nav_section "Accounts" do %>
|
||||
<% Current.user.identity.memberships.where.not(user_tenant: Current.user.tenant).each do |membership| %>
|
||||
<%= filter_place_menu_item root_url(script_name: "/#{membership.user_tenant}"), "#{membership.account_name}", "logo-color", new_window: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -3,5 +3,5 @@
|
||||
action: "click->dialog#open:stop keydown.j@document->hotkey#click keydown.meta+j@document->hotkey#click keydown.ctrl+j@document->hotkey#click",
|
||||
controller: "hotkey" } do %>
|
||||
<%= inline_svg "fizzy-logo" %>
|
||||
<strong class="txt-medium overflow-ellipsis margin-none"><%= Account.sole.name %></strong>
|
||||
<strong class="txt-medium overflow-ellipsis margin-none"><%= Current.user.identity.memberships.many? ? Account.sole.name : "Fizzy" %></strong>
|
||||
<% end %>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<% # Using method: :get inside Turbo frame triggers some special Turbo handling where the script_name is lost %>
|
||||
<% if user_filtering.collections.any? %>
|
||||
<%= collapsible_nav_section "Collections" do %>
|
||||
<%= render "filters/menu/collections/all_option", user_filtering: user_filtering %>
|
||||
<%= render partial: "filters/menu/collections/collection", collection: user_filtering.collections, as: :collection %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -3,7 +3,4 @@
|
||||
<%= filter_place_menu_item user_path(Current.user), "My Profile", "person" %>
|
||||
<%= filter_place_menu_item notifications_path, "Notifications", "bell" %>
|
||||
<%= filter_place_menu_item notifications_settings_path, "Notification Settings", "settings" %>
|
||||
<% Current.user.identity.memberships.where.not(user_tenant: Current.user.tenant).each do |membership| %>
|
||||
<%= filter_place_menu_item root_url(script_name: "/#{membership.user_tenant}"), "#{membership.account_name}", "logo-color", new_window: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<% if Current.user.collections.many? %>
|
||||
<li class="popup__item" data-filter-target="item" data-navigable-list-target="item" id="filter-collection-all">
|
||||
<%= icon_tag "collection", class: "popup__icon" %>
|
||||
<%= link_to cards_path, class: "popup__btn btn" do %>
|
||||
<span class="overflow-ellipsis">All collections</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
@@ -19,7 +19,7 @@
|
||||
<strong class="popup__title">Assigned to…</strong>
|
||||
|
||||
<% if User.active.many? %>
|
||||
<%= text_field_tag nil, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<strong class="popup__title">Closed by…</strong>
|
||||
|
||||
<% if user_filtering.users.many? %>
|
||||
<%= text_field_tag nil, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<strong class="popup__title">Collection…</strong>
|
||||
|
||||
<% if user_filtering.collections.many? %>
|
||||
<%= text_field_tag nil, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<strong class="popup__title">Added by…</strong>
|
||||
|
||||
<% if user_filtering.users.many? %>
|
||||
<%= text_field_tag nil, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<strong class="popup__title">Tagged…</strong>
|
||||
|
||||
<% if user_filtering.tags.many? %>
|
||||
<%= text_field_tag nil, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<%= render "filters/menu/tags", user_filtering: @user_filtering %>
|
||||
<%= render "filters/menu/users", user_filtering: @user_filtering %>
|
||||
<%= render "filters/menu/places", user_filtering: @user_filtering %>
|
||||
<%= render "filters/menu/accounts", user_filtering: @user_filtering %>
|
||||
|
||||
<%= render "filters/menu/shortcut_notice" %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<div class="card__collection">
|
||||
<span class="card__id"><%= notification.card.id %></span>
|
||||
<span class="card__id">
|
||||
<span class="for-screen-reader">Card number</span>
|
||||
<%= notification.card.id %>
|
||||
</span>
|
||||
<span class="card__collection-name">
|
||||
<span class="overflow-ellipsis"><%= notification.card.collection.name %></span>
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<div class="card__collection">
|
||||
<span class="card__id"><%= notification.card.id %></span>
|
||||
<span class="card__id">
|
||||
<span class="for-screen-reader">Card number</span>
|
||||
<%= notification.card.id %>
|
||||
</span>
|
||||
<span class="card__collection-name">
|
||||
<span class="overflow-ellipsis"><%= notification.card.collection.name %></span>
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%= turbo_frame_tag :cards_container do %>
|
||||
<div class="card-columns" data-controller="collapsible-columns" 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">
|
||||
|
||||
<div class="card-columns__left">
|
||||
<%= render "public/collections/show/not_now", collection: collection %>
|
||||
@@ -12,4 +12,4 @@
|
||||
<%= render "public/collections/show/closed", collection: collection %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# Pin npm packages by running ./bin/importmap
|
||||
|
||||
pin "application"
|
||||
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
||||
pin "@hotwired/turbo-rails", to: "turbo.patched.js"
|
||||
pin "@hotwired/stimulus", to: "stimulus.min.js"
|
||||
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
||||
pin "@rails/request.js", to: "@rails--request.js" # @0.0.11
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ default: &default
|
||||
batch_size: 500
|
||||
workers:
|
||||
- queues: [ "default", "solid_queue_recurring", "backend", "webhooks" ]
|
||||
threads: 1
|
||||
threads: 3
|
||||
processes: <%= ENV.fetch("JOB_CONCURRENCY", Concurrent.physical_processor_count) %>
|
||||
polling_interval: 0.1
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ production: &production
|
||||
# Application cleanup
|
||||
auto_postpone_all_due:
|
||||
class: Card::AutoPostponeAllDueJob
|
||||
schedule: every hour
|
||||
schedule: every hour at minute 50
|
||||
remove_abandoned_creations:
|
||||
class: RemoveAbandonedCreationsJob
|
||||
schedule: every hour
|
||||
schedule: every hour at minute 59
|
||||
delete_unused_tags:
|
||||
class: DeleteUnusedTagsJob
|
||||
schedule: every day at 04:00
|
||||
schedule: every day at 04:02
|
||||
|
||||
# Operations cleanup and backups
|
||||
clear_solid_queue_finished_jobs:
|
||||
@@ -27,10 +27,10 @@ production: &production
|
||||
schedule: every hour at minute 52
|
||||
cleanup_webhook_deliveries:
|
||||
class: Webhook::CleanupDeliveriesJob
|
||||
schedule: every 4 hours
|
||||
schedule: every 4 hours at minute 51
|
||||
sqlite_backups:
|
||||
class: SQLiteBackupsJob
|
||||
schedule: every day at 05:00
|
||||
schedule: every day at 05:02
|
||||
|
||||
# Metrics
|
||||
yabeda_actioncable:
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class AddIndexToEventsOnCollectionIdAndActionAndCreatedAt < ActiveRecord::Migration[8.1]
|
||||
def change
|
||||
add_index :events, [ :collection_id, :action, :created_at ], name: "index_events_on_collection_id_and_action_and_created_at"
|
||||
end
|
||||
end
|
||||
Generated
+2
-1
@@ -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.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
|
||||
@@ -272,6 +272,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_15_123003) do
|
||||
t.json "particulars", default: {}
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["action"], name: "index_events_on_summary_id_and_action"
|
||||
t.index ["collection_id", "action", "created_at"], name: "index_events_on_collection_id_and_action_and_created_at"
|
||||
t.index ["collection_id"], name: "index_events_on_collection_id"
|
||||
t.index ["creator_id"], name: "index_events_on_creator_id"
|
||||
t.index ["eventable_type", "eventable_id"], name: "index_events_on_eventable"
|
||||
|
||||
+19
-1
@@ -2351,6 +2351,24 @@ indexes:
|
||||
nulls_not_distinct:
|
||||
comment:
|
||||
valid: true
|
||||
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
|
||||
table: events
|
||||
name: index_events_on_collection_id_and_action_and_created_at
|
||||
unique: false
|
||||
columns:
|
||||
- collection_id
|
||||
- action
|
||||
- created_at
|
||||
lengths: {}
|
||||
orders: {}
|
||||
opclasses: {}
|
||||
where:
|
||||
type:
|
||||
using:
|
||||
include:
|
||||
nulls_not_distinct:
|
||||
comment:
|
||||
valid: true
|
||||
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
|
||||
table: events
|
||||
name: index_events_on_creator_id
|
||||
@@ -3197,4 +3215,4 @@ indexes:
|
||||
nulls_not_distinct:
|
||||
comment:
|
||||
valid: true
|
||||
version: 20251015123003
|
||||
version: 20251016153034
|
||||
|
||||
@@ -114,4 +114,20 @@ class Notification::BundleTest < ActiveSupport::TestCase
|
||||
bundle.reload
|
||||
assert bundle.pending?
|
||||
end
|
||||
|
||||
test "deliver sends email with time in user's time zone" do
|
||||
@user.settings.update!(timezone_name: "Madrid")
|
||||
|
||||
freeze_time Time.utc(2025, 1, 15, 14, 30, 0) do
|
||||
@user.notifications.create!(source: events(:logo_published), creator: @user)
|
||||
bundle = @user.notification_bundles.pending.last
|
||||
bundle.deliver
|
||||
|
||||
email = ActionMailer::Base.deliveries.last
|
||||
assert_not_nil email
|
||||
|
||||
# Time in Madrid should be 15:30 (UTC+1 in winter)
|
||||
assert_match /everything since 3pm/i, email.text_part&.body&.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Vendored
+6050
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user