Add chip filters for the new time window filters
rename close => closure to ease interpretation by the LLM
This commit is contained in:
@@ -49,7 +49,7 @@ class Command::Ai::Translator
|
||||
"tag_ids": string[],
|
||||
"creation": "today" | "yesterday" | "thisweek" | "thismonth" | "thisyear"
|
||||
| "lastweek" | "lastmonth" | "lastyear",
|
||||
"completion": "today" | "yesterday" | "thisweek" | "thismonth" | "thisyear"
|
||||
"closure": "today" | "yesterday" | "thisweek" | "thismonth" | "thisyear"
|
||||
| "lastweek" | "lastmonth" | "lastyear"
|
||||
},
|
||||
"commands": string[] /* OPTIONAL, each starts with "/" */
|
||||
@@ -90,7 +90,7 @@ class Command::Ai::Translator
|
||||
* tag_ids — array of tag names
|
||||
* creation — relative range when the card was **created** (values listed above). Use it only
|
||||
when the user asks for cards created in a specific timeframe.
|
||||
* completion — relative range when the card was **completed/closed** (values listed above). Use it
|
||||
* closure — relative range when the card was **completed/closed** (values listed above). Use it
|
||||
only when the user asks for cards completed/closed in a specific timeframe.
|
||||
|
||||
---------------------- EXPLICIT FILTERING RULES ----------------------
|
||||
@@ -112,7 +112,7 @@ class Command::Ai::Translator
|
||||
today, yesterday, thisweek, thismonth, thisyear,
|
||||
lastweek, lastmonth, lastyear** ) → indexed_by: "closed"
|
||||
|
||||
– Never add "completion" unless one of the eight
|
||||
– Never add "closure" unless one of the eight
|
||||
timeframe tokens is present in the user text.
|
||||
|
||||
* Never add the literal words "card" or "cards" to terms; treat them as
|
||||
@@ -136,7 +136,7 @@ class Command::Ai::Translator
|
||||
– Only use "latest" if the user mentions activity, updates, or changes
|
||||
– Otherwise, prefer "newest" for generic mentions of “recent”
|
||||
* "Completed/closed cards" (no date range) → indexed_by: "closed"
|
||||
– VERY IMPORTANT: Do **not** set "completion" filter unless the user explicitly supplies a timeframe
|
||||
– VERY IMPORTANT: Do **not** set "closure" filter unless the user explicitly supplies a timeframe
|
||||
(e.g., “completed this month”, “closed last week”).
|
||||
(If the timeframe is supplied with “closed” instead of “completed”, treat it the same way.)
|
||||
|
||||
@@ -184,11 +184,12 @@ class Command::Ai::Translator
|
||||
* Never duplicate the assignee in both commands and context.
|
||||
– If the request says “assign to X”, produce only /assign X, never assignee_ids
|
||||
* Never add properties tied to UI view ("card", "list", etc.).
|
||||
* To filter completed or closed cards, use "indexed_by: closed", don't set a "completion" filter unless the user is
|
||||
* To filter completed or closed cards, use "indexed_by: closed", don't set a "closure" filter unless the user is
|
||||
asking for cards completed in a certain window of time.
|
||||
* When you see a word with a # prefix, assume it refers to a tag (either a filter or a command argument, but don't search for it).
|
||||
* All filters, including terms, must live **inside** context.
|
||||
* Do not duplicate terms across properties.
|
||||
* Don't use "creation" and "closure" filters at the same time.
|
||||
* Avoid redundant terms.
|
||||
|
||||
---------------------------- OUTPUT CLEANLINESS ----------------------------
|
||||
@@ -238,7 +239,7 @@ class Command::Ai::Translator
|
||||
User: completed cards yesterday
|
||||
Output:
|
||||
{
|
||||
"context": { "indexed_by": "closed", "completion": "yesterday" }
|
||||
"context": { "indexed_by": "closed", "closure": "yesterday" }
|
||||
}
|
||||
|
||||
User: "cards tagged with #design" or "#design cards"
|
||||
@@ -269,7 +270,7 @@ class Command::Ai::Translator
|
||||
User: cards completed last week
|
||||
Output:
|
||||
{
|
||||
"context": { "completion": "lastweek", "indexed_by": "closed" }
|
||||
"context": { "closure": "lastweek", "indexed_by": "closed" }
|
||||
}
|
||||
|
||||
Fallback search example (when nothing matches):
|
||||
|
||||
@@ -20,7 +20,7 @@ class Filter < ApplicationRecord
|
||||
@cards ||= begin
|
||||
result = creator.accessible_cards.indexed_by(indexed_by)
|
||||
result = result.where(id: card_ids) if card_ids.present?
|
||||
result = result.open unless indexed_by.closed? || close_window || card_ids.present?
|
||||
result = result.open unless indexed_by.closed? || closure_window || card_ids.present?
|
||||
result = result.by_engagement_status(engagement_status) if engagement_status.present?
|
||||
result = result.unassigned if assignment_status.unassigned?
|
||||
result = result.assigned_to(assignees.ids) if assignees.present?
|
||||
@@ -29,7 +29,7 @@ class Filter < ApplicationRecord
|
||||
result = result.in_stage(stages.ids) if stages.present?
|
||||
result = result.tagged_with(tags.ids) if tags.present?
|
||||
result = result.where("cards.created_at": creation_window) if creation_window
|
||||
result = result.closed_at_window(close_window) if close_window
|
||||
result = result.closed_at_window(closure_window) if closure_window
|
||||
result = terms.reduce(result) do |result, term|
|
||||
result.mentioning(term)
|
||||
end
|
||||
|
||||
@@ -17,7 +17,7 @@ module Filter::Fields
|
||||
|
||||
included do
|
||||
store_accessor :fields, :assignment_status, :indexed_by, :terms,
|
||||
:engagement_status, :card_ids, :creation, :close
|
||||
:engagement_status, :card_ids, :creation, :closure
|
||||
|
||||
def assignment_status
|
||||
super.to_s.inquiry
|
||||
@@ -35,8 +35,8 @@ module Filter::Fields
|
||||
TimeWindowParser.parse(creation)
|
||||
end
|
||||
|
||||
def close_window
|
||||
TimeWindowParser.parse(close)
|
||||
def closure_window
|
||||
TimeWindowParser.parse(closure)
|
||||
end
|
||||
|
||||
def terms
|
||||
|
||||
@@ -41,7 +41,7 @@ module Filter::Params
|
||||
params[:indexed_by] = indexed_by
|
||||
params[:engagement_status] = engagement_status
|
||||
params[:creation] = creation
|
||||
params[:close] = close
|
||||
params[:closure] = closure
|
||||
params[:assignment_status] = assignment_status
|
||||
params[:terms] = terms
|
||||
params[:tag_ids] = tags.ids
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
class TimeWindowParser
|
||||
attr_reader :now
|
||||
|
||||
HUMAN_NAMES_BY_VALUE = {
|
||||
"today" => "Today",
|
||||
"yesterday" => "Yesterday",
|
||||
"thisweek" => "This week",
|
||||
"thismonth" => "This month",
|
||||
"thisyear" => "This year",
|
||||
"lastweek" => "Last week",
|
||||
"lastmonth" => "Last month",
|
||||
"lastyear" => "Last year"
|
||||
}
|
||||
|
||||
VALUES = HUMAN_NAMES_BY_VALUE.keys
|
||||
|
||||
class << self
|
||||
def parse(string)
|
||||
new.parse(string)
|
||||
end
|
||||
|
||||
def human_name_for(value)
|
||||
HUMAN_NAMES_BY_VALUE[value]
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(now: Time.current)
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
<%= render "filters/tags", filter: filter %>
|
||||
<%= render "filters/assignees", filter: filter %>
|
||||
<%= render "filters/creators", filter: filter %>
|
||||
<%= render "filters/time_window", filter: filter, name: :creation, label: "Created" %>
|
||||
<%= render "filters/time_window", filter: filter, name: :closure, label: "Closed" %>
|
||||
<%# FIXME LATER: this needs to be able to allow selection of unique stages across all active workflows %>
|
||||
<%#= render "filters/stages", filter: filter %>
|
||||
<%= render "filters/cards", filter: filter %>
|
||||
|
||||
|
||||
|
||||
<% filter.terms.each do |term| %>
|
||||
<%= filter_chip_tag %Q("#{term}"), filter.as_params_without(:terms, term) %>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<% if filter.public_send("#{name}_window").present? %>
|
||||
<div class="quick-filter position-relative" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
|
||||
<button class="btn input input--select flex-inline txt-x-small" data-action="click->dialog#open:stop">
|
||||
<span class="overflow-ellipsis">
|
||||
<%= "#{label} #{TimeWindowParser.human_name_for(filter.public_send(name))&.downcase}" %>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
|
||||
aria-label="Created…" aria-description="Created…"
|
||||
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
|
||||
<strong class="popup__title margin-block-start-half pad-inline-half">Created…</strong>
|
||||
<%= form_with url: cards_path, method: :get, class: "popup__list",
|
||||
data: { controller: "form" } do |form| %>
|
||||
<% filter.as_params.except(name).each do |key, value| %>
|
||||
<%= filter_hidden_field_tag key, value %>
|
||||
<% end %>
|
||||
|
||||
<% TimeWindowParser::VALUES.each do |value| %>
|
||||
<div class="btn popup__item">
|
||||
<%= form.radio_button name, value,
|
||||
checked: filter.public_send(name) == value,
|
||||
data: { action: "change->form#submit" } %>
|
||||
|
||||
<%= form.label name, TimeWindowParser.human_name_for(value), value: value, class: "overflow-ellipsis" %>
|
||||
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</dialog>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -122,7 +122,7 @@ class Command::Ai::TranslatorTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
test "filter by time ranges" do
|
||||
assert_command({context: {completion: "thisweek", indexed_by: "closed"}}, "cards completed this week")
|
||||
assert_command({context: {closure: "thisweek", indexed_by: "closed"}}, "cards completed this week")
|
||||
assert_command({context: {creation: "thisweek", tag_ids: ["design"], creator_ids: [ "jz" ]}}, "cards created this week by jz tagged as #design")
|
||||
end
|
||||
|
||||
|
||||
@@ -144,8 +144,8 @@ class FilterTest < ActiveSupport::TestCase
|
||||
assert_includes filter.cards, cards(:logo)
|
||||
end
|
||||
|
||||
test "close window" do
|
||||
filter = users(:david).filters.new close: "this week"
|
||||
test "closure window" do
|
||||
filter = users(:david).filters.new closure: "this week"
|
||||
|
||||
cards(:shipping).closure.update_columns created_at: 2.weeks.ago
|
||||
assert_not_includes filter.cards, cards(:shipping)
|
||||
|
||||
+1038
File diff suppressed because it is too large
Load Diff
+1047
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+4136
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1036
File diff suppressed because it is too large
Load Diff
+1035
File diff suppressed because it is too large
Load Diff
+1038
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user