card 1.93.5 → 1.93.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/db/migrate_core_cards/data/1.12_stylesheets/classic_cards.scss +1 -1
  4. data/db/migrate_core_cards/data/1.12_stylesheets/traditional.scss +2 -1
  5. data/lib/card/format/nest.rb +4 -1
  6. data/lib/card/format/nest/mode.rb +11 -3
  7. data/lib/card/query.rb +6 -2
  8. data/lib/card/query/sorting.rb +21 -12
  9. data/lib/card/query/sql_statement.rb +1 -2
  10. data/lib/card/view/options.rb +17 -12
  11. data/mod/Modfile +1 -0
  12. data/mod/core/layout/simple_modal.html +3 -0
  13. data/mod/core/set/all/fetch_helper.rb +42 -30
  14. data/mod/core/spec/format/html_format_spec.rb +10 -17
  15. data/mod/core/spec/set/all/fetch_helper_spec.rb +48 -0
  16. data/mod/machines/file/all_script_machine_output/file.js +132 -18
  17. data/mod/machines/lib/javascript/decko.js.coffee +11 -2
  18. data/mod/machines/lib/javascript/decko_filter.js.coffee +116 -9
  19. data/mod/machines/lib/stylesheets/style_cards.scss +24 -3
  20. data/mod/pointer/lib/javascript/script_pointer_config.js.coffee +16 -6
  21. data/mod/pointer/set/abstract/00_paging_params.rb +17 -4
  22. data/mod/pointer/set/abstract/02_pointer/filtered.rb +48 -0
  23. data/mod/pointer/set/self/input_options.rb +1 -0
  24. data/mod/pointer/spec/set/self/input_options_spec.rb +1 -1
  25. data/mod/pointer/template/abstract/02_pointer/filtered/filter_items.haml +34 -0
  26. data/mod/pointer/template/abstract/02_pointer/filtered/filtered_list_input.haml +30 -0
  27. data/mod/search/lib/card/filter_query.rb +81 -0
  28. data/mod/search/set/abstract/00_filter_helper.rb +51 -0
  29. data/mod/search/set/abstract/01_filter_form_helper.rb +77 -0
  30. data/mod/search/set/abstract/01_search_params.rb +3 -11
  31. data/mod/search/set/abstract/02_filter_formgroups.rb +134 -0
  32. data/mod/search/set/abstract/03_filter.rb +117 -0
  33. data/mod/search/set/abstract/04_right_filter_form.rb +23 -0
  34. data/mod/search/set/abstract/search.rb +44 -10
  35. data/mod/search/set/abstract/wql_search.rb +22 -18
  36. data/mod/search/spec/set/{all → abstract}/filter_spec.rb +6 -5
  37. data/mod/search/template/{all/filter → abstract/03_filter}/_filter_input.haml +0 -0
  38. data/mod/search/template/{all/filter → abstract/03_filter}/filter_form.haml +9 -7
  39. data/mod/search/template/abstract/search/checkbox_item.haml +7 -0
  40. data/mod/search/template/abstract/search/select_item.haml +14 -0
  41. data/mod/standard/set/all/rich_html/editing.rb +4 -4
  42. data/mod/standard/set/all/rich_html/form_elements.rb +11 -2
  43. data/mod/standard/set/all/rich_html/modal.rb +26 -19
  44. data/mod/standard/set/all/rich_html/new.rb +8 -2
  45. data/mod/standard/set/all/rich_html/wrapper.rb +22 -18
  46. data/mod/standard/set/type/cardtype.rb +2 -2
  47. data/mod/standard/spec/set/all/rich_html/editing_spec.rb +0 -1
  48. data/mod/utility/set/abstract/utility.rb +13 -0
  49. data/mod/utility/spec/set/abstract/utility_spec.rb +16 -0
  50. metadata +21 -7
  51. data/mod/search/set/all/filter.rb +0 -9
@@ -0,0 +1,134 @@
1
+ include_set Abstract::FilterFormHelper
2
+
3
+ # FIXME: the vast majority of these need to be moved back to wikirate
4
+ format :html do
5
+ view :filter_name_formgroup, cache: :never do
6
+ text_filter :name
7
+ end
8
+
9
+ view :filter_project_formgroup, cache: :never do
10
+ select_filter_type_based :project
11
+ end
12
+
13
+ view :filter_year_formgroup, cache: :never do
14
+ select_filter :year, "Year", "most recent"
15
+ end
16
+
17
+ view :filter_wikirate_topic_formgroup, cache: :never do
18
+ # select_filter_type_based :wikirate_topic
19
+ autocomplete_filter :wikirate_topic
20
+ end
21
+
22
+ view :filter_metric_formgroup, cache: :never do
23
+ # select_filter_type_based :metric
24
+ autocomplete_filter :metric
25
+ end
26
+
27
+ view :filter_wikirate_company_formgroup, cache: :never do
28
+ # select_filter_type_based :wikirate_company
29
+ autocomplete_filter :wikirate_company
30
+ end
31
+
32
+ view :filter_research_policy_formgroup, cache: :never do
33
+ multiselect_filter :research_policy, "Research Policy"
34
+ end
35
+
36
+ def research_policy_select
37
+ select_filter_type_based :research_policy
38
+ end
39
+
40
+ view :filter_metric_type_formgroup, cache: :never do
41
+ multiselect_filter :metric_type, "Metric Type"
42
+ end
43
+
44
+ def metric_type_select
45
+ select_filter :metric_type, "Metric Type"
46
+ end
47
+
48
+ view :filter_metric_value_formgroup, cache: :never do
49
+ select_filter :metric_value, "Value", "exists"
50
+ end
51
+
52
+ view :filter_designer_formgroup, cache: :never do
53
+ select_filter :designer, "Designer"
54
+ end
55
+
56
+ view :filter_importance_formgroup, cache: :never do
57
+ multiselect_filter :importance, "My Vote", %w[upvotes novotes]
58
+ end
59
+
60
+ view :filter_industry_formgroup, cache: :never do
61
+ select_filter :industry, "Industry"
62
+ end
63
+
64
+ def sort_options
65
+ {}
66
+ end
67
+
68
+ def default_year_option
69
+ { "Most Recent" => "latest" }
70
+ end
71
+
72
+ def year_options
73
+ type_options(:year, "desc").each_with_object(default_year_option) do |v, h|
74
+ h[v] = v
75
+ end
76
+ end
77
+
78
+ def metric_value_options
79
+ opts = {
80
+ "All" => "all",
81
+ "Researched" => "exists",
82
+ "Known" => "known",
83
+ "Unknown" => "unknown",
84
+ "Not Researched" => "none",
85
+ "Edited today" => "today",
86
+ "Edited this week" => "week",
87
+ "Edited this month" => "month",
88
+ "Outliers" => "outliers"
89
+ }
90
+ return opts unless filter_param(:range)
91
+ opts.each_with_object({}) do |(k, v), h|
92
+ h[add_range(k, v)] = v
93
+ end
94
+ end
95
+
96
+ def add_range key, _value
97
+ key # unless selected_value?(value)
98
+ # range = filter_param :range
99
+ # "#{range[:from]} <= #{key} < #{range[:to]}"
100
+ end
101
+
102
+ def selected_value? value
103
+ (filter_param(:metric_value) && value == filter_param(:metric_value)) ||
104
+ value == "exists"
105
+ end
106
+
107
+ def metric_type_options
108
+ %i[researched formula wiki_rating].map { |n| Card.quick_fetch(n).name }
109
+ end
110
+
111
+ def research_policy_options
112
+ type_options :research_policy
113
+ end
114
+
115
+ def importance_options
116
+ { "I voted FOR" => :upvotes,
117
+ "I voted AGAINST" => :downvotes,
118
+ "I did NOT vote" => :novotes }
119
+ end
120
+
121
+ def industry_options
122
+ card_name = CompanyFilterQuery::INDUSTRY_METRIC_NAME
123
+ Card[card_name].value_options
124
+ end
125
+
126
+ def designer_options
127
+ metrics = Card.search type_id: MetricID, return: :name
128
+ metrics.map do |m|
129
+ names = m.to_name.parts
130
+ # score metric?
131
+ names.length == 3 ? names[2] : names[0]
132
+ end.uniq!(&:downcase).sort_by!(&:downcase)
133
+ end
134
+ end
@@ -0,0 +1,117 @@
1
+ include_set Abstract::FilterFormgroups
2
+ include_set Abstract::Utility
3
+
4
+ def filter_class
5
+ Card::FilterQuery
6
+ end
7
+
8
+ def filter_and_sort_wql
9
+ sort? ? filter_wql.merge(sort_wql) : filter_wql
10
+ end
11
+
12
+ def filter_wql
13
+ return {} if filter_hash.empty?
14
+ filter_wql_from_params
15
+ end
16
+
17
+ # separate method is needed for tests
18
+ def filter_wql_from_params
19
+ filter_class.new(filter_keys_with_values, blocked_id_wql).to_wql
20
+ end
21
+
22
+ def sort_wql
23
+ return {} if !sort? || sort_param.blank?
24
+ sort_hash
25
+ end
26
+
27
+ def sort?
28
+ true
29
+ end
30
+
31
+ def current_sort
32
+ sort_param || default_sort_option
33
+ end
34
+
35
+ def blocked_id_wql
36
+ not_ids = filter_param :not_ids
37
+ not_ids.present? ? { id: ["not in", not_ids.split(",")] } : {}
38
+ end
39
+
40
+ def advanced_filter_keys
41
+ []
42
+ end
43
+
44
+ # all filter keys in the order they were selected
45
+ def all_filter_keys
46
+ @all_filter_keys ||= filter_keys_from_params | filter_keys | advanced_filter_keys
47
+ end
48
+
49
+ def filter_keys
50
+ [:name]
51
+ end
52
+
53
+ def filter_keys_from_params
54
+ filter_hash.keys.map(&:to_sym) - [:not_ids]
55
+ end
56
+
57
+ format :html do
58
+ delegate :filter_hash, to: :card
59
+
60
+ def filter_fields slot_selector: nil, sort_field: nil
61
+ form_args = { action: filter_action_path, class: "slotter" }
62
+ form_args["data-slot-selector"] = slot_selector if slot_selector
63
+ filter_form filter_form_data, sort_field, form_args
64
+ end
65
+
66
+ def filter_form_data
67
+ all_filter_keys.each_with_object({}) do |cat, h|
68
+ h[cat] = { label: filter_label(cat),
69
+ input_field: _render("filter_#{cat}_formgroup"),
70
+ active: show_filter_field?(cat) }
71
+ end
72
+ end
73
+
74
+ def show_filter_field? field
75
+ filter_hash.present? ? filter_hash[field] : card.default_filter_option[field]
76
+ end
77
+
78
+ def filter_label field
79
+ return "Keyword" if field.to_sym == :name
80
+ Card.fetch_name(field) { field.to_s.capitalize }
81
+ end
82
+
83
+ def filter_action_path
84
+ path
85
+ end
86
+
87
+ view :filter_form, cache: :never do
88
+ filter_fields slot_selector: "._filter-result-slot",
89
+ sort_field: _render(:sort_formgroup)
90
+ end
91
+
92
+ # @param data [Hash] the filter categories. The hash needs for every category
93
+ # a hash with a label and a input_field entry.
94
+ def filter_form data={}, sort_input_field=nil, form_args={}
95
+ haml :filter_form, categories: data,
96
+ sort_input_field: sort_input_field,
97
+ form_args: form_args
98
+ end
99
+
100
+ def sort_options
101
+ {
102
+ "Alphabetical": :name,
103
+ "Recently Updated": :updated_at
104
+ }
105
+ end
106
+
107
+ view :sort_formgroup, cache: :never do
108
+ select_tag "sort",
109
+ options_for_select(sort_options, card.current_sort),
110
+ class: "pointer-select _filter-sort",
111
+ "data-minimum-results-for-search": "Infinity"
112
+ end
113
+ end
114
+
115
+ def default_sort_option
116
+ wql_from_content[:sort]
117
+ end
@@ -0,0 +1,23 @@
1
+ # To be included in a field card to get a filter for the parent.
2
+ # The core view renders a filter for the left card.
3
+
4
+ include_set Set::Abstract::Filter
5
+
6
+ def virtual?
7
+ true
8
+ end
9
+
10
+ format :html do
11
+ def filter_action_path
12
+ path mark: card.name.left, view: filter_view
13
+ end
14
+
15
+ # FIXME: wikirate-specific reference in selector
16
+ view :core, cache: :never do
17
+ filter_fields slot_selector: ".RIGHT-all_metric_value.filter_result-view"
18
+ end
19
+
20
+ def filter_view
21
+ :filter_result
22
+ end
23
+ end
@@ -1,5 +1,6 @@
1
1
  include_set Abstract::SearchParams
2
2
  include_set Abstract::Paging
3
+ include_set Abstract::Filter
3
4
 
4
5
  def search _args={}
5
6
  raise Error, "search not overridden"
@@ -106,26 +107,59 @@ format :csv do
106
107
  end
107
108
 
108
109
  format :html do
109
- view :card_list do
110
+ def with_results
110
111
  return render_no_search_results if search_with_params.empty?
111
- search_result_list do
112
- search_with_params.map do |item_card|
113
- nest_item item_card, size: voo.size do |rendered, item_view|
114
- klass = "search-result-item item-#{item_view}"
115
- %(<div class="#{klass}">#{rendered}</div>)
116
- end
112
+ yield
113
+ end
114
+
115
+ view :card_list do
116
+ with_results do
117
+ search_result_list "search-result-list" do |item_card|
118
+ card_list_item item_card
117
119
  end
118
120
  end
119
121
  end
120
122
 
121
- def search_result_list
123
+ def card_list_item item_card
124
+ nest_item item_card, size: voo.size do |rendered, item_view|
125
+ %(<div class="search-result-item item-#{item_view}">#{rendered}</div>)
126
+ end
127
+ end
128
+
129
+ def search_result_list klass
122
130
  with_paging do
123
- wrap_with :div, class: "search-result-list" do
124
- yield
131
+ wrap_with :div, class: klass do
132
+ search_with_params.map do |item_card|
133
+ yield item_card
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ view :select_item, cache: :never do
140
+ wrap do
141
+ haml :select_item
142
+ end
143
+ end
144
+
145
+ def default_select_item_args _args
146
+ class_up "card-slot", "_filter-result-slot"
147
+ end
148
+
149
+ view :checkbox_list, cache: :never do
150
+ with_results do
151
+ search_result_list "_search-checkbox-list" do |item_card|
152
+ checkbox_item item_card
125
153
  end
126
154
  end
127
155
  end
128
156
 
157
+ def checkbox_item item_card
158
+ subformat(item_card).wrap do
159
+ haml :checkbox_item, unique_id: unique_id, item_card: item_card
160
+ end
161
+ end
162
+
129
163
  def closed_limit
130
164
  [search_params[:limit].to_i, Card.config.closed_search_limit].min
131
165
  end
@@ -8,34 +8,38 @@ def search args={}
8
8
  query.run
9
9
  end
10
10
 
11
- # override this to define search
12
- def wql_hash
13
- @wql_hash ||= begin
14
- query = content
15
- query = query.is_a?(Hash) ? query : parse_json_query(query)
16
- query.symbolize_keys
17
- end
18
- end
19
-
20
- def query_args args={}
21
- wql_hash.merge args
11
+ def fetch_query args={}
12
+ @query ||= {}
13
+ @query[args.to_s] ||= query(args.clone) # cache query
22
14
  end
23
15
 
24
16
  def query args={}
25
17
  Query.new standardized_query_args(args), name
26
18
  end
27
19
 
28
- def fetch_query args={}
29
- @query ||= {}
30
- @query[args.to_s] ||= query(args.clone) # cache query
31
- end
32
-
33
20
  def standardized_query_args args
34
21
  args = query_args(args).symbolize_keys
35
22
  args[:context] ||= name
36
23
  args
37
24
  end
38
25
 
26
+ # override this to define search
27
+ def wql_hash
28
+ @wql_hash ||= wql_from_content.merge filter_and_sort_wql
29
+ end
30
+
31
+ def wql_from_content
32
+ @wql_from_content ||= begin
33
+ query = content
34
+ query = query.is_a?(Hash) ? query : parse_json_query(query)
35
+ query.symbolize_keys
36
+ end
37
+ end
38
+
39
+ def query_args args={}
40
+ wql_hash.merge args
41
+ end
42
+
39
43
  def parse_json_query query
40
44
  empty_query_error! if query.empty?
41
45
  JSON.parse query
@@ -49,8 +53,8 @@ def empty_query_error!
49
53
  end
50
54
 
51
55
  format do
52
- def default_search_params
53
- @default_search_params ||= { limit: (card_content_limit || default_limit) }
56
+ def default_limit
57
+ card_content_limit || super
54
58
  end
55
59
 
56
60
  def card_content_limit
@@ -1,9 +1,10 @@
1
- describe Card::Set::All::Filter do
1
+ describe Card::Set::Abstract::Filter do
2
2
  subject do
3
- Card["A"].format.filter_form a: { input_field: "<input id='a'/>", label: "A" },
4
- b: { input_field: "<select id='b'/>", label: "B" },
5
- c: { input_field: "<select id='c'/>", label: "C",
6
- active: true }
3
+ search_card = Card.new type: "Search"
4
+ search_card.format.filter_form a: { input_field: "<input id='a'/>", label: "A" },
5
+ b: { input_field: "<select id='b'/>", label: "B" },
6
+ c: { input_field: "<select id='c'/>", label: "C",
7
+ active: true }
7
8
  end
8
9
 
9
10
  specify "#filter_form" do
@@ -8,9 +8,11 @@
8
8
  - unless category[:active]
9
9
  = haml_partial :filter_input, key: key, category: category
10
10
 
11
- %form._filter-form{form_args, "accept-charset" => "UTF-8",
12
- "data-remote" => "true",
13
- :method => "get"}
11
+ %form._filter-form{form_args, "accept-charset": "UTF-8",
12
+ "data-remote": "true",
13
+ method: "get"}
14
+ - not_ids = params.dig :filter, :not_ids
15
+ %input{ type: :hidden, name: "filter[not_ids]", class: "_not-ids", value: not_ids }
14
16
  .d-flex.flex-row
15
17
  - if sort_input_field
16
18
  %i.material-icons.text-muted.md-24.p-2 sort
@@ -25,10 +27,10 @@
25
27
  = haml_partial :filter_input, key: key, category: category
26
28
 
27
29
  .dropdown._add-filter-dropdown
28
- %button.btn.btn-secondary.dropdown-toggle{"aria-expanded" => "false",
29
- "aria-haspopup" => "true",
30
- "data-toggle" => "dropdown",
31
- :type => "button"}
30
+ %button.btn.btn-secondary.dropdown-toggle{"aria-expanded": "false",
31
+ "aria-haspopup": "true",
32
+ "data-toggle": "dropdown",
33
+ type: "button" }
32
34
  Add filter
33
35
  .dropdown-menu
34
36
  - categories.each do |key, category|