plutonium 0.15.0 → 0.15.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/views/resource/_interactive_action_form.html.erb +1 -0
  3. data/app/views/resource/{interactive_resource_collection_action.html.erb → interactive_bulk_action.html.erb} +1 -1
  4. data/app/views/resource/interactive_record_action.html.erb +1 -0
  5. data/app/views/resource/{interactive_resource_record_action.html.erb → interactive_resource_action.html.erb} +1 -1
  6. data/lib/plutonium/action/base.rb +6 -3
  7. data/lib/plutonium/action/interactive.rb +16 -9
  8. data/lib/plutonium/definition/base.rb +8 -1
  9. data/lib/plutonium/definition/sorting.rb +15 -0
  10. data/lib/plutonium/interaction/README.md +34 -1
  11. data/lib/plutonium/interaction/base.rb +38 -7
  12. data/lib/plutonium/interaction/concerns/presentable.rb +24 -12
  13. data/lib/plutonium/interaction/outcome.rb +77 -55
  14. data/lib/plutonium/interaction/response/base.rb +3 -1
  15. data/lib/plutonium/interaction/response/failure.rb +18 -0
  16. data/lib/plutonium/interaction/response/file.rb +20 -0
  17. data/lib/plutonium/interaction/response/redirect.rb +1 -11
  18. data/lib/plutonium/interaction/response/render.rb +1 -9
  19. data/lib/plutonium/resource/controllers/authorizable.rb +29 -5
  20. data/lib/plutonium/resource/controllers/interactive_actions.rb +171 -130
  21. data/lib/plutonium/resource/controllers/queryable.rb +2 -2
  22. data/lib/plutonium/resource/interaction.rb +1 -3
  23. data/lib/plutonium/resource/query_object.rb +2 -2
  24. data/lib/plutonium/routing/mapper_extensions.rb +9 -9
  25. data/lib/plutonium/ui/action_button.rb +4 -3
  26. data/lib/plutonium/ui/component/methods.rb +1 -0
  27. data/lib/plutonium/ui/display/theme.rb +4 -3
  28. data/lib/plutonium/ui/form/interaction.rb +35 -0
  29. data/lib/plutonium/ui/form/resource.rb +1 -1
  30. data/lib/plutonium/ui/page/interactive_action.rb +23 -0
  31. data/lib/plutonium/ui/table/components/search_bar.rb +1 -1
  32. data/lib/plutonium/ui/table/display_theme.rb +2 -2
  33. data/lib/plutonium/ui/table/resource.rb +11 -5
  34. data/lib/plutonium/version.rb +1 -1
  35. data/lib/plutonium.rb +1 -0
  36. metadata +11 -21
  37. data/app/views/resource/_interactive_resource_action_form.html.erb +0 -45
  38. data/app/views/resource/interactive_resource_recordless_action.html.erb +0 -5
  39. data/gemfiles/rails_7.gemfile.lock +0 -339
@@ -26,7 +26,6 @@ module Plutonium
26
26
  class ActionMissingCurrentAuthorizedScope < ActionPolicy::UnauthorizedAction; end
27
27
 
28
28
  included do
29
- verify_authorized
30
29
  after_action :verify_authorize_current
31
30
  after_action :verify_current_authorized_scope, except: %i[new create]
32
31
 
@@ -35,28 +34,53 @@ module Plutonium
35
34
  attr_writer :authorize_current_count
36
35
  attr_writer :current_authorized_scope_count
37
36
 
37
+ attr_reader :verify_authorize_current_skipped
38
+ attr_reader :verify_current_authorized_scope_skipped
39
+
38
40
  protected :authorize_current_count=, :authorize_current_count
39
41
  protected :current_authorized_scope_count=, :current_authorized_scope_count
40
42
  end
41
43
 
44
+ class_methods do
45
+ # Skips verify_authorize_current after_action callback.
46
+ def skip_verify_authorize_current(**options)
47
+ skip_after_action :verify_authorize_current, options
48
+ end
49
+
50
+ # Skips verify_current_authorized_scope after_action callback.
51
+ def skip_verify_current_authorized_scope(**options)
52
+ skip_after_action :verify_current_authorized_scope, options
53
+ end
54
+ end
55
+
42
56
  private
43
57
 
58
+ def skip_verify_authorize_current!
59
+ @verify_authorize_current_skipped = true
60
+ end
61
+
62
+ def skip_verify_current_authorized_scope!
63
+ @verify_current_authorized_scope_skipped = true
64
+ end
65
+
44
66
  # Verifies that authorize_current has been called
45
67
  #
46
68
  # @raise [ActionMissingAuthorizeCurrent] if authorize_current hasn't been called
47
69
  def verify_authorize_current
48
- return if verify_authorized_skipped
70
+ return if verify_authorize_current_skipped
71
+ return if authorize_current_count > 0
49
72
 
50
- raise ActionMissingAuthorizeCurrent.new(controller_path, action_name) if authorize_current_count.zero?
73
+ raise ActionMissingAuthorizeCurrent.new(controller_path, action_name)
51
74
  end
52
75
 
53
76
  # Verifies that current_authorized_scope has been called
54
77
  #
55
78
  # @raise [ActionMissingCurrentAuthorizedScope] if current_authorized_scope hasn't been called
56
79
  def verify_current_authorized_scope
57
- return if verify_authorized_skipped
80
+ return if current_authorized_scope_count
81
+ return if current_authorized_scope_count > 0
58
82
 
59
- raise ActionMissingCurrentAuthorizedScope.new(controller_path, action_name) if current_authorized_scope_count.zero?
83
+ raise ActionMissingCurrentAuthorizedScope.new(controller_path, action_name)
60
84
  end
61
85
 
62
86
  # @return [Integer] the number of times authorize_current has been called
@@ -7,169 +7,178 @@ module Plutonium
7
7
  included do
8
8
  helper_method :current_interactive_action
9
9
 
10
- before_action :validate_interactive_resource_action!, only: %i[
11
- begin_interactive_resource_record_action commit_interactive_resource_record_action
12
- begin_interactive_resource_collection_action commit_interactive_resource_collection_action
13
- begin_interactive_resource_recordless_action commit_interactive_resource_recordless_action
10
+ before_action :validate_interactive_action!, only: %i[
11
+ interactive_record_action commit_interactive_record_action
12
+ interactive_bulk_action commit_interactive_bulk_action
13
+ interactive_resource_action commit_interactive_resource_action
14
14
  ]
15
15
 
16
- before_action :authorize_interactive_resource_record_action!, only: %i[
17
- begin_interactive_resource_record_action commit_interactive_resource_record_action
16
+ before_action :authorize_interactive_record_action!, only: %i[
17
+ interactive_record_action commit_interactive_record_action
18
18
  ]
19
19
 
20
20
  before_action :authorize_interactive_resource_action!, only: %i[
21
- begin_interactive_resource_collection_action commit_interactive_resource_collection_action
22
- begin_interactive_resource_recordless_action commit_interactive_resource_recordless_action
21
+ interactive_bulk_action commit_interactive_bulk_action
22
+ interactive_resource_action commit_interactive_resource_action
23
23
  ]
24
24
  end
25
25
 
26
- # GET /resources/1/actions/:interactive_action
27
- def begin_interactive_resource_record_action
28
- @interaction = current_interactive_action.interaction.new interaction_params
26
+ # GET /resources/1/record_actions/:interactive_action
27
+ def interactive_record_action
28
+ build_interactive_record_action_interaction
29
29
 
30
30
  if helpers.current_turbo_frame == "modal"
31
31
  render layout: false
32
32
  else
33
- render :interactive_resource_record_action
33
+ render :interactive_record_action
34
34
  end
35
35
  end
36
36
 
37
- # POST /resources/1/actions/:interactive_action
38
- def commit_interactive_resource_record_action
39
- respond_to do |format|
40
- inputs = interaction_params.merge(resource: resource_record)
41
- @interaction = current_interactive_action.interaction.run(inputs)
42
-
43
- if @interaction.valid?
44
- flash[:notice] = "TODO:#{current_interactive_action} was successfully updated."
45
-
46
- format.html { redirect_to resource_url_for(@interaction.result), status: :see_other }
47
- format.any { render :show, status: :ok, location: resource_url_for(@interaction.result) }
48
-
49
- if helpers.current_turbo_frame == "modal"
50
- format.turbo_stream do
51
- render turbo_stream: [
52
- turbo_stream.redirect(resource_url_for(@interaction.result))
53
- ]
37
+ # POST /resources/1/record_actions/:interactive_action
38
+ def commit_interactive_record_action
39
+ build_interactive_record_action_interaction
40
+
41
+ outcome = @interaction.call
42
+ if outcome.success?
43
+ outcome.to_response.process(self) do |value|
44
+ respond_to do |format|
45
+ return_url = redirect_url_after_action_on(resource_record)
46
+ format.any { redirect_to return_url, status: :see_other }
47
+ if helpers.current_turbo_frame == "modal"
48
+ format.turbo_stream do
49
+ render turbo_stream: [
50
+ turbo_stream.redirect(return_url)
51
+ ]
52
+ end
54
53
  end
55
54
  end
56
- else
57
- format.html do
58
- render :interactive_resource_record_action, status: :unprocessable_entity
59
- end
60
- format.any do
61
- @errors = @interaction.errors
62
- render "errors", status: :unprocessable_entity
63
- end
64
-
65
- if helpers.current_turbo_frame == "modal"
66
- format.turbo_stream do
67
- render turbo_stream: turbo_stream.replace(:modal, partial: "interactive_resource_record_action_form")
55
+ end
56
+ else
57
+ outcome.to_response.process(self) do
58
+ respond_to do |format|
59
+ format.html do
60
+ render :interactive_record_action, status: :unprocessable_entity
61
+ end
62
+ format.any do
63
+ @errors = @interaction.errors
64
+ render "errors", status: :unprocessable_entity
65
+ end
66
+ if helpers.current_turbo_frame == "modal"
67
+ format.turbo_stream do
68
+ render turbo_stream: [
69
+ turbo_stream.replace(:modal, partial: "interactive_action_form")
70
+ ]
71
+ end
68
72
  end
69
73
  end
70
74
  end
71
75
  end
72
76
  end
73
77
 
74
- # GET /resources/actions/:interactive_action?ids[]=1&ids[]=2
75
- def begin_interactive_resource_collection_action
76
- # TODO: ensure that the selected list matches the returned value
77
- interactive_resource_collection
78
- @interaction = current_interactive_action.interaction.new((params[:interaction] || {}).except(:resources))
78
+ # GET /resources/resource_actions/:interactive_action
79
+ def interactive_resource_action
80
+ skip_verify_current_authorized_scope!
81
+ build_interactive_resource_action_interaction
79
82
 
80
83
  if helpers.current_turbo_frame == "modal"
81
84
  render layout: false
82
85
  else
83
- render :interactive_resource_collection_action
86
+ render :interactive_resource_action
84
87
  end
85
88
  end
86
89
 
87
- # POST /resources/actions/:interactive_action?ids[]=1&ids[]=2
88
- def commit_interactive_resource_collection_action
89
- respond_to do |format|
90
- inputs = interaction_params.merge(resources: interactive_resource_collection)
91
- @interaction = current_interactive_action.interaction.run(inputs)
92
-
93
- if @interaction.valid?
94
- collection_count = interactive_resource_collection.size
95
-
96
- flash[:notice] = "TODO:#{current_interactive_action} #{collection_count} #{resource_class.model_name.human.pluralize(collection_count)} successfully updated."
97
-
98
- format.html { redirect_to resource_url_for(resource_class) }
99
- if helpers.current_turbo_frame == "modal"
100
- format.turbo_stream do
101
- render turbo_stream: [
102
- turbo_stream.redirect(resource_url_for(resource_class))
103
- ]
90
+ # POST /resources/resource_actions/:interactive_action
91
+ def commit_interactive_resource_action
92
+ skip_verify_current_authorized_scope!
93
+ build_interactive_resource_action_interaction
94
+
95
+ outcome = @interaction.call
96
+ if outcome.success?
97
+ outcome.to_response.process(self) do |value|
98
+ respond_to do |format|
99
+ return_url = redirect_url_after_action_on(resource_class)
100
+ format.any { redirect_to return_url, status: :see_other }
101
+ if helpers.current_turbo_frame == "modal"
102
+ format.turbo_stream do
103
+ render turbo_stream: [
104
+ turbo_stream.redirect(return_url)
105
+ ]
106
+ end
104
107
  end
105
108
  end
106
- else
107
- format.html do
108
- render :interactive_resource_collection_action, status: :unprocessable_entity
109
- end
110
- format.any do
111
- @errors = @interaction.errors
112
- render "errors", status: :unprocessable_entity
113
- end
114
-
115
- if helpers.current_turbo_frame == "modal"
116
- format.turbo_stream do
117
- render turbo_stream: turbo_stream.replace(:modal, partial: "interactive_resource_collection_action_form")
109
+ end
110
+ else
111
+ outcome.to_response.process(self) do
112
+ respond_to do |format|
113
+ format.html do
114
+ render :interactive_record_action, status: :unprocessable_entity
115
+ end
116
+ format.any do
117
+ @errors = @interaction.errors
118
+ render "errors", status: :unprocessable_entity
119
+ end
120
+ if helpers.current_turbo_frame == "modal"
121
+ format.turbo_stream do
122
+ render turbo_stream: [
123
+ turbo_stream.replace(:modal, partial: "interactive_action_form")
124
+ ]
125
+ end
118
126
  end
119
127
  end
120
128
  end
121
129
  end
122
130
  end
123
131
 
124
- # GET /resources/actions/:interactive_action
125
- def begin_interactive_resource_recordless_action
126
- # skip_policy_scope
127
-
128
- @interaction = current_interactive_action.interaction.new interaction_params
129
-
130
- if helpers.current_turbo_frame == "modal"
131
- render layout: false
132
- else
133
- render :interactive_resource_recordless_action
134
- end
132
+ # GET /resources/bulk_actions/:interactive_action?ids[]=1&ids[]=2
133
+ def interactive_bulk_action
134
+ raise NotImplementedError
135
+ # # TODO: ensure that the selected list matches the returned value
136
+ # interactive_bulk
137
+ # @interaction = current_interactive_action.interaction.new((params[:interaction] || {}).except(:resources))
138
+
139
+ # if helpers.current_turbo_frame == "modal"
140
+ # render layout: false
141
+ # else
142
+ # render :interactive_bulk_action
143
+ # end
135
144
  end
136
145
 
137
- # POST /resources/actions/:interactive_action
138
- def commit_interactive_resource_recordless_action
139
- # skip_policy_scope
140
-
141
- respond_to do |format|
142
- inputs = interaction_params
143
- @interaction = current_interactive_action.interaction.run(inputs)
144
-
145
- if @interaction.valid?
146
- flash[:notice] = "TODO:#{current_interactive_action} was successfully updated."
147
-
148
- format.html { redirect_to resource_url_for(resource_class) }
149
-
150
- if helpers.current_turbo_frame == "modal"
151
- format.turbo_stream do
152
- render turbo_stream: [
153
- turbo_stream.redirect(resource_url_for(resource_class))
154
- ]
155
- end
156
- end
157
- else
158
- format.html do
159
- render :interactive_resource_recordless_action, status: :unprocessable_entity
160
- end
161
- format.any do
162
- @errors = @interaction.errors
163
- render "errors", status: :unprocessable_entity
164
- end
165
-
166
- if helpers.current_turbo_frame == "modal"
167
- format.turbo_stream do
168
- render turbo_stream: turbo_stream.replace(:modal, partial: "interactive_resource_recordless_action_form")
169
- end
170
- end
171
- end
172
- end
146
+ # POST /resources/bulk_actions/:interactive_action?ids[]=1&ids[]=2
147
+ def commit_interactive_bulk_action
148
+ raise NotImplementedError
149
+ # respond_to do |format|
150
+ # inputs = interaction_params.merge(resources: interactive_bulk)
151
+ # @interaction = current_interactive_action.interaction.run(inputs)
152
+
153
+ # if @interaction.valid?
154
+ # collection_count = interactive_bulk.size
155
+
156
+ # flash[:notice] = "TODO:#{current_interactive_action} #{collection_count} #{resource_class.model_name.human.pluralize(collection_count)} successfully updated."
157
+
158
+ # format.html { redirect_to resource_url_for(resource_class) }
159
+ # if helpers.current_turbo_frame == "modal"
160
+ # format.turbo_stream do
161
+ # render turbo_stream: [
162
+ # turbo_stream.redirect(resource_url_for(resource_class))
163
+ # ]
164
+ # end
165
+ # end
166
+ # else
167
+ # format.html do
168
+ # render :interactive_bulk_action, status: :unprocessable_entity
169
+ # end
170
+ # format.any do
171
+ # @errors = @interaction.errors
172
+ # render "errors", status: :unprocessable_entity
173
+ # end
174
+
175
+ # if helpers.current_turbo_frame == "modal"
176
+ # format.turbo_stream do
177
+ # render turbo_stream: turbo_stream.replace(:modal, partial: "interactive_bulk_action_form")
178
+ # end
179
+ # end
180
+ # end
181
+ # end
173
182
  end
174
183
 
175
184
  private
@@ -179,17 +188,19 @@ module Plutonium
179
188
  end
180
189
 
181
190
  def interactive_resource_actions
182
- @interactive_resource_actions ||= current_presenter.actions.except :new, :show, :edit, :destroy
191
+ @interactive_resource_actions ||= current_definition
192
+ .defined_actions
193
+ .select { |k, v| v.is_a?(Plutonium::Action::Interactive) }
183
194
  end
184
195
 
185
- def validate_interactive_resource_action!
196
+ def validate_interactive_action!
186
197
  interactive_resource_action = params[:interactive_action]&.to_sym
187
198
  unless interactive_resource_actions.key?(interactive_resource_action)
188
199
  raise ::AbstractController::ActionNotFound, "Unknown action '#{interactive_resource_action}'"
189
200
  end
190
201
  end
191
202
 
192
- def authorize_interactive_resource_record_action!
203
+ def authorize_interactive_record_action!
193
204
  interactive_resource_action = params[:interactive_action]&.to_sym
194
205
  authorize_current! resource_record, to: :"#{interactive_resource_action}?"
195
206
  end
@@ -199,13 +210,43 @@ module Plutonium
199
210
  authorize_current! resource_class, to: :"#{interactive_resource_action}?"
200
211
  end
201
212
 
202
- def interactive_resource_collection
203
- @interactive_resource_collection ||= current_authorized_scope.from_path_param(params.require(:ids))
213
+ def interactive_bulk
214
+ @interactive_bulk ||= current_authorized_scope.from_path_param(params.require(:ids))
215
+ end
216
+
217
+ def build_interactive_record_action_interaction
218
+ @interaction = current_interactive_action.interaction.new
219
+ @interaction.attributes = interaction_params.merge(resource: resource_record)
220
+ @interaction
221
+ end
222
+
223
+ def build_interactive_resource_action_interaction
224
+ @interaction = current_interactive_action.interaction.new
225
+ @interaction.attributes = interaction_params
226
+ @interaction
227
+ end
228
+
229
+ # Returns the submitted resource parameters
230
+ # @return [Hash] The submitted resource parameters
231
+ def submitted_interaction_params
232
+ @submitted_interaction_params ||= current_interactive_action
233
+ .interaction
234
+ .build_form(nil)
235
+ .extract_input(params)[:interaction]
236
+ end
237
+
238
+ def redirect_url_after_action_on(resource_record_or_resource_class)
239
+ if (return_to = url_from(params[:return_to]))
240
+ return return_to
241
+ end
242
+
243
+ resource_url_for(resource_record_or_resource_class)
204
244
  end
205
245
 
246
+ # Returns the resource parameters, including scoped and parent parameters
247
+ # @return [Hash] The resource parameters
206
248
  def interaction_params
207
- # active interaction handles filtering so no need for strong params
208
- (params[:interaction] || {}).except(:resource, :resources)
249
+ @interaction_params ||= submitted_interaction_params.except(:resource, :resources)
209
250
  end
210
251
  end
211
252
  end
@@ -24,11 +24,11 @@ module Plutonium
24
24
  end
25
25
 
26
26
  current_definition.defined_scopes.each do |key, value|
27
- query_object.define_scope key, value[:block]
27
+ query_object.define_scope key, value[:block], **value[:options]
28
28
  end
29
29
 
30
30
  current_definition.defined_sorts.each do |key, value|
31
- query_object.define_sorter key, value[:block]
31
+ query_object.define_sorter key, value[:block], **value[:options]
32
32
  end
33
33
 
34
34
  query_object
@@ -1,8 +1,6 @@
1
- require "active_interaction"
2
-
3
1
  module Plutonium
4
2
  module Resource
5
- class Interaction < ActiveInteraction::Base
3
+ class Interaction < Plutonium::Interaction::Base
6
4
  end
7
5
  end
8
6
  end
@@ -138,9 +138,9 @@ module Plutonium
138
138
  #
139
139
  # @param name [Symbol] The name of the sort.
140
140
  # @param body [Proc, nil] The body of the sort.
141
- def define_sorter(name, body = nil)
141
+ def define_sorter(name, body = nil, using: nil)
142
142
  if body.nil?
143
- sort_field = determine_sort_field(name)
143
+ sort_field = using || determine_sort_field(name)
144
144
  body = ->(scope, direction:) { scope.order(sort_field => direction) }
145
145
  end
146
146
 
@@ -90,9 +90,9 @@ module Plutonium
90
90
  # @return [void]
91
91
  def define_member_interactive_actions
92
92
  member do
93
- get "record_actions/:interactive_action", action: :begin_interactive_resource_record_action,
94
- as: :interactive_resource_record_action
95
- post "record_actions/:interactive_action", action: :commit_interactive_resource_record_action
93
+ get "record_actions/:interactive_action", action: :interactive_record_action,
94
+ as: :record_action
95
+ post "record_actions/:interactive_action", action: :commit_interactive_record_action
96
96
  end
97
97
  end
98
98
 
@@ -101,13 +101,13 @@ module Plutonium
101
101
  # @return [void]
102
102
  def define_collection_interactive_actions
103
103
  collection do
104
- get "collection_actions/:interactive_action", action: :begin_interactive_resource_collection_action,
105
- as: :interactive_resource_collection_action
106
- post "collection_actions/:interactive_action", action: :commit_interactive_resource_collection_action
104
+ get "bulk_actions/:interactive_action", action: :interactive_bulk_action,
105
+ as: :bulk_action
106
+ post "bulk_actions/:interactive_action", action: :commit_interactive_bulk_action
107
107
 
108
- get "recordless_actions/:interactive_action", action: :begin_interactive_resource_recordless_action,
109
- as: :interactive_resource_recordless_action
110
- post "recordless_actions/:interactive_action", action: :commit_interactive_resource_recordless_action
108
+ get "resource_actions/:interactive_action", action: :interactive_resource_action,
109
+ as: :resource_action
110
+ post "resource_actions/:interactive_action", action: :commit_interactive_resource_action
111
111
  end
112
112
  end
113
113
 
@@ -40,6 +40,7 @@ module Plutonium
40
40
  class: "inline-block",
41
41
  form: {
42
42
  data: {
43
+ turbo: @action.turbo,
43
44
  turbo_confirm: @action.confirmation,
44
45
  turbo_frame: @action.turbo_frame
45
46
  }
@@ -55,7 +56,7 @@ module Plutonium
55
56
  if @action.icon
56
57
  render @action.icon.new(class: icon_classes)
57
58
  end
58
- span { @action.label } unless @variant == :table
59
+ span { @action.label }
59
60
  end
60
61
 
61
62
  def button_classes
@@ -69,7 +70,7 @@ module Plutonium
69
70
 
70
71
  def base_classes
71
72
  if @variant == :table
72
- "inline-flex items-center justify-center p-1 rounded-lg focus:outline-none focus:ring-2"
73
+ "inline-flex items-center justify-center py-1 px-2 rounded-lg focus:outline-none focus:ring-2"
73
74
  else
74
75
  "flex items-center justify-center px-4 py-2 text-sm font-medium rounded-lg focus:outline-none focus:ring-4"
75
76
  end
@@ -77,7 +78,7 @@ module Plutonium
77
78
 
78
79
  def icon_classes
79
80
  if @variant == :table
80
- "h-4 w-4"
81
+ "h-4 w-4 mr-1"
81
82
  else
82
83
  "h-3.5 w-3.5 mr-2 -ml-1"
83
84
  end
@@ -44,6 +44,7 @@ module Plutonium
44
44
  :resource_query_params,
45
45
  :current_policy,
46
46
  :current_turbo_frame,
47
+ :current_interactive_action,
47
48
  :policy_for,
48
49
  :allowed_to?,
49
50
  :registered_resources,
@@ -7,17 +7,18 @@ module Plutonium
7
7
  def self.theme
8
8
  super.merge({
9
9
  base: "relative bg-white dark:bg-gray-800 shadow-md sm:rounded-lg my-3",
10
+ value_wrapper: "max-h-[300px] overflow-y-auto",
10
11
  fields_wrapper: "p-6 grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-6 gap-y-10 grid-flow-row-dense",
11
12
  label: "text-base font-bold text-gray-500 dark:text-gray-400 mb-1",
12
13
  description: "text-sm text-gray-400 dark:text-gray-500",
13
14
  placeholder: "text-md text-gray-500 dark:text-gray-300 mb-1 italic",
14
- string: "max-h-[300px] overflow-y-auto text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
15
- text: "max-h-[300px] overflow-y-auto text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
15
+ string: "text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
16
+ text: "text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
16
17
  link: "text-primary-600 dark:text-primary-500 whitespace-pre-line",
17
18
  color: "flex items-center text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
18
19
  color_indicator: "w-10 h-10 rounded-full mr-2", # max-h-fit
19
20
  email: "flex items-center text-md text-primary-600 dark:text-primary-500 mb-1 whitespace-pre-line",
20
- json: "max-h-[300px] overflow-y-auto text-sm text-gray-900 dark:text-white mb-1 whitespace-pre font-mono shadow-inner p-4",
21
+ json: "text-sm text-gray-900 dark:text-white mb-1 whitespace-pre font-mono shadow-inner p-4",
21
22
  prefixed_icon: "w-8 h-8 mr-2"
22
23
  })
23
24
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Form
6
+ class Interaction < Resource
7
+ def initialize(interaction, *, **options, &)
8
+ options[:key] = :interaction
9
+ options[:resource_fields] = interaction.attribute_names.map(&:to_sym) - %i[resource resources]
10
+ options[:resource_definition] = interaction
11
+
12
+ super
13
+ end
14
+
15
+ private
16
+
17
+ def form_action
18
+ # interactive action forms post to the same page
19
+ nil
20
+ end
21
+
22
+ def initialize_attributes
23
+ super
24
+ attributes.fetch(:data_turbo) { attributes[:data_turbo] = object.turbo.to_s }
25
+ end
26
+
27
+ def submit_button(*, **)
28
+ super(*, **) do
29
+ object.label
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -65,7 +65,7 @@ module Plutonium
65
65
  end
66
66
 
67
67
  def when_permitted(name, &)
68
- return unless @resource_fields.include? name
68
+ return unless resource_fields.include? name
69
69
 
70
70
  yield
71
71
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Page
6
+ class InteractiveAction < Base
7
+ private
8
+
9
+ def page_title
10
+ current_interactive_action.label || super
11
+ end
12
+
13
+ def page_description
14
+ current_interactive_action.description || super
15
+ end
16
+
17
+ def render_default_content
18
+ render "interactive_action_form"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end