refine-rails 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +413 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/config/refine_rails_manifest.js +0 -0
  5. data/app/assets/javascripts/refine-stimulus.esm.js +2 -0
  6. data/app/assets/javascripts/refine-stimulus.esm.js.map +1 -0
  7. data/app/assets/javascripts/refine-stimulus.js +2 -0
  8. data/app/assets/javascripts/refine-stimulus.js.map +1 -0
  9. data/app/assets/javascripts/refine-stimulus.modern.js +2 -0
  10. data/app/assets/javascripts/refine-stimulus.modern.js.map +1 -0
  11. data/app/assets/javascripts/refine-stimulus.umd.js +2 -0
  12. data/app/assets/javascripts/refine-stimulus.umd.js.map +1 -0
  13. data/app/assets/stylesheets/index.css +1873 -0
  14. data/app/assets/stylesheets/index.tailwind.css +1035 -0
  15. data/app/controllers/refine/blueprints_controller.rb +80 -0
  16. data/app/controllers/refine/filter_application_controller.rb +29 -0
  17. data/app/controllers/refine/inline/criteria_controller.rb +161 -0
  18. data/app/controllers/refine/inline/stored_filters_controller.rb +84 -0
  19. data/app/controllers/refine/stored_filters_controller.rb +69 -0
  20. data/app/javascript/controllers/index.js +66 -0
  21. data/app/javascript/controllers/refine/add-controller.js +42 -0
  22. data/app/javascript/controllers/refine/criterion-form-controller.js +31 -0
  23. data/app/javascript/controllers/refine/date-controller.js +113 -0
  24. data/app/javascript/controllers/refine/defaults-controller.js +32 -0
  25. data/app/javascript/controllers/refine/delete-controller.js +13 -0
  26. data/app/javascript/controllers/refine/filter-pills-controller.js +63 -0
  27. data/app/javascript/controllers/refine/form-controller.js +51 -0
  28. data/app/javascript/controllers/refine/inline-conditions-controller.js +33 -0
  29. data/app/javascript/controllers/refine/popup-controller.js +46 -0
  30. data/app/javascript/controllers/refine/search-filter-controller.js +50 -0
  31. data/app/javascript/controllers/refine/server-refresh-controller.js +43 -0
  32. data/app/javascript/controllers/refine/state-controller.js +220 -0
  33. data/app/javascript/controllers/refine/stored-filter-controller.js +23 -0
  34. data/app/javascript/controllers/refine/submit-form-controller.js +8 -0
  35. data/app/javascript/controllers/refine/toggle-controller.js +12 -0
  36. data/app/javascript/controllers/refine/turbo-stream-form-controller.js +24 -0
  37. data/app/javascript/controllers/refine/turbo-stream-link-controller.js +24 -0
  38. data/app/javascript/controllers/refine/update-controller.js +86 -0
  39. data/app/javascript/index.js +1 -0
  40. data/app/javascript/refine/helpers/index.js +77 -0
  41. data/app/models/refine/blueprints/blueprint.rb +58 -0
  42. data/app/models/refine/blueprints/blueprint_example.json +25 -0
  43. data/app/models/refine/conditions/boolean_condition.rb +112 -0
  44. data/app/models/refine/conditions/clause.rb +38 -0
  45. data/app/models/refine/conditions/clauses.rb +38 -0
  46. data/app/models/refine/conditions/condition.rb +285 -0
  47. data/app/models/refine/conditions/condition_error.rb +1 -0
  48. data/app/models/refine/conditions/date_condition.rb +464 -0
  49. data/app/models/refine/conditions/date_with_time_condition.rb +8 -0
  50. data/app/models/refine/conditions/errors/condition_clause_error.rb +7 -0
  51. data/app/models/refine/conditions/errors/criteria_limit_exceeded_error.rb +2 -0
  52. data/app/models/refine/conditions/errors/option_error.rb +2 -0
  53. data/app/models/refine/conditions/errors/relationship_error.rb +1 -0
  54. data/app/models/refine/conditions/filter_condition.rb +93 -0
  55. data/app/models/refine/conditions/has_clauses.rb +117 -0
  56. data/app/models/refine/conditions/has_meta.rb +10 -0
  57. data/app/models/refine/conditions/has_refinements.rb +156 -0
  58. data/app/models/refine/conditions/numeric_condition.rb +224 -0
  59. data/app/models/refine/conditions/option_condition.rb +260 -0
  60. data/app/models/refine/conditions/text_condition.rb +152 -0
  61. data/app/models/refine/conditions/uses_attributes.rb +168 -0
  62. data/app/models/refine/filter.rb +302 -0
  63. data/app/models/refine/filters/blueprint_editor.rb +102 -0
  64. data/app/models/refine/filters/builder.rb +59 -0
  65. data/app/models/refine/filters/criterion.rb +87 -0
  66. data/app/models/refine/filters/query.rb +82 -0
  67. data/app/models/refine/inline/criteria/input.rb +50 -0
  68. data/app/models/refine/inline/criteria/numeric_refinement.rb +13 -0
  69. data/app/models/refine/inline/criteria/option.rb +2 -0
  70. data/app/models/refine/inline/criterion.rb +141 -0
  71. data/app/models/refine/invalid_filter_error.rb +8 -0
  72. data/app/models/refine/stabilize.rb +29 -0
  73. data/app/models/refine/stabilizers/database_stabilizer.rb +21 -0
  74. data/app/models/refine/stabilizers/errors/url_stabilizer_error.rb +2 -0
  75. data/app/models/refine/stabilizers/url_encoded_stabilizer.rb +21 -0
  76. data/app/models/refine/stored_filter.rb +14 -0
  77. data/app/models/refine/tracks_pending_relationship_subqueries.rb +196 -0
  78. data/app/views/_filter_builder_dropdown.html.erb +63 -0
  79. data/app/views/_filter_pills.html.erb +40 -0
  80. data/app/views/_loading.html.erb +32 -0
  81. data/app/views/refine/blueprints/_add_and.html.erb +25 -0
  82. data/app/views/refine/blueprints/_add_group.html.erb +24 -0
  83. data/app/views/refine/blueprints/_clause_select.html.erb +24 -0
  84. data/app/views/refine/blueprints/_condition_select.html.erb +53 -0
  85. data/app/views/refine/blueprints/_criterion.html.erb +41 -0
  86. data/app/views/refine/blueprints/_criterion_errors.html.erb +7 -0
  87. data/app/views/refine/blueprints/_delete_criterion.html.erb +11 -0
  88. data/app/views/refine/blueprints/_group.html.erb +13 -0
  89. data/app/views/refine/blueprints/_query.html.erb +34 -0
  90. data/app/views/refine/blueprints/_stored_filters.html.erb +23 -0
  91. data/app/views/refine/blueprints/clauses/_date_condition.html.erb +80 -0
  92. data/app/views/refine/blueprints/clauses/_date_picker.html.erb +26 -0
  93. data/app/views/refine/blueprints/clauses/_filter_condition.html.erb +36 -0
  94. data/app/views/refine/blueprints/clauses/_numeric_condition.html.erb +35 -0
  95. data/app/views/refine/blueprints/clauses/_option_condition.html.erb +37 -0
  96. data/app/views/refine/blueprints/clauses/_text_condition.html.erb +13 -0
  97. data/app/views/refine/blueprints/create.turbo_stream.erb +22 -0
  98. data/app/views/refine/blueprints/new.html.erb +7 -0
  99. data/app/views/refine/blueprints/show.html.erb +4 -0
  100. data/app/views/refine/blueprints/show.turbo_stream.erb +22 -0
  101. data/app/views/refine/inline/criteria/_form_fields.html.erb +62 -0
  102. data/app/views/refine/inline/criteria/create.turbo_stream.erb +19 -0
  103. data/app/views/refine/inline/criteria/edit.turbo_stream.erb +26 -0
  104. data/app/views/refine/inline/criteria/index.html.erb +64 -0
  105. data/app/views/refine/inline/criteria/new.turbo_stream.erb +24 -0
  106. data/app/views/refine/inline/filters/_add_first_condition_button.html.erb +19 -0
  107. data/app/views/refine/inline/filters/_and_button.html.erb +26 -0
  108. data/app/views/refine/inline/filters/_criterion.html.erb +23 -0
  109. data/app/views/refine/inline/filters/_group.html.erb +13 -0
  110. data/app/views/refine/inline/filters/_load_button.html.erb +15 -0
  111. data/app/views/refine/inline/filters/_or_button.html.erb +26 -0
  112. data/app/views/refine/inline/filters/_popup.html.erb +26 -0
  113. data/app/views/refine/inline/filters/_save_button.html.erb +15 -0
  114. data/app/views/refine/inline/filters/_show.html.erb +40 -0
  115. data/app/views/refine/inline/inputs/_date_condition.html.erb +7 -0
  116. data/app/views/refine/inline/inputs/_date_condition_days.html.erb +18 -0
  117. data/app/views/refine/inline/inputs/_date_condition_range.html.erb +22 -0
  118. data/app/views/refine/inline/inputs/_date_condition_single.html.erb +9 -0
  119. data/app/views/refine/inline/inputs/_date_picker.html.erb +20 -0
  120. data/app/views/refine/inline/inputs/_numeric_condition.html.erb +23 -0
  121. data/app/views/refine/inline/inputs/_option_condition.html.erb +14 -0
  122. data/app/views/refine/inline/inputs/_text_condition.html.erb +8 -0
  123. data/app/views/refine/inline/stored_filters/find.turbo_stream.erb +19 -0
  124. data/app/views/refine/inline/stored_filters/index.html.erb +28 -0
  125. data/app/views/refine/inline/stored_filters/new.turbo_stream.erb +47 -0
  126. data/app/views/refine/stored_filters/create.turbo_stream.erb +2 -0
  127. data/app/views/refine/stored_filters/find.turbo_stream.erb +5 -0
  128. data/app/views/refine/stored_filters/index.html.erb +39 -0
  129. data/app/views/refine/stored_filters/new.html.erb +29 -0
  130. data/app/views/refine/stored_filters/show.html.erb +1 -0
  131. data/config/locales/en/dates.en.yml +29 -0
  132. data/config/locales/en/en.yml +20 -0
  133. data/config/locales/en/refine.en.yml +187 -0
  134. data/config/routes.rb +17 -0
  135. data/lib/generators/filter/filter_generator.rb +27 -0
  136. data/lib/generators/filter/templates/filter.rb.erb +20 -0
  137. data/lib/refine/rails/engine.rb +15 -0
  138. data/lib/refine/rails/version.rb +5 -0
  139. data/lib/refine/rails.rb +38 -0
  140. data/lib/tasks/refine/rails_tasks.rake +13 -0
  141. metadata +202 -0
@@ -0,0 +1,80 @@
1
+ module Refine
2
+ class BlueprintsController < ApplicationController
3
+ layout false
4
+ before_action :set_builder
5
+ before_action :set_filter
6
+ before_action :set_form
7
+
8
+ # entry point for initial render of the filter builder
9
+ def new
10
+ @show_stored_filters = params[:stored_filters]
11
+ end
12
+
13
+ # refresh the filter builder
14
+ def show
15
+ if @refine_filter_query.valid?
16
+ @stable_id = @refine_filter.to_stable_id
17
+ end
18
+
19
+ # don't display errors
20
+ include_errors = ActiveRecord::Type::Boolean.new.cast(params[:include_errors])
21
+ @refine_filter_query.clear_errors unless include_errors
22
+
23
+ respond_to do |format|
24
+ format.turbo_stream
25
+ format.html
26
+ end
27
+ end
28
+
29
+ # handles filter submission
30
+ def create
31
+ if @refine_filter_query.valid?
32
+ # set stable_id
33
+ @stable_id = @refine_filter.to_stable_id
34
+
35
+ # set url
36
+ uri = URI(request.referrer)
37
+ new_query_ar = URI.decode_www_form(String(uri.query))
38
+ new_query_ar.reject! { |(k, _v)| k == "stable_id" }
39
+ new_query_ar << ["stable_id", @stable_id]
40
+ uri.query = URI.encode_www_form(new_query_ar)
41
+ @url_for_redirect = uri.to_s
42
+ @filter_submit_success = true
43
+ end
44
+ end
45
+
46
+ def stored_filters
47
+ render partial: "stored_filters", layout: false
48
+ end
49
+
50
+ def validate
51
+ if @refine_filter_query.valid?
52
+ head :ok
53
+ else
54
+ head :unprocessable_entity
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def set_builder
61
+ builder_params = params.require(:refine_filters_builder).permit(
62
+ :blueprint_json,
63
+ :filter_class,
64
+ :stable_id,
65
+ :stored_filter_id,
66
+ :client_id
67
+ )
68
+
69
+ @refine_filter_builder = Refine::Filters::Builder.new(builder_params)
70
+ end
71
+
72
+ def set_filter
73
+ @refine_filter = @refine_filter_builder.refine_filter
74
+ end
75
+
76
+ def set_form
77
+ @refine_filter_query = @refine_filter_builder.query
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,29 @@
1
+ module Refine::FilterApplicationController
2
+ # Optional module to get started quickly.You can send in the current controller's instance variable if you'd like to update the collection here
3
+ # The current scope can be used to modify the query
4
+
5
+
6
+ def apply_filter(filter_class, initial_query: nil)
7
+ if filter_class.present?
8
+ @stable_id = params[:stable_id]
9
+ @refine_filter_builder = Refine::Filters::Builder.new(
10
+ stable_id: @stable_id,
11
+ filter_class: filter_class.name,
12
+ initial_query: initial_query)
13
+ @refine_filter = @refine_filter_builder.refine_filter
14
+ end
15
+ end
16
+
17
+ # Use this on pages that use the new inline filter
18
+ def apply_inline_filter(filter_class, initial_query: nil)
19
+ stable_id = params[:stable_id]
20
+
21
+ if stable_id.present?
22
+ @refine_filter = Refine::Rails.configuration.stabilizer_classes[:url].new.from_stable_id(id: stable_id, initial_query: initial_query)
23
+ else
24
+ @refine_filter = filter_class.new([], initial_query)
25
+ end
26
+ @refine_client_id = SecureRandom.uuid
27
+ @refine_stable_id = @refine_filter.to_stable_id
28
+ end
29
+ end
@@ -0,0 +1,161 @@
1
+ class Refine::Inline::CriteriaController < ApplicationController
2
+ layout false
3
+ before_action :set_refine_filter
4
+
5
+ # List available conditions for new criteria
6
+ # Carries position and index forward
7
+ def index
8
+ @criterion = Refine::Inline::Criterion.new(criterion_params.merge(refine_filter: @refine_filter))
9
+ @conditions = @refine_filter.instantiated_conditions
10
+ end
11
+
12
+ # Show the form to add a new criteria
13
+ def new
14
+ @criterion = Refine::Inline::Criterion.new(criterion_params.merge(refine_filter: @refine_filter))
15
+ respond_to do |format|
16
+ # render a turbo_stream so that we can render inputs and forms in separate areas of the page
17
+ # in case we're nested inside a form
18
+ format.turbo_stream
19
+ end
20
+ end
21
+
22
+ # Create a new criterion
23
+ def create
24
+ @criterion = Refine::Inline::Criterion.new(criterion_params.merge(refine_filter: @refine_filter))
25
+ blueprint = @refine_filter.blueprint
26
+
27
+ Refine::Filters::BlueprintEditor
28
+ .new(@refine_filter.blueprint)
29
+ .add(
30
+ position: @criterion.position.to_i,
31
+ conjunction: @criterion.conjunction,
32
+ criterion: @criterion.to_blueprint_node
33
+ )
34
+
35
+ @error_messages = filter_error_messages(@refine_filter)
36
+ if @error_messages.none?
37
+ handle_filter_update(@refine_filter.to_stable_id)
38
+ else
39
+ respond_to do |format|
40
+ format.turbo_stream { render :new }
41
+ end
42
+ end
43
+ end
44
+
45
+ # show the form to edit an existing criterion
46
+ def edit
47
+ @criterion = Refine::Inline::Criterion
48
+ .groups_from_filter(@refine_filter, **criterion_params.slice(:client_id, :stable_id))
49
+ .flatten
50
+ .detect { |c| c.position.to_s == params[:id] }
51
+
52
+ @criterion.attributes = criterion_params
53
+ respond_to do |format|
54
+ # render a turbo_stream so that we can render inputs and forms in separate areas of the page
55
+ # in case we're nested inside a form
56
+ format.turbo_stream
57
+ end
58
+ end
59
+
60
+ # update an exsiting criterion
61
+ def update
62
+ @criterion = Refine::Inline::Criterion.new(criterion_params.merge(refine_filter: @refine_filter))
63
+ Refine::Filters::BlueprintEditor
64
+ .new(@refine_filter.blueprint)
65
+ .update(params[:id].to_i, criterion: @criterion.to_blueprint_node)
66
+
67
+ @error_messages = filter_error_messages(@refine_filter)
68
+ if @error_messages.none?
69
+ handle_filter_update(@refine_filter.to_stable_id)
70
+ else
71
+ respond_to do |format|
72
+ format.turbo_stream { render :edit }
73
+ end
74
+ end
75
+ end
76
+
77
+ # remove an existing criterion
78
+ def destroy
79
+ @criterion = Refine::Inline::Criterion
80
+ .groups_from_filter(@refine_filter, **criterion_params.slice(:client_id, :stable_id))
81
+ .flatten
82
+ .detect { |c| c.position.to_s == params[:id] }
83
+
84
+ Refine::Filters::BlueprintEditor
85
+ .new(@refine_filter.blueprint)
86
+ .delete(params[:id].to_i)
87
+
88
+ handle_filter_update(@refine_filter.to_stable_id)
89
+ end
90
+
91
+ private
92
+
93
+ def set_refine_filter
94
+ @refine_filter ||= Refine::Rails.configuration.stabilizer_classes[:url]
95
+ .new
96
+ .from_stable_id(id: criterion_params[:stable_id])
97
+ end
98
+
99
+ def criterion_params
100
+ params.require(:refine_inline_criterion).permit(
101
+ :stable_id,
102
+ :client_id,
103
+ :condition_id,
104
+ :position,
105
+ :conjunction,
106
+ input_attributes: [
107
+ :clause,
108
+ :date1,
109
+ :date2,
110
+ :days,
111
+ :modifier,
112
+ :selected,
113
+ :value,
114
+ :value1,
115
+ :value2,
116
+ selected: [],
117
+ count_refinement_attributes: [
118
+ :clause,
119
+ :value1,
120
+ :value2
121
+ ]
122
+ ]
123
+ )
124
+ end
125
+
126
+ # either directly redirect or emit a `filter-submit-success` event
127
+ def handle_filter_update stable_id
128
+ # update_stable_id in url
129
+ uri = URI(request.referrer)
130
+ new_query_ar = URI.decode_www_form(String(uri.query))
131
+ new_query_ar.reject! { |(k, _v)| k == "stable_id" }
132
+ new_query_ar << ["stable_id", stable_id]
133
+ uri.query = URI.encode_www_form(new_query_ar)
134
+
135
+ respond_to do |format|
136
+ format.turbo_stream do
137
+ @refine_stable_id = stable_id
138
+ @url_for_redirect = uri
139
+ @refine_client_id = @criterion.client_id
140
+ render :create
141
+ end
142
+ format.html {redirect_to uri.to_s }
143
+ end
144
+ end
145
+
146
+ def filter_valid?(refine_filter)
147
+ Refine::Inline::Criterion
148
+ .groups_from_filter(refine_filter, **criterion_params.slice(:client_id, :stable_id))
149
+ .flatten
150
+ .all?(&:valid?)
151
+ end
152
+
153
+ def filter_error_messages(refine_filter)
154
+ criteria = Refine::Inline::Criterion
155
+ .groups_from_filter(refine_filter, **criterion_params.slice(:client_id, :stable_id))
156
+ .flatten
157
+
158
+ criteria.each(&:validate!)
159
+ criteria.flat_map {|c| c.errors.full_messages }
160
+ end
161
+ end
@@ -0,0 +1,84 @@
1
+ class Refine::Inline::StoredFiltersController < ApplicationController
2
+ layout false
3
+ before_action :set_refine_filter
4
+
5
+
6
+ def index
7
+ @criterion = Refine::Inline::Criterion.new(criterion_params)
8
+ @stored_filters = Refine::StoredFilter.where(filter_type: @refine_filter.type)
9
+ @stored_filters = instance_exec(@stored_filters, &Refine::Rails.configuration.stored_filter_scope)
10
+ end
11
+
12
+ def find
13
+ @stored_filter = Refine::StoredFilter.find_by(id: params[:id], filter_type: @refine_filter.type)
14
+ handle_filter_update @stored_filter.refine_filter.to_stable_id
15
+ end
16
+
17
+ def new
18
+ @criterion = Refine::Inline::Criterion.new(criterion_params)
19
+ @stored_filter = Refine::StoredFilter.new(filter_type: @refine_filter.type)
20
+ respond_to do |format|
21
+ format.turbo_stream
22
+ end
23
+ end
24
+
25
+ def create
26
+ @criterion = Refine::Inline::Criterion.new(criterion_params)
27
+ @stored_filter = Refine::StoredFilter.new(stored_filter_params.merge(
28
+ state: @refine_filter.state,
29
+ filter_type: @refine_filter.type,
30
+ **instance_exec(&Refine::Rails.configuration.custom_stored_filter_attributes)
31
+ )
32
+ )
33
+
34
+ if @stored_filter.save
35
+ handle_filter_update @stored_filter.refine_filter.to_stable_id
36
+ else
37
+ respond_to do |format|
38
+ format.turbo_stream { render :new }
39
+ end
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def set_refine_filter
46
+ @refine_filter ||= Refine::Rails.configuration.stabilizer_classes[:url]
47
+ .new
48
+ .from_stable_id(id: criterion_params[:stable_id])
49
+ end
50
+
51
+ def criterion_params
52
+ params.require(:refine_inline_criterion).permit(
53
+ :stable_id,
54
+ :client_id,
55
+ :position,
56
+ :conjunction
57
+ )
58
+ end
59
+
60
+ def stored_filter_params
61
+ params.require(:refine_stored_filter).permit(:name)
62
+ end
63
+
64
+ # either directly redirect or emit a `filter-submit-success` event
65
+ def handle_filter_update stable_id
66
+ # update_stable_id in url
67
+ uri = URI(request.referrer)
68
+ new_query_ar = URI.decode_www_form(String(uri.query))
69
+ new_query_ar.reject! { |(k, _v)| k == "stable_id" }
70
+ new_query_ar << ["stable_id", stable_id]
71
+ uri.query = URI.encode_www_form(new_query_ar)
72
+
73
+ respond_to do |format|
74
+ format.turbo_stream do
75
+ @refine_stable_id = stable_id
76
+ @url_for_redirect = uri
77
+ @refine_client_id = criterion_params[:client_id]
78
+ @refine_filter = @stored_filter.refine_filter
79
+ render :find
80
+ end
81
+ format.html {redirect_to uri.to_s }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,69 @@
1
+ module Refine
2
+ class StoredFiltersController < ApplicationController
3
+ layout false
4
+
5
+ before_action :set_builder
6
+
7
+ def index
8
+ @stored_filters = StoredFilter.where(filter_type: @refine_filter_builder.filter_class)
9
+ @stored_filters = instance_exec(@stored_filters, &Refine::Rails.configuration.stored_filter_scope)
10
+ end
11
+
12
+ def find
13
+ @stored_filter = StoredFilter.find_by(id: params[:id])
14
+ @refine_filter_builder.stored_filter_id = @stored_filter.id
15
+
16
+ # KLUDGE we need a base scope for determining if the filter is valid.
17
+ # We set initial_queryto model.all because this filter is used just for validation
18
+ # and is never evaluated.
19
+ #
20
+ # This method is a temporary workaround until we clean up input validation and
21
+ # filter evaluation
22
+ filter_for_model = @stored_filter.refine_filter
23
+ model = filter_for_model.model # AR::Base subclass
24
+ @refine_filter = @stored_filter.refine_filter(initial_query: model.all)
25
+ @refine_filter_query = Refine::Filters::Query.new(@refine_filter)
26
+ unless @refine_filter.valid_query?
27
+ redirect_to refine_stored_filters_path(@refine_filter_builder.to_params),
28
+ alert: "Sorry, that filter is not valid"
29
+ end
30
+ end
31
+
32
+ def new
33
+ @stored_filter = StoredFilter.new(filter_type: @refine_filter_builder.filter_class)
34
+ end
35
+
36
+ def create
37
+ @refine_filter = @refine_filter_builder.refine_filter
38
+
39
+ @stored_filter = StoredFilter.new(name: params[:name], state: @refine_filter.state, filter_type: @refine_filter.type, **instance_exec(&Refine::Rails.configuration.custom_stored_filter_attributes))
40
+ @refine_filter_query = @refine_filter_builder.query
41
+
42
+ if !@refine_filter_query.valid?
43
+ # replace the filter form with errors
44
+ render :create
45
+ elsif !@stored_filter.save
46
+ # replace the stored filter form
47
+ render :new, status: :unprocessable_entity
48
+ else
49
+ # return to stored filters header and load the filter into the query builder
50
+ @refine_filter_builder.stored_filter_id = @stored_filter.id
51
+ render :find
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def set_builder
58
+ builder_params = params.require(:refine_filters_builder).permit(
59
+ :blueprint_json,
60
+ :filter_class,
61
+ :stable_id,
62
+ :stored_filter_id,
63
+ :client_id,
64
+ )
65
+
66
+ @refine_filter_builder = Refine::Filters::Builder.new(builder_params)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,66 @@
1
+ import { identifierForContextKey } from "@hotwired/stimulus-webpack-helpers"
2
+
3
+ import AddController from './refine/add-controller'
4
+ import InlineConditionsController from './refine/inline-conditions-controller'
5
+ import CriterionFormController from './refine/criterion-form-controller'
6
+ import DefaultsController from './refine/defaults-controller'
7
+ import DeleteController from './refine/delete-controller'
8
+ import FilterPillsController from './refine/filter-pills-controller'
9
+ import PopupController from './refine/popup-controller'
10
+ import SearchFilterController from './refine/search-filter-controller'
11
+ import ServerRefreshController from './refine/server-refresh-controller'
12
+ import StateController from './refine/state-controller'
13
+ import StoredFilterController from './refine/stored-filter-controller'
14
+ import SubmitForm from './refine/submit-form-controller'
15
+ import ToggleController from './refine/toggle-controller'
16
+ import TurboStreamFormController from './refine/turbo-stream-form-controller'
17
+ import TurboStreamLinkController from './refine/turbo-stream-link-controller'
18
+ import UpdateController from './refine/update-controller'
19
+ import DateController from './refine/date-controller'
20
+
21
+ export const controllerDefinitions = [
22
+ [AddController, 'refine/add-controller.js'],
23
+ [InlineConditionsController, './refine/inline-conditions-controller.js'],
24
+ [CriterionFormController, 'refine/criterion-form-controller.js'],
25
+ [DefaultsController, 'refine/defaults-controller.js'],
26
+ [DeleteController, 'refine/delete-controller.js'],
27
+ [FilterPillsController, 'refine/filter-pills-controller.js'],
28
+ [PopupController, 'refine/popup-controller.js'],
29
+ [SearchFilterController, 'refine/search-filter-controller.js'],
30
+ [ServerRefreshController, 'refine/server-refresh-controller.js'],
31
+ [StateController, 'refine/state-controller.js'],
32
+ [StoredFilterController, 'refine/stored-filter-controller.js'],
33
+ [SubmitForm, 'refine/submit-form-controller.js'],
34
+ [ToggleController, 'refine/toggle-controller.js'],
35
+ [TurboStreamFormController, 'refine/turbo-stream-form-controller.js'],
36
+ [TurboStreamLinkController, 'refine/turbo-stream-link-controller.js'],
37
+ [UpdateController, 'refine/update-controller.js'],
38
+ [DateController, 'refine/date-controller.js']
39
+ ].map(function(d) {
40
+ const key = d[1]
41
+ const controller = d[0]
42
+ return {
43
+ identifier: identifierForContextKey(key),
44
+ controllerConstructor: controller
45
+ }
46
+ })
47
+
48
+ export {
49
+ AddController,
50
+ InlineConditionsController,
51
+ CriterionFormController,
52
+ DefaultsController,
53
+ DeleteController,
54
+ FilterPillsController,
55
+ PopupController,
56
+ SearchFilterController,
57
+ ServerRefreshController,
58
+ StateController,
59
+ StoredFilterController,
60
+ SubmitForm,
61
+ ToggleController,
62
+ TurboStreamFormController,
63
+ TurboStreamLinkController,
64
+ UpdateController,
65
+ DateController
66
+ }
@@ -0,0 +1,42 @@
1
+ import ServerRefreshController from './server-refresh-controller'
2
+ import { FetchRequest } from '@rails/request.js'
3
+
4
+ export default class extends ServerRefreshController {
5
+ static values = {
6
+ previousCriterionId: Number,
7
+ }
8
+
9
+ async criterion() {
10
+ const isValid = await this.validateBlueprint()
11
+ if (isValid) {
12
+ this.state.addCriterion(this.previousCriterionIdValue)
13
+ }
14
+ this.refreshFromServer({includeErrors: !isValid})
15
+ }
16
+
17
+ async group() {
18
+ const isValid = await this.validateBlueprint()
19
+ if (isValid) {
20
+ this.state.addGroup()
21
+ }
22
+ this.refreshFromServer({includeErrors: !isValid})
23
+ }
24
+
25
+ async validateBlueprint(blueprint) {
26
+ const { state } = this
27
+
28
+ const request = new FetchRequest(
29
+ "GET",
30
+ this.state.validateBlueprintUrlValue,
31
+ {
32
+ query: {
33
+ "refine_filters_builder[filter_class]": this.state.filterName,
34
+ "refine_filters_builder[blueprint_json]": JSON.stringify(this.state.blueprint),
35
+ "refine_filters_builder[client_id]": this.state.clientIdValue
36
+ }
37
+ }
38
+ )
39
+ const response = await request.perform()
40
+ return response.ok
41
+ }
42
+ }
@@ -0,0 +1,31 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { FetchRequest } from '@rails/request.js'
3
+
4
+ /*
5
+ This controller handles criteria forms
6
+ (refine/inline/criteria/new|edit)
7
+ */
8
+ export default class extends Controller {
9
+ static values = {
10
+ url: String,
11
+ turboFrame: String,
12
+ method: { type: String, default: "POST" }
13
+ }
14
+
15
+ refresh(_event) {
16
+ // update the url with params from the form
17
+ const formData = new FormData(this.element)
18
+ const url = new URL(this.urlValue)
19
+
20
+ for (const [name, value] of formData.entries()) {
21
+ console.log(name, value)
22
+ url.searchParams.set(name, value)
23
+ }
24
+
25
+ // navigate the modal to refresh the form
26
+ window.Turbo.visit(url.toString(), {frame: this.turboFrameValue})
27
+ }
28
+
29
+
30
+
31
+ }
@@ -0,0 +1,113 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import moment from 'moment'
3
+ require('daterangepicker/daterangepicker.css')
4
+
5
+ // requires jQuery, moment, might want to consider a vanilla JS alternative
6
+ import $ from 'jquery' // ensure jquery is loaded before daterangepicker
7
+ import 'daterangepicker'
8
+
9
+ export default class extends Controller {
10
+ static targets = [
11
+ 'field',
12
+ 'hiddenField',
13
+ 'clearButton',
14
+ ]
15
+
16
+ static values = {
17
+ includeTime: Boolean,
18
+ futureOnly: Boolean,
19
+ drops: String,
20
+ inline: Boolean,
21
+ dateFormat: String,
22
+ timeFormat: String,
23
+ isAmPm: Boolean,
24
+ locale: { type: String, default: 'en' },
25
+ datetimeFormat: { type: String, default: 'MM/DD/YYYY h:mm A' },
26
+ pickerLocale: { type: Object, default: {} },
27
+ }
28
+
29
+ connect() {
30
+ this.initPluginInstance()
31
+ }
32
+
33
+ disconnect() {
34
+ this.teardownPluginInstance()
35
+ }
36
+
37
+ clearDate(event) {
38
+ // don't submit the form, unless it originated from the cancel/clear button
39
+ event.preventDefault()
40
+
41
+ window.$(this.fieldTarget).val('')
42
+
43
+ this.dispatch('value-cleared')
44
+ }
45
+
46
+ applyDateToField(event, picker) {
47
+ const format = this.includeTimeValue ? this.timeFormatValue : this.dateFormatValue
48
+
49
+ const momentVal = picker
50
+ ? moment(picker.startDate.toISOString())
51
+ : moment(this.fieldTarget.value, 'YYYY-MM-DDTHH:mm').format('YYYY-MM-DDTHH:mm')
52
+ const displayVal = momentVal.format(format)
53
+ const dataVal = this.includeTimeValue ? momentVal.toISOString(true) : momentVal.format('YYYY-MM-DD')
54
+
55
+ this.fieldTarget.value = displayVal
56
+ this.hiddenFieldTarget.value = dataVal
57
+ // bubble up a change event when the input is updated for other listeners
58
+ window.$(this.fieldTarget).trigger('change', picker)
59
+
60
+ // emit native change event
61
+ this.hiddenFieldTarget.dispatchEvent(new Event('change', { detail: picker, bubbles: true }))
62
+ }
63
+
64
+ initPluginInstance() {
65
+ const localeValues = this.pickerLocaleValue
66
+ const isAmPm = this.isAmPmValue
67
+ localeValues['format'] = this.includeTimeValue ? this.timeFormatValue : this.dateFormatValue
68
+
69
+ window.$(this.fieldTarget).daterangepicker({
70
+ singleDatePicker: true,
71
+ timePicker: this.includeTimeValue,
72
+ timePickerIncrement: 5,
73
+ autoUpdateInput: false,
74
+ autoApply: true,
75
+ minDate: this.futureOnlyValue ? new Date() : false,
76
+ locale: localeValues,
77
+ parentEl: $(this.element),
78
+ drops: this.dropsValue ? this.dropsValue : 'down',
79
+ timePicker24Hour: !isAmPm,
80
+ })
81
+
82
+ window.$(this.fieldTarget).on('apply.daterangepicker', this.applyDateToField.bind(this))
83
+ window.$(this.fieldTarget).on('cancel.daterangepicker', this.clearDate.bind(this))
84
+ window.$(this.fieldTarget).on('showCalendar.daterangepicker', this.showCalendar.bind(this))
85
+
86
+ this.pluginMainEl = this.fieldTarget
87
+ this.plugin = $(this.pluginMainEl).data('daterangepicker') // weird
88
+
89
+ if (this.inlineValue) {
90
+ this.element.classList.add('date-input--inline')
91
+ }
92
+
93
+ }
94
+
95
+ teardownPluginInstance() {
96
+ if (this.plugin === undefined) {
97
+ return
98
+ }
99
+
100
+ $(this.pluginMainEl).off('apply.daterangepicker')
101
+ $(this.pluginMainEl).off('cancel.daterangepicker')
102
+ $(this.pluginMainEl).off('showCalendar.daterangepicker')
103
+
104
+ // revert to original markup, remove any event listeners
105
+ this.plugin.remove()
106
+
107
+ }
108
+
109
+ showCalendar() {
110
+ this.dispatch('show-calendar')
111
+ }
112
+
113
+ }