refine-rails 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }