plutonium 0.14.1 → 0.15.0.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/README copy.md +1 -1
  3. data/README.md +1 -1
  4. data/app/assets/plutonium.css +1 -1
  5. data/app/views/{application → plutonium}/_resource_header.html copy.erb +1 -1
  6. data/app/views/{application → plutonium}/_resource_header.html.erb +1 -1
  7. data/app/views/{application → plutonium}/_resource_sidebar.html.erb +2 -0
  8. data/app/views/resource/_resource_details.html.erb +1 -36
  9. data/app/views/resource/_resource_form.html.erb +1 -5
  10. data/app/views/resource/_resource_table.html.erb +315 -85
  11. data/app/views/resource/edit.html.erb +1 -5
  12. data/app/views/resource/index.html.erb +1 -5
  13. data/app/views/resource/new.html.erb +1 -5
  14. data/app/views/resource/show.html.erb +1 -5
  15. data/config/initializers/pagy.rb +1 -0
  16. data/config/initializers/rabl.rb +27 -20
  17. data/gemfiles/rails_7.gemfile.lock +5 -1
  18. data/lib/generators/pu/core/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
  19. data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
  20. data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
  21. data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
  22. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +1 -8
  23. data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
  24. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +1 -1
  25. data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
  26. data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
  27. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
  28. data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
  29. data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
  30. data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
  31. data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
  32. data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
  33. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
  34. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
  35. data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
  36. data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
  37. data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
  38. data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
  39. data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
  40. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
  41. data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
  42. data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
  43. data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
  44. data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
  45. data/lib/generators/pu/res/model/model_generator.rb +0 -7
  46. data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
  47. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
  48. data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
  49. data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
  50. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
  51. data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
  52. data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
  53. data/lib/plutonium/action/README.md +0 -0
  54. data/lib/plutonium/action/base.rb +103 -0
  55. data/lib/plutonium/action/interactive.rb +117 -0
  56. data/lib/plutonium/action/route_options.rb +65 -0
  57. data/lib/plutonium/action/simple.rb +8 -0
  58. data/lib/plutonium/auth.rb +1 -1
  59. data/lib/plutonium/configuration.rb +0 -8
  60. data/lib/plutonium/core/actions/collection.rb +1 -1
  61. data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
  62. data/lib/plutonium/core/controller.rb +110 -0
  63. data/lib/plutonium/core/controllers/authorizable.rb +12 -35
  64. data/lib/plutonium/core/controllers/bootable.rb +38 -7
  65. data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
  66. data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
  67. data/lib/plutonium/core/ui/collection.rb +1 -1
  68. data/lib/plutonium/core/ui/detail.rb +1 -1
  69. data/lib/plutonium/core/ui/form.rb +1 -1
  70. data/lib/plutonium/definition/actions.rb +50 -0
  71. data/lib/plutonium/definition/base.rb +92 -0
  72. data/lib/plutonium/definition/config_attr.rb +30 -0
  73. data/lib/plutonium/definition/defineable_props.rb +96 -0
  74. data/lib/plutonium/definition/search.rb +21 -0
  75. data/lib/plutonium/engine/validator.rb +30 -0
  76. data/lib/plutonium/engine.rb +25 -0
  77. data/lib/plutonium/helpers/form_helper.rb +1 -3
  78. data/lib/plutonium/interaction/README.md +369 -0
  79. data/lib/plutonium/interaction/base.rb +75 -0
  80. data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
  81. data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
  82. data/lib/plutonium/interaction/outcome.rb +129 -0
  83. data/lib/plutonium/interaction/response/base.rb +63 -0
  84. data/lib/plutonium/interaction/response/null.rb +33 -0
  85. data/lib/plutonium/interaction/response/redirect.rb +30 -0
  86. data/lib/plutonium/interaction/response/render.rb +28 -0
  87. data/lib/plutonium/lib/bit_flags.rb +70 -9
  88. data/lib/plutonium/{config → lib}/overlayed_hash.rb +1 -1
  89. data/lib/plutonium/lib/smart_cache.rb +171 -0
  90. data/lib/plutonium/models/has_cents.rb +170 -0
  91. data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
  92. data/lib/plutonium/{application → portal}/controller.rb +3 -11
  93. data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
  94. data/lib/plutonium/portal/engine.rb +15 -0
  95. data/lib/plutonium/railtie.rb +33 -1
  96. data/lib/plutonium/reloader.rb +5 -5
  97. data/lib/plutonium/resource/controller.rb +51 -34
  98. data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
  99. data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
  100. data/lib/plutonium/resource/controllers/defineable.rb +26 -0
  101. data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
  102. data/lib/plutonium/resource/controllers/presentable.rb +41 -0
  103. data/lib/plutonium/resource/controllers/queryable.rb +44 -0
  104. data/lib/plutonium/resource/definition.rb +6 -0
  105. data/lib/plutonium/resource/policy.rb +25 -13
  106. data/lib/plutonium/resource/query_object.rb +50 -51
  107. data/lib/plutonium/resource/record.rb +6 -89
  108. data/lib/plutonium/resource/register.rb +82 -0
  109. data/lib/plutonium/routing/mapper_extensions.rb +1 -1
  110. data/lib/plutonium/routing/resource_registration.rb +1 -1
  111. data/lib/plutonium/routing/route_set_extensions.rb +6 -18
  112. data/lib/plutonium/ui/action_button.rb +125 -0
  113. data/lib/plutonium/ui/breadcrumbs.rb +163 -0
  114. data/lib/plutonium/ui/component/base.rb +13 -0
  115. data/lib/plutonium/ui/component/behaviour.rb +38 -0
  116. data/lib/plutonium/ui/component/kit.rb +31 -0
  117. data/lib/plutonium/ui/component/methods.rb +54 -0
  118. data/lib/plutonium/ui/display/base.rb +25 -0
  119. data/lib/plutonium/ui/display/component/association.rb +26 -0
  120. data/lib/plutonium/ui/display/resource.rb +77 -0
  121. data/lib/plutonium/ui/display/theme.rb +27 -0
  122. data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
  123. data/lib/plutonium/ui/empty_card.rb +20 -0
  124. data/lib/plutonium/ui/form/base.rb +37 -0
  125. data/lib/plutonium/ui/form/resource.rb +75 -0
  126. data/lib/plutonium/ui/form/theme.rb +42 -0
  127. data/lib/plutonium/ui/page/base.rb +112 -0
  128. data/lib/plutonium/ui/page/edit.rb +23 -0
  129. data/lib/plutonium/ui/page/index.rb +27 -0
  130. data/lib/plutonium/ui/page/new.rb +23 -0
  131. data/lib/plutonium/ui/page/show.rb +27 -0
  132. data/lib/plutonium/ui/page_header.rb +49 -0
  133. data/lib/plutonium/ui/table/base.rb +13 -0
  134. data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
  135. data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
  136. data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
  137. data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
  138. data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
  139. data/lib/plutonium/ui/table/display_theme.rb +21 -0
  140. data/lib/plutonium/ui/table/resource.rb +98 -0
  141. data/lib/plutonium/ui/table/theme.rb +35 -0
  142. data/lib/plutonium/ui.rb +9 -0
  143. data/lib/plutonium/version.rb +5 -1
  144. data/lib/plutonium.rb +14 -1
  145. data/package-lock.json +19 -22
  146. data/package.json +4 -4
  147. data/src/css/plutonium.css +15 -0
  148. data/tailwind.options.js +11 -3
  149. metadata +218 -81
  150. data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
  151. data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
  152. data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
  153. data/lib/plutonium/concerns/resource_validatable.rb +0 -34
  154. data/lib/plutonium/config.rb +0 -9
  155. data/lib/plutonium/core/controllers/base.rb +0 -101
  156. data/lib/plutonium/core/controllers/presentable.rb +0 -65
  157. data/lib/plutonium/core/controllers/queryable.rb +0 -28
  158. data/lib/plutonium/pkg/app.rb +0 -35
  159. data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
  160. data/lib/plutonium/pkg/feature.rb +0 -18
  161. data/lib/plutonium/policy/initializer.rb +0 -22
  162. data/lib/plutonium/policy/scope.rb +0 -19
  163. data/lib/plutonium/pundit/context.rb +0 -18
  164. data/lib/plutonium/pundit/policy_finder.rb +0 -25
  165. data/lib/plutonium/resource/policy_context.rb +0 -5
  166. data/lib/plutonium/resource_register.rb +0 -83
  167. data/lib/plutonium/smart_cache.rb +0 -151
  168. /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
  169. /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
  170. /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
  171. /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
  172. /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
  173. /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
  174. /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
  175. /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  176. /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
  177. /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
  178. /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  179. /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
  180. /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
  181. /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
  182. /data/lib/generators/pu/pkg/{app → portal}/templates/lib/engine.rb.tt +0 -0
@@ -2,6 +2,7 @@ module Plutonium
2
2
  module Resource
3
3
  module Record
4
4
  extend ActiveSupport::Concern
5
+ include Plutonium::Models::HasCents
5
6
 
6
7
  included do
7
8
  scope :from_path_param, ->(param) { where(id: param) }
@@ -14,12 +15,12 @@ module Plutonium
14
15
  # TODO: add logging
15
16
  # TODO: memoize this
16
17
 
17
- own_association = klass.find_association_to_record(record)
18
+ own_association = klass.find_association_from_self_to_record(record)
18
19
  if own_association
19
20
  return klass.query_based_on_association(own_association, record)
20
21
  end
21
22
 
22
- record_association = klass.find_association_to_self(record)
23
+ record_association = klass.find_association_to_self_from_record(record)
23
24
  if record_association
24
25
  # TODO: add a warning here about a potentially poor performing query
25
26
  return where(id: record.public_send(record_association.name))
@@ -43,7 +44,7 @@ module Plutonium
43
44
  # @param [Proc] scope The scope for the association
44
45
  # @param [Hash] options The options for the association
45
46
  def belongs_to(name, scope = nil, **options)
46
- super(name, scope, **options)
47
+ super
47
48
 
48
49
  return unless options[:polymorphic]
49
50
 
@@ -155,31 +156,10 @@ module Plutonium
155
156
  end.compact.to_h
156
157
  end
157
158
 
158
- # Returns the strong parameters definition for the given attribute names
159
- # @param [Array<Symbol>] *attributes Attribute names
160
- # @return [Array<Symbol, Hash>] A strong parameters compatible array
161
- def strong_parameters_for(*attributes)
162
- unbacked = attributes - strong_parameters_definition.keys
163
-
164
- backed = strong_parameters_definition.slice(*attributes).values.reduce(:merge)
165
- &.map { |key, value| value.nil? ? key : {key => value} } || {}
166
-
167
- case backed.presence
168
- when Hash
169
- [*unbacked, **backed]
170
- when Array
171
- [*unbacked, *backed]
172
- when nil
173
- unbacked
174
- else
175
- raise "Unexpected strong parameters definition: #{backed.class}"
176
- end
177
- end
178
-
179
159
  # Finds the association to the given record
180
160
  # @param [ActiveRecord::Base] record The record to find the association with
181
161
  # @return [ActiveRecord::Reflection::AssociationReflection, nil]
182
- def find_association_to_record(record)
162
+ def find_association_from_self_to_record(record)
183
163
  reflect_on_all_associations.find do |assoc|
184
164
  assoc.klass.name == record.class.name unless assoc.polymorphic?
185
165
  rescue
@@ -191,7 +171,7 @@ module Plutonium
191
171
  # Finds the association to self in the given record
192
172
  # @param [ActiveRecord::Base] record The record to find the association with
193
173
  # @return [ActiveRecord::Reflection::AssociationReflection, nil]
194
- def find_association_to_self(record)
174
+ def find_association_to_self_from_record(record)
195
175
  record.class.reflect_on_all_associations.find do |assoc|
196
176
  assoc.klass.name == name
197
177
  rescue
@@ -231,69 +211,6 @@ module Plutonium
231
211
 
232
212
  private
233
213
 
234
- # Defines the strong parameters
235
- # @return [Hash]
236
- def strong_parameters_definition
237
- unless Rails.env.local?
238
- return @strong_parameters if defined?(@strong_parameters)
239
- end
240
-
241
- @strong_parameters = build_strong_parameters
242
- end
243
-
244
- # Builds the strong parameters hash
245
- # @return [Hash]
246
- def build_strong_parameters
247
- parameters = content_column_field_names.map do |name|
248
- column = columns_hash[name.to_s]
249
- type = if column.try(:array?)
250
- []
251
- else
252
- ([:json, :jsonb].include?(column&.type) ? {} : nil)
253
- end
254
- [name, {name => type}]
255
- end.to_h
256
-
257
- parameters.merge!(belongs_to_association_parameters)
258
- parameters.merge!(has_many_association_parameters)
259
- parameters.merge!(attachment_parameters)
260
- parameters.merge!(nested_attributes_parameters)
261
-
262
- parameters
263
- end
264
-
265
- # Returns the parameters for belongs_to associations
266
- # @return [Hash]
267
- def belongs_to_association_parameters
268
- reflect_on_all_associations(:belongs_to).map do |reflection|
269
- input_param = reflection.options[:foreign_key] || :"#{reflection.name}_id"
270
- [reflection.name, {input_param => nil, :"#{reflection.name}_sgid" => nil}]
271
- end.to_h
272
- end
273
-
274
- # Returns the parameters for has_many associations
275
- # @return [Hash]
276
- def has_many_association_parameters
277
- has_many_association_field_names.map do |name|
278
- [name, {"#{name.to_s.singularize}_ids": []}]
279
- end.to_h
280
- end
281
-
282
- # Returns the parameters for attachments
283
- # @return [Hash]
284
- def attachment_parameters
285
- has_many_attached_field_names.map { |name| [name, {name => []}] }.to_h
286
- .merge(has_one_attached_field_names.map { |name| [name, {name => nil}] }.to_h)
287
- end
288
-
289
- # Returns the parameters for nested attributes
290
- # @return [Hash]
291
- def nested_attributes_parameters
292
- all_nested_attributes_options.keys.map do |name|
293
- [name, {"#{name}_attributes" => {}}]
294
- end.to_h
295
- end
296
-
297
214
  # Defines a scope and method for path parameters
298
215
  # @param [Symbol] param_name The name of the parameter
299
216
  def path_parameter(param_name)
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module Resource
5
+ # Resource register manages the registration and lookup of resources.
6
+ class Register
7
+ include Plutonium::Lib::SmartCache
8
+
9
+ # Custom error class for frozen register operations
10
+ class FrozenRegisterError < StandardError; end
11
+
12
+ def initialize
13
+ @resources = Set.new
14
+ @frozen = false
15
+ end
16
+
17
+ # Registers a new resource with the register.
18
+ #
19
+ # @param resource [Class] The resource class to be registered.
20
+ # @raise [FrozenRegisterError] If the register is frozen.
21
+ # @return [void]
22
+ def register(resource)
23
+ raise FrozenRegisterError, "Cannot modify frozen resource register" if @frozen
24
+
25
+ @resources.add(resource.to_s)
26
+ end
27
+
28
+ # Returns an array of all registered resource classes and freezes the register.
29
+ #
30
+ # @return [Array<Class>] An array of registered resource classes.
31
+ def resources
32
+ freeze
33
+ @resources.map(&:constantize)
34
+ end
35
+ memoize_unless_reloading :resources
36
+
37
+ # Returns a hash mapping route keys to their corresponding resource classes.
38
+ # This method will freeze the register if it hasn't been frozen already.
39
+ #
40
+ # @return [Hash{Symbol => Class}] A hash where keys are route keys and values are resource classes.
41
+ def route_key_lookup
42
+ freeze
43
+ resources.to_h do |resource|
44
+ [resource.model_name.singular_route_key.to_sym, resource]
45
+ end
46
+ end
47
+ memoize_unless_reloading :route_key_lookup
48
+
49
+ # Clears all registered resources and invalidates the cache.
50
+ #
51
+ # @return [void]
52
+ def clear
53
+ @resources.clear
54
+ @frozen = false
55
+ invalidate_cache
56
+ end
57
+
58
+ # Checks if the register is frozen.
59
+ #
60
+ # @return [Boolean] True if the register is frozen, false otherwise.
61
+ def frozen?
62
+ @frozen
63
+ end
64
+
65
+ private
66
+
67
+ # Freezes the register
68
+ #
69
+ # @return [Boolean] Always returns true
70
+ def freeze
71
+ @frozen ||= true
72
+ end
73
+
74
+ # Invalidates the memoization cache
75
+ #
76
+ # @return [void]
77
+ def invalidate_cache
78
+ flush_smart_cache
79
+ end
80
+ end
81
+ end
82
+ end
@@ -79,7 +79,7 @@ module Plutonium
79
79
  def define_nested_resource_routes(resource)
80
80
  nested_configs = route_set.resource_route_config_for(*resource.has_many_association_routes)
81
81
  nested_configs.each do |nested_config|
82
- resources nested_config[:route_name], **nested_config[:route_options] do
82
+ resources "nested_#{nested_config[:route_name]}", **nested_config[:route_options] do
83
83
  instance_exec(&nested_config[:block]) if nested_config[:block]
84
84
  end
85
85
  end
@@ -8,7 +8,7 @@ module Plutonium
8
8
 
9
9
  class_methods do
10
10
  def resource_register
11
- @resource_register ||= Plutonium::ResourceRegister.new
11
+ @resource_register ||= Plutonium::Resource::Register.new
12
12
  end
13
13
  end
14
14
  end
@@ -12,6 +12,9 @@ module Plutonium
12
12
  # register_resource SomeModel
13
13
  # end
14
14
  module RouteSetExtensions
15
+ extend ActiveSupport::Concern
16
+ include Plutonium::Engine::Validator
17
+
15
18
  # Clears all registered resources and route configurations.
16
19
  #
17
20
  # This method should be called when you want to reset all registered resources
@@ -30,7 +33,7 @@ module Plutonium
30
33
  # @return [void]
31
34
  # @yield Executes the given block in the context of route drawing.
32
35
  def draw(&block)
33
- if supported_engine?
36
+ if self.class.supported_engine?(engine)
34
37
  ActiveSupport::Notifications.instrument("plutonium.resource_routes.draw", app: engine.to_s) do
35
38
  super do
36
39
  setup_shared_resource_concerns
@@ -48,9 +51,9 @@ module Plutonium
48
51
  # @param resource [Class] The resource class to be registered.
49
52
  # @yield An optional block for additional resource configuration.
50
53
  # @return [Hash] The configuration for the registered resource.
51
- # @raise [ArgumentError] If the engine doesn't support Plutonium::Pkg::App.
54
+ # @raise [ArgumentError] If the engine is not supported.
52
55
  def register_resource(resource, &)
53
- validate_engine!
56
+ self.class.validate_engine! engine
54
57
  engine.resource_register.register(resource)
55
58
 
56
59
  route_name = resource.model_name.plural
@@ -85,21 +88,6 @@ module Plutonium
85
88
  @resource_route_config_lookup ||= {}
86
89
  end
87
90
 
88
- # Validates that the current engine supports Plutonium features.
89
- #
90
- # @raise [ArgumentError] If the engine doesn't include Plutonium::Pkg::App.
91
- # @return [void]
92
- def validate_engine!
93
- raise ArgumentError, "#{engine} must include Plutonium::Pkg::App to register resources" unless supported_engine?
94
- end
95
-
96
- # Checks if the current engine supports Plutonium features.
97
- #
98
- # @return [Boolean] True if the engine includes Plutonium::Pkg::App, false otherwise.
99
- def supported_engine?
100
- engine.include?(Plutonium::Pkg::App)
101
- end
102
-
103
91
  # Determines the appropriate engine based on the current scope.
104
92
  #
105
93
  # @return [Class] The determined engine class.
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ class ActionButton < Plutonium::UI::Component::Base
6
+ include Phlex::Rails::Helpers::LinkTo
7
+ include Phlex::Rails::Helpers::ButtonTo
8
+
9
+ def initialize(action, url:, variant: :default)
10
+ @action = action
11
+ @url = url
12
+ @variant = variant
13
+ end
14
+
15
+ def view_template
16
+ if @action.route_options.method == :get
17
+ render_link
18
+ else
19
+ render_button
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def render_link
26
+ link_to(
27
+ @url,
28
+ class: button_classes,
29
+ data: {turbo_frame: @action.turbo_frame}
30
+ ) do
31
+ render_button_content
32
+ end
33
+ end
34
+
35
+ def render_button
36
+ button_to(
37
+ @url,
38
+ method: @action.route_options.method,
39
+ name: :return_to, value: request.original_url,
40
+ class: "inline-block",
41
+ form: {
42
+ data: {
43
+ turbo_confirm: @action.confirmation,
44
+ turbo_frame: @action.turbo_frame
45
+ }
46
+ }
47
+ ) do
48
+ span(class: button_classes) do
49
+ render_button_content
50
+ end
51
+ end
52
+ end
53
+
54
+ def render_button_content
55
+ if @action.icon
56
+ render @action.icon.new(class: icon_classes)
57
+ end
58
+ span { @action.label } unless @variant == :table
59
+ end
60
+
61
+ def button_classes
62
+ tokens(
63
+ base_classes,
64
+ color_classes,
65
+ size_classes,
66
+ -> { @action.icon && @variant != :table } => "space-x-1"
67
+ )
68
+ end
69
+
70
+ def base_classes
71
+ if @variant == :table
72
+ "inline-flex items-center justify-center p-1 rounded-lg focus:outline-none focus:ring-2"
73
+ else
74
+ "flex items-center justify-center px-4 py-2 text-sm font-medium rounded-lg focus:outline-none focus:ring-4"
75
+ end
76
+ end
77
+
78
+ def icon_classes
79
+ if @variant == :table
80
+ "h-4 w-4"
81
+ else
82
+ "h-3.5 w-3.5 mr-2 -ml-1"
83
+ end
84
+ end
85
+
86
+ def size_classes
87
+ (@variant == :table) ? "text-xs" : "text-sm"
88
+ end
89
+
90
+ def color_classes
91
+ case @action.color || @action.category.to_sym
92
+ when :primary
93
+ table_variant_class(
94
+ "bg-primary-100 text-primary-700 hover:bg-primary-200 focus:ring-primary-300 dark:bg-primary-700 dark:text-primary-100 dark:hover:bg-primary-600 dark:focus:ring-primary-600",
95
+ "bg-primary-700 text-white hover:bg-primary-800 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
96
+ )
97
+ when :warning
98
+ table_variant_class(
99
+ "bg-yellow-100 text-yellow-700 hover:bg-yellow-200 focus:ring-yellow-300 dark:bg-yellow-700 dark:text-yellow-100 dark:hover:bg-yellow-600 dark:focus:ring-yellow-600",
100
+ "bg-yellow-400 text-white hover:bg-yellow-500 focus:ring-yellow-300 dark:bg-yellow-600 dark:hover:bg-yellow-700 dark:focus:ring-yellow-800"
101
+ )
102
+ when :danger
103
+ table_variant_class(
104
+ "bg-red-100 text-red-700 hover:bg-red-200 focus:ring-red-300 dark:bg-red-700 dark:text-red-100 dark:hover:bg-red-600 dark:focus:ring-red-600",
105
+ "bg-red-700 text-white hover:bg-red-800 focus:ring-red-300 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900"
106
+ )
107
+ when :success
108
+ table_variant_class(
109
+ "bg-green-100 text-green-700 hover:bg-green-200 focus:ring-green-300 dark:bg-green-700 dark:text-green-100 dark:hover:bg-green-600 dark:focus:ring-green-600",
110
+ "bg-green-700 text-white hover:bg-green-800 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800"
111
+ )
112
+ else
113
+ table_variant_class(
114
+ "bg-gray-100 text-gray-700 hover:bg-gray-200 focus:ring-gray-300 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600 dark:focus:ring-gray-600",
115
+ "border border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
116
+ )
117
+ end
118
+ end
119
+
120
+ def table_variant_class(table_class, default_class)
121
+ (@variant == :table) ? table_class : default_class
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,163 @@
1
+ module Plutonium
2
+ module UI
3
+ class Breadcrumbs < Plutonium::UI::Component::Base
4
+ include Phlex::Rails::Helpers::ActionName
5
+ include Phlex::Rails::Helpers::LinkTo
6
+
7
+ def view_template
8
+ nav(
9
+ class:
10
+ "flex py-3 text-gray-700 mb-2",
11
+ aria_label: "Breadcrumb"
12
+ ) do
13
+ ol(
14
+ class:
15
+ "inline-flex items-center space-x-1 md:space-x-2 rtl:space-x-reverse"
16
+ ) do
17
+ # Dashboard
18
+ li(class: "inline-flex items-center") do
19
+ a(
20
+ href: helpers.root_path,
21
+ class:
22
+ "inline-flex items-center text-sm font-medium text-gray-700 hover:text-primary-600 dark:text-gray-200 dark:hover:text-white"
23
+ ) do
24
+ svg(
25
+ class: "w-3 h-3 me-2.5",
26
+ aria_hidden: "true",
27
+ xmlns: "http://www.w3.org/2000/svg",
28
+ fill: "currentColor",
29
+ viewbox: "0 0 20 20"
30
+ ) do |s|
31
+ s.path(
32
+ d:
33
+ "m19.707 9.293-2-2-7-7a1 1 0 0 0-1.414 0l-7 7-2 2a1 1 0 0 0 1.414 1.414L2 10.414V18a2 2 0 0 0 2 2h3a1 1 0 0 0 1-1v-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 0 1 1h3a2 2 0 0 0 2-2v-7.586l.293.293a1 1 0 0 0 1.414-1.414Z"
34
+ )
35
+ end
36
+ plain " Dashboard "
37
+ end
38
+ end
39
+
40
+ # Parent
41
+ if current_parent.present?
42
+ # Parent Resource
43
+ li(class: "flex items-center") do
44
+ svg(
45
+ class: "rtl:rotate-180 block w-3 h-3 mx-1 text-gray-400",
46
+ aria_hidden: "true",
47
+ xmlns: "http://www.w3.org/2000/svg",
48
+ fill: "none",
49
+ viewbox: "0 0 6 10"
50
+ ) do |s|
51
+ s.path(
52
+ stroke: "currentColor",
53
+ stroke_linecap: "round",
54
+ stroke_linejoin: "round",
55
+ stroke_width: "2",
56
+ d: "m1 9 4-4-4-4"
57
+ )
58
+ end
59
+ link_to resource_name_plural(current_parent.class),
60
+ resource_url_for(current_parent.class, parent: nil),
61
+ class:
62
+ "ms-1 text-sm font-medium text-gray-700 hover:text-primary-600 md:ms-2 dark:text-gray-200 dark:hover:text-white"
63
+ end
64
+
65
+ # Parent Itself
66
+ li(class: "flex items-center") do
67
+ svg(
68
+ class: "rtl:rotate-180 block w-3 h-3 mx-1 text-gray-400",
69
+ aria_hidden: "true",
70
+ xmlns: "http://www.w3.org/2000/svg",
71
+ fill: "none",
72
+ viewbox: "0 0 6 10"
73
+ ) do |s|
74
+ s.path(
75
+ stroke: "currentColor",
76
+ stroke_linecap: "round",
77
+ stroke_linejoin: "round",
78
+ stroke_width: "2",
79
+ d: "m1 9 4-4-4-4"
80
+ )
81
+ end
82
+ link_to display_name_of(current_parent),
83
+ resource_url_for(current_parent, parent: nil),
84
+ class:
85
+ "ms-1 text-sm font-medium text-gray-700 hover:text-primary-600 md:ms-2 dark:text-gray-200 dark:hover:text-white"
86
+ end
87
+ end
88
+
89
+ # Record
90
+ if resource_record.present?
91
+ # Record Resource
92
+ li(class: "flex items-center") do
93
+ svg(
94
+ class: "rtl:rotate-180 block w-3 h-3 mx-1 text-gray-400",
95
+ aria_hidden: "true",
96
+ xmlns: "http://www.w3.org/2000/svg",
97
+ fill: "none",
98
+ viewbox: "0 0 6 10"
99
+ ) do |s|
100
+ s.path(
101
+ stroke: "currentColor",
102
+ stroke_linecap: "round",
103
+ stroke_linejoin: "round",
104
+ stroke_width: "2",
105
+ d: "m1 9 4-4-4-4"
106
+ )
107
+ end
108
+ link_to resource_name_plural(resource_class),
109
+ resource_url_for(resource_class),
110
+ class:
111
+ "ms-1 text-sm font-medium text-gray-700 hover:text-primary-600 md:ms-2 dark:text-gray-200 dark:hover:text-white"
112
+ end
113
+
114
+ # Record Itself
115
+ if resource_record.persisted? && action_name != "show"
116
+ li(class: "flex items-center") do
117
+ svg(
118
+ class: "rtl:rotate-180 block w-3 h-3 mx-1 text-gray-400",
119
+ aria_hidden: "true",
120
+ xmlns: "http://www.w3.org/2000/svg",
121
+ fill: "none",
122
+ viewbox: "0 0 6 10"
123
+ ) do |s|
124
+ s.path(
125
+ stroke: "currentColor",
126
+ stroke_linecap: "round",
127
+ stroke_linejoin: "round",
128
+ stroke_width: "2",
129
+ d: "m1 9 4-4-4-4"
130
+ )
131
+ end
132
+ link_to display_name_of(resource_record),
133
+ resource_url_for(resource_record),
134
+ class:
135
+ "ms-1 text-sm font-medium text-gray-700 hover:text-primary-600 md:ms-2 dark:text-gray-200 dark:hover:text-white"
136
+ end
137
+ end
138
+ end
139
+
140
+ # Trailing Caret
141
+ li(class: "flex items-center") do
142
+ svg(
143
+ class: "rtl:rotate-180 block w-3 h-3 mx-1 text-gray-400",
144
+ aria_hidden: "true",
145
+ xmlns: "http://www.w3.org/2000/svg",
146
+ fill: "none",
147
+ viewbox: "0 0 6 10"
148
+ ) do |s|
149
+ s.path(
150
+ stroke: "currentColor",
151
+ stroke_linecap: "round",
152
+ stroke_linejoin: "round",
153
+ stroke_width: "2",
154
+ d: "m1 9 4-4-4-4"
155
+ )
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "phlex"
4
+
5
+ module Plutonium
6
+ module UI
7
+ module Component
8
+ class Base < (defined?(::ApplicationComponent) ? ::ApplicationComponent : Phlex::HTML)
9
+ include Plutonium::UI::Component::Behaviour
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Component
6
+ module Behaviour
7
+ extend ActiveSupport::Concern
8
+ include Methods
9
+ include Kit
10
+
11
+ if Rails.env.development?
12
+ def around_template(&)
13
+ comment { "open:#{self.class.name}" }
14
+ super
15
+ comment { "close:#{self.class.name}" }
16
+ end
17
+ end
18
+
19
+ protected
20
+
21
+ def phlexi_render(arg, &)
22
+ return unless arg
23
+ raise ArgumentError, "phlexi_render requires a default render block" unless block_given?
24
+
25
+ # Handle Phlex components or Rails Renderables
26
+ if arg.class < Phlex::SGML || arg.respond_to?(:render_in)
27
+ render arg
28
+ # Handle procs
29
+ elsif arg.respond_to?(:to_proc)
30
+ instance_exec(&arg)
31
+ else
32
+ yield
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "phlex"
4
+
5
+ module Plutonium
6
+ module UI
7
+ module Component
8
+ module Kit
9
+ extend ActiveSupport::Concern
10
+
11
+ def Breadcrumbs(...) = render Plutonium::UI::Breadcrumbs.new(...)
12
+
13
+ def DynaFrameContent(...) = render Plutonium::UI::DynaFrame::Content.new(...)
14
+
15
+ def PageHeader(...) = render Plutonium::UI::PageHeader.new(...)
16
+
17
+ def ActionButton(...) = render Plutonium::UI::ActionButton.new(...)
18
+
19
+ def EmptyCard(...) = render Plutonium::UI::EmptyCard.new(...)
20
+
21
+ def TableSearchBar(...) = render Plutonium::UI::Table::Components::SearchBar.new(...)
22
+
23
+ def TableScopesBar(...) = render Plutonium::UI::Table::Components::ScopesBar.new(...)
24
+
25
+ def TableInfo(...) = render Plutonium::UI::Table::Components::PagyInfo.new(...)
26
+
27
+ def TablePagination(...) = render Plutonium::UI::Table::Components::PagyPagination.new(...)
28
+ end
29
+ end
30
+ end
31
+ end