avo 2.9.1.pre5 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of avo might be problematic. Click here for more details.

Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -5
  3. data/README.md +4 -0
  4. data/app/assets/stylesheets/css/buttons.css +4 -1
  5. data/app/components/avo/actions_component.rb +6 -2
  6. data/app/components/avo/base_component.rb +2 -0
  7. data/app/components/avo/button_component.rb +3 -1
  8. data/app/components/avo/fields/common/key_value_component.html.erb +2 -2
  9. data/app/components/avo/fields/common/single_file_viewer_component.rb +1 -1
  10. data/app/components/avo/fields/edit_component.rb +5 -0
  11. data/app/components/avo/fields/show_component.rb +1 -1
  12. data/app/components/avo/index/ordering/button_component.rb +2 -12
  13. data/app/components/avo/index/resource_controls_component.html.erb +2 -2
  14. data/app/components/avo/index/resource_controls_component.rb +5 -1
  15. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  16. data/app/components/avo/index/table_row_component.html.erb +1 -1
  17. data/app/components/avo/item_switcher_component.html.erb +19 -0
  18. data/app/components/avo/item_switcher_component.rb +45 -0
  19. data/app/components/avo/panel_component.html.erb +23 -24
  20. data/app/components/avo/panel_component.rb +8 -5
  21. data/app/components/avo/tab_group_component.html.erb +53 -0
  22. data/app/components/avo/tab_group_component.rb +51 -0
  23. data/app/components/avo/tab_switcher_component.html.erb +21 -0
  24. data/app/components/avo/tab_switcher_component.rb +86 -0
  25. data/app/components/avo/views/resource_edit_component.html.erb +34 -56
  26. data/app/components/avo/views/resource_edit_component.rb +10 -0
  27. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  28. data/app/components/avo/views/resource_index_component.rb +3 -3
  29. data/app/components/avo/views/resource_show_component.html.erb +58 -89
  30. data/app/components/avo/views/resource_show_component.rb +2 -2
  31. data/app/controllers/avo/actions_controller.rb +1 -1
  32. data/app/controllers/avo/application_controller.rb +20 -3
  33. data/app/helpers/avo/application_helper.rb +0 -6
  34. data/app/helpers/avo/url_helpers.rb +1 -1
  35. data/app/javascript/avo.js +5 -1
  36. data/app/javascript/js/controllers/loading_button_controller.js +25 -21
  37. data/app/javascript/js/controllers/tabs_controller.js +86 -0
  38. data/app/javascript/js/controllers.js +2 -0
  39. data/app/views/avo/base/index.html.erb +1 -1
  40. data/app/views/avo/base/show.html.erb +1 -1
  41. data/app/views/avo/cards/show.html.erb +1 -1
  42. data/app/views/avo/debug/index.html.erb +1 -1
  43. data/app/views/avo/home/_actions.html.erb +1 -1
  44. data/app/views/avo/home/_dashboards.html.erb +19 -0
  45. data/app/views/avo/home/_filters.html.erb +1 -1
  46. data/app/views/avo/home/_resources.html.erb +1 -1
  47. data/app/views/avo/home/failed_to_load.html.erb +1 -1
  48. data/app/views/avo/home/index.html.erb +14 -2
  49. data/app/views/avo/partials/_javascript.html.erb +1 -1
  50. data/app/views/avo/partials/_tabs_toggle.html.erb +20 -0
  51. data/app/views/avo/private/design.html.erb +1 -1
  52. data/config/routes.rb +1 -1
  53. data/lib/avo/app.rb +9 -2
  54. data/lib/avo/base_action.rb +2 -19
  55. data/lib/avo/base_card.rb +1 -7
  56. data/lib/avo/base_resource.rb +0 -94
  57. data/lib/avo/base_resource_tool.rb +3 -1
  58. data/lib/avo/concerns/has_fields.rb +247 -50
  59. data/lib/avo/concerns/has_html_attributes.rb +1 -1
  60. data/lib/avo/concerns/is_resource_item.rb +36 -0
  61. data/lib/avo/dashboards/base_dashboard.rb +1 -1
  62. data/lib/avo/dsl/field_parser.rb +83 -0
  63. data/lib/avo/fields/base_field.rb +19 -2
  64. data/lib/avo/fields/field_extensions/visible_in_different_views.rb +18 -1
  65. data/lib/avo/fields/has_base_field.rb +20 -1
  66. data/lib/avo/fields/has_one_field.rb +4 -1
  67. data/lib/avo/grid_collector.rb +6 -3
  68. data/lib/avo/items_holder.rb +68 -0
  69. data/lib/avo/licensing/h_q.rb +10 -0
  70. data/lib/avo/main_panel.rb +3 -0
  71. data/lib/avo/menu/builder.rb +7 -7
  72. data/lib/avo/panel.rb +25 -0
  73. data/lib/avo/panel_builder.rb +23 -0
  74. data/lib/avo/services/uri_service.rb +71 -0
  75. data/lib/avo/tab.rb +78 -0
  76. data/lib/avo/tab_builder.rb +25 -0
  77. data/lib/avo/tab_group.rb +40 -0
  78. data/lib/avo/tab_group_builder.rb +43 -0
  79. data/lib/avo/version.rb +1 -1
  80. data/lib/avo.rb +1 -0
  81. data/lib/generators/avo/templates/resource/controller.tt +2 -0
  82. data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
  83. data/lib/generators/avo/templates/tool/view.tt +1 -1
  84. data/public/avo-assets/avo.css +27 -3
  85. data/public/avo-assets/avo.js +73 -73
  86. data/public/avo-assets/avo.js.map +3 -3
  87. metadata +24 -14
  88. data/app/assets/builds/action_cable.js +0 -2
  89. data/app/assets/builds/action_cable.js.map +0 -7
  90. data/app/assets/builds/application.js +0 -2
  91. data/app/assets/builds/application.js.map +0 -7
  92. data/app/assets/builds/avo.css +0 -9028
  93. data/app/assets/builds/avo.js +0 -512
  94. data/app/assets/builds/avo.js.map +0 -7
  95. data/app/assets/builds/avo_custom.js +0 -6
  96. data/app/assets/builds/avo_custom.js.map +0 -7
  97. data/lib/avo/concerns/has_tools.rb +0 -47
@@ -0,0 +1,68 @@
1
+ module Avo
2
+ class ItemsHolder
3
+ attr_reader :tools
4
+ attr_accessor :items
5
+ attr_accessor :invalid_fields
6
+
7
+ def initialize
8
+ @items = []
9
+ @items_index = 0
10
+ @invalid_fields = []
11
+ end
12
+
13
+ # Adds a field to the bag
14
+ def field(field_name, **args, &block)
15
+ field_parser = Avo::Dsl::FieldParser.new(id: field_name, order_index: @items_index, **args, &block).parse
16
+
17
+ if field_parser.invalid?
18
+ as = args.fetch(:as, nil)
19
+
20
+ # End execution ehre and add the field to the invalid_fileds payload so we know to wanr the developer about that.
21
+ # @todo: Make sure this warning is still active
22
+ return add_invalid_field({
23
+ name: field_name,
24
+ as: as,
25
+ # resource: resource_class.name,
26
+ message: "There's an invalid field configuration for this resource. <br/> <code class='px-1 py-px rounded bg-red-600'>field :#{field_name}, as: #{as}</code>"
27
+ })
28
+ end
29
+
30
+ add_item field_parser.instance
31
+ end
32
+
33
+ def tabs(instance)
34
+ add_item instance
35
+ end
36
+
37
+ def tab(name, **args, &block)
38
+ add_item Avo::TabBuilder.parse_block(name: name, **args, &block)
39
+ end
40
+
41
+ def tool(klass, **args)
42
+ instance = klass.new(**args)
43
+ add_item instance
44
+ end
45
+
46
+ def panel(panel_name = nil, **args, &block)
47
+ panel = Avo::PanelBuilder.parse_block(name: panel_name, **args, &block)
48
+
49
+ add_item panel
50
+ end
51
+
52
+ def add_item(instance)
53
+ @items << instance
54
+
55
+ increment_order_index
56
+ end
57
+
58
+ private
59
+
60
+ def add_invalid_field(payload)
61
+ @invalid_fields << payload
62
+ end
63
+
64
+ def increment_order_index
65
+ @items_index += 1
66
+ end
67
+ end
68
+ end
@@ -98,6 +98,7 @@ module Avo
98
98
  **other_metadata(:filters),
99
99
  main_menu_present: Avo.configuration.main_menu.present?,
100
100
  profile_menu_present: Avo.configuration.profile_menu.present?,
101
+ **config_metadata
101
102
  }
102
103
  rescue
103
104
  {}
@@ -178,6 +179,15 @@ module Avo
178
179
  }
179
180
  end
180
181
 
182
+ def config_metadata
183
+ {
184
+ config: {
185
+ root_path: Avo.configuration.root_path,
186
+ app_name: Avo.configuration.app_name
187
+ }
188
+ }
189
+ end
190
+
181
191
  def cache_and_return_error(error, exception_message = "")
182
192
  cache_response response: {error: error, exception_message: exception_message}.stringify_keys, time: 5.minutes.to_i
183
193
  end
@@ -0,0 +1,3 @@
1
+ class Avo::MainPanel < Avo::Panel
2
+ class_attribute :item_type, default: :main_panel
3
+ end
@@ -5,12 +5,12 @@ class Avo::Menu::Builder
5
5
  end
6
6
  end
7
7
 
8
- delegate :context, to: Avo::App
9
- delegate :current_user, to: Avo::App
10
- delegate :params, to: Avo::App
11
- delegate :request, to: Avo::App
12
- delegate :root_path, to: Avo::App
13
- delegate :view_context, to: Avo::App
8
+ delegate :context, to: ::Avo::App
9
+ delegate :current_user, to: ::Avo::App
10
+ delegate :params, to: ::Avo::App
11
+ delegate :request, to: ::Avo::App
12
+ delegate :root_path, to: ::Avo::App
13
+ delegate :view_context, to: ::Avo::App
14
14
 
15
15
  def initialize(name: nil, items: [])
16
16
  @menu = Avo::Menu::Menu.new
@@ -67,7 +67,7 @@ class Avo::Menu::Builder
67
67
  # Add all the tools
68
68
  def all_tools(**args)
69
69
  Avo::App.tools_for_navigation.each do |tool|
70
- link tool.humanize, path: "#{root_path}/#{tool}"
70
+ link tool.humanize, path: root_path(paths: [tool])
71
71
  end
72
72
  end
73
73
 
data/lib/avo/panel.rb ADDED
@@ -0,0 +1,25 @@
1
+ class Avo::Panel
2
+ include Avo::Concerns::IsResourceItem
3
+
4
+ class_attribute :item_type, default: :panel
5
+
6
+ attr_reader :name
7
+ attr_reader :description
8
+ attr_accessor :items_holder
9
+
10
+ delegate :items, :add_item, to: :items_holder
11
+
12
+ def initialize(name: nil, description: nil)
13
+ @name = name
14
+ @description = description
15
+ @items_holder = Avo::ItemsHolder.new
16
+ end
17
+
18
+ def add_item(item)
19
+ @items << item
20
+ end
21
+
22
+ def has_items?
23
+ @items.present?
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ class Avo::PanelBuilder
2
+ class << self
3
+ def parse_block(**args, &block)
4
+ Docile.dsl_eval(new(**args), &block).build
5
+ end
6
+ end
7
+
8
+ delegate :field, to: :items_holder
9
+ delegate :items, to: :items_holder
10
+
11
+ attr_reader :items_holder
12
+
13
+ def initialize(name: nil, **args)
14
+ @panel = Avo::Panel.new(name: name, **args)
15
+ @items_holder = Avo::ItemsHolder.new
16
+ end
17
+
18
+ # Fetch the tab
19
+ def build
20
+ @panel.items_holder = @items_holder
21
+ @panel
22
+ end
23
+ end
@@ -0,0 +1,71 @@
1
+ module Avo
2
+ module Services
3
+ class URIService
4
+ class << self
5
+ def parse(path)
6
+ self.new path
7
+ end
8
+ end
9
+
10
+ attr_reader :uri
11
+
12
+ def initialize(path = '')
13
+ @uri = Addressable::URI.parse(path)
14
+ end
15
+
16
+ def append_paths(*paths)
17
+ paths = Array.wrap(paths).flatten
18
+
19
+ return self if paths.blank?
20
+
21
+ # Add the intermediary forward slash
22
+ @uri.path = @uri.path.concat("/") unless @uri.path.ends_with? "/"
23
+
24
+ # Add the paths to the URI
25
+ @uri.merge!(path: @uri.path.concat(join_paths(paths)))
26
+
27
+ self
28
+ end
29
+ alias_method :append_path, :append_paths
30
+
31
+ def append_query(params)
32
+ params = if params.is_a? Hash
33
+ params.map do |key, value|
34
+ "#{key}=#{value}"
35
+ end
36
+ else
37
+ {}
38
+ end
39
+
40
+ return self if params.blank?
41
+
42
+ # Add the query params to the URI
43
+ @uri.merge!(query: [@uri.query, *params].compact.join("&"))
44
+
45
+ self
46
+ end
47
+
48
+ def to_s
49
+ @uri.to_s
50
+ end
51
+
52
+ private
53
+
54
+ def join_paths(paths)
55
+ paths.map do |path|
56
+ sanitize_path path
57
+ end
58
+ .join("/")
59
+ end
60
+
61
+ # Removes the forward slash if it's present at the start of the path
62
+ def sanitize_path(path)
63
+ if path.to_s.starts_with? '/'
64
+ path = path[1..-1]
65
+ end
66
+
67
+ path
68
+ end
69
+ end
70
+ end
71
+ end
data/lib/avo/tab.rb ADDED
@@ -0,0 +1,78 @@
1
+ class Avo::Tab
2
+ include Avo::Concerns::IsResourceItem
3
+ include Avo::Fields::FieldExtensions::VisibleInDifferentViews
4
+
5
+ class_attribute :item_type, default: :tab
6
+ delegate :items, :add_item, to: :items_holder
7
+
8
+ attr_reader :name
9
+ attr_reader :view
10
+ attr_accessor :description
11
+ attr_accessor :items_holder
12
+ attr_accessor :holds_one_field
13
+
14
+ def initialize(name: nil, description: nil, view: nil, holds_one_field: false, **args)
15
+ # Initialize the visibility markers
16
+ super
17
+
18
+ @name = name
19
+ @description = description
20
+ @holds_one_field = holds_one_field
21
+ @items_holder = Avo::ItemsHolder.new
22
+ @view = view
23
+
24
+ show_on args[:show_on] if args[:show_on].present?
25
+ hide_on args[:hide_on] if args[:hide_on].present?
26
+ only_on args[:only_on] if args[:only_on].present?
27
+ except_on args[:except_on] if args[:except_on].present?
28
+ end
29
+
30
+ def hydrate(view: nil)
31
+ @view = view
32
+
33
+ self
34
+ end
35
+
36
+ def turbo_frame_id(parent: nil)
37
+ id = "#{Avo::Tab.to_s.parameterize} #{name}".parameterize
38
+
39
+ return id if parent.nil?
40
+
41
+ "#{parent.turbo_frame_id} #{id}".parameterize
42
+ end
43
+
44
+ def empty?
45
+ visible_items.blank?
46
+ end
47
+
48
+ # Checks for visibility on itself or on theone field it holds
49
+ def visible_on?(view)
50
+ if holds_one_field
51
+ super(view) && items.first.visible_on?(view)
52
+ else
53
+ super(view)
54
+ end
55
+ end
56
+
57
+ def items
58
+ if self.items_holder.present?
59
+ self.items_holder.items
60
+ else
61
+ []
62
+ end
63
+ end
64
+
65
+ def visible_items
66
+ items.map do |item|
67
+ if item.is_field?
68
+ visible = item.visible_on?(view)
69
+ # Remove the fields that shouldn't be visible in this view
70
+ # eg: has_many fields on edit
71
+ item = nil unless visible
72
+ end
73
+
74
+ item
75
+ end
76
+ .compact
77
+ end
78
+ end
@@ -0,0 +1,25 @@
1
+ class Avo::TabBuilder
2
+ class << self
3
+ def parse_block(**args, &block)
4
+ Docile.dsl_eval(new(**args), &block).build
5
+ end
6
+ end
7
+
8
+ attr_reader :items_holder
9
+
10
+ delegate :field, to: :items_holder
11
+ delegate :tool, to: :items_holder
12
+ delegate :panel, to: :items_holder
13
+ delegate :items, to: :items_holder
14
+
15
+ def initialize(name: nil, **args)
16
+ @tab = Avo::Tab.new(name: name, **args)
17
+ @items_holder = Avo::ItemsHolder.new
18
+ end
19
+
20
+ # Fetch the tab
21
+ def build
22
+ @tab.items_holder = @items_holder
23
+ @tab
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ class Avo::TabGroup
2
+ include Avo::Concerns::HasFields
3
+ include Avo::Concerns::IsResourceItem
4
+
5
+ class_attribute :item_type, default: :tab_group
6
+
7
+ attr_reader :view
8
+ attr_accessor :index
9
+ attr_accessor :items_holder
10
+
11
+ def initialize(index: 0, view: nil)
12
+ @index = index
13
+ @items_holder = Avo::ItemsHolder.new
14
+ @view = view
15
+ end
16
+
17
+ def hydrate(view: nil)
18
+ @view = view
19
+
20
+ self
21
+ end
22
+
23
+ def visible_items
24
+ items.map do |item|
25
+ item.hydrate view: view
26
+ end
27
+ .select do |item|
28
+ # Remove items hidden in this view
29
+ item.visible_on? view
30
+ end
31
+ .select do |item|
32
+ # Remove empty items
33
+ !item.empty?
34
+ end
35
+ end
36
+
37
+ def turbo_frame_id
38
+ "#{Avo::TabGroup.to_s.parameterize} #{index}".parameterize
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ class Avo::TabGroupBuilder
2
+ class << self
3
+ def parse_block(&block)
4
+ Docile.dsl_eval(new, &block).build
5
+ end
6
+ end
7
+
8
+ attr_reader :items_holder
9
+
10
+ delegate :tab, to: :items_holder
11
+
12
+ def initialize
13
+ @group = Avo::TabGroup.new
14
+ @items_holder = Avo::ItemsHolder.new
15
+ end
16
+
17
+ def field(field_name, **args, &block)
18
+ parsed = Avo::Dsl::FieldParser.new(id: field_name, order_index: @items_index, **args, &block).parse
19
+ field_instance = parsed.instance
20
+
21
+ name = field_instance.name
22
+ tab = Avo::Tab.new name: name
23
+
24
+ if field_instance.has_own_panel?
25
+ tab.items_holder.add_item parsed.instance
26
+ tab.holds_one_field = true
27
+ else
28
+ # If the field is not in a panel, create one and add it
29
+ panel = Avo::Panel.new name: name
30
+ panel.items_holder.add_item parsed.instance
31
+ # Add that panel to the bag
32
+ tab.items_holder.add_item panel
33
+ end
34
+
35
+ @items_holder.tabs tab
36
+ end
37
+
38
+ # Fetch the tab
39
+ def build
40
+ @group.items_holder = @items_holder
41
+ @group
42
+ end
43
+ end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.9.1.pre5" unless const_defined?(:VERSION)
2
+ VERSION = "2.10.0" unless const_defined?(:VERSION)
3
3
  end
data/lib/avo.rb CHANGED
@@ -5,6 +5,7 @@ require_relative "avo/engine" if defined?(Rails)
5
5
  loader = Zeitwerk::Loader.for_gem
6
6
  loader.inflector.inflect(
7
7
  "html" => "HTML",
8
+ "uri_service" => "URIService",
8
9
  "has_html_attributes" => "HasHTMLAttributes"
9
10
  )
10
11
  loader.ignore("#{__dir__}/generators")
@@ -1,2 +1,4 @@
1
+ # This controller has been generated to enable Rails' resource routes.
2
+ # You shouldn't need to modify it in order to use Avo.
1
3
  class <%= controller_class %> < Avo::ResourcesController
2
4
  end
@@ -1,5 +1,5 @@
1
1
  <div class="flex flex-col">
2
- <%%= render Avo::PanelComponent.new title: "<%= human_name %>" do |c| %>
2
+ <%%= render Avo::PanelComponent.new(name: "<%= human_name %>") do |c| %>
3
3
  <%% c.tools do %>
4
4
  <%%= a_link('/avo', icon: 'heroicons/solid/academic-cap', style: :primary) do %>
5
5
  Dummy link
@@ -1,5 +1,5 @@
1
1
  <div class="flex flex-col">
2
- <%%= render Avo::PanelComponent.new title: '<%= human_name %>', display_breadcrumbs: true do |c| %>
2
+ <%%= render Avo::PanelComponent.new(name: '<%= human_name %>', display_breadcrumbs: true) do |c| %>
3
3
  <%% c.tools do %>
4
4
  <div class="text-sm italic">This is the panels tools section.</div>
5
5
  <%% end %>
@@ -3977,9 +3977,9 @@ a{
3977
3977
  display:flex;
3978
3978
  }
3979
3979
 
3980
- .button-group .button-component:first-child{
3981
- margin-right:-1px
3982
- }
3980
+ .button-group .button-component{
3981
+ margin-right:-1px
3982
+ }
3983
3983
 
3984
3984
  .button-group .button-component:first-child{
3985
3985
  border-top-left-radius:0.25rem;
@@ -6354,6 +6354,16 @@ trix-editor .attachment__metadata .attachment__size {
6354
6354
  margin-bottom:0px
6355
6355
  }
6356
6356
 
6357
+ .-mx-2{
6358
+ margin-left:-0.5rem;
6359
+ margin-right:-0.5rem
6360
+ }
6361
+
6362
+ .-my-2{
6363
+ margin-top:-0.5rem;
6364
+ margin-bottom:-0.5rem
6365
+ }
6366
+
6357
6367
  .mx-6{
6358
6368
  margin-left:1.5rem;
6359
6369
  margin-right:1.5rem
@@ -8885,6 +8895,16 @@ trix-editor {
8885
8895
  }
8886
8896
 
8887
8897
  @media (min-width: 1024px){
8898
+ .lg\:-mx-4{
8899
+ margin-left:-1rem;
8900
+ margin-right:-1rem
8901
+ }
8902
+
8903
+ .lg\:-my-4{
8904
+ margin-top:-1rem;
8905
+ margin-bottom:-1rem
8906
+ }
8907
+
8888
8908
  .lg\:flex{
8889
8909
  display:flex
8890
8910
  }
@@ -8927,6 +8947,10 @@ trix-editor {
8927
8947
  padding:1.5rem
8928
8948
  }
8929
8949
 
8950
+ .lg\:p-4{
8951
+ padding:1rem
8952
+ }
8953
+
8930
8954
  .lg\:px-8{
8931
8955
  padding-left:2rem;
8932
8956
  padding-right:2rem