avo 2.5.2.pre.7 → 2.6.1.pre.2

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +5 -1
  4. data/app/assets/builds/action_cable.js +2 -0
  5. data/app/assets/builds/action_cable.js.map +7 -0
  6. data/app/assets/builds/avo.css +175 -122
  7. data/app/assets/builds/avo.js +63 -63
  8. data/app/assets/builds/avo.js.map +3 -3
  9. data/app/assets/stylesheets/avo.css +33 -1
  10. data/app/assets/stylesheets/css/search.css +1 -1
  11. data/app/assets/svgs/heroicons/solid/user-remove.svg +1 -1
  12. data/app/components/avo/actions_component.html.erb +3 -2
  13. data/app/components/avo/alert_component.html.erb +1 -1
  14. data/app/components/avo/alert_component.rb +24 -5
  15. data/app/components/avo/button_component.rb +50 -17
  16. data/app/components/avo/card_component.rb +12 -0
  17. data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +3 -0
  18. data/app/components/avo/fields/concerns/item_labels.rb +40 -0
  19. data/app/components/avo/fields/tags_field/edit_component.html.erb +1 -1
  20. data/app/components/avo/fields/tags_field/index_component.html.erb +1 -5
  21. data/app/components/avo/fields/tags_field/index_component.rb +2 -0
  22. data/app/components/avo/fields/tags_field/show_component.rb +3 -7
  23. data/app/components/avo/fields/tags_field/tag_component.html.erb +1 -1
  24. data/app/components/avo/filters_component.html.erb +1 -1
  25. data/app/components/avo/index/field_wrapper_component.html.erb +1 -1
  26. data/app/components/avo/index/grid_cover_empty_state_component.html.erb +1 -1
  27. data/app/components/avo/index/table_row_component.html.erb +1 -1
  28. data/app/components/avo/paginator_component.html.erb +2 -2
  29. data/app/components/avo/panel_component.html.erb +3 -3
  30. data/app/components/avo/panel_component.rb +1 -1
  31. data/app/components/avo/resource_component.rb +50 -0
  32. data/app/components/avo/sidebar/group_component.html.erb +4 -2
  33. data/app/components/avo/sidebar/heading_component.html.erb +1 -1
  34. data/app/components/avo/sidebar/link_component.html.erb +1 -1
  35. data/app/components/avo/sidebar/link_component.rb +1 -1
  36. data/app/components/avo/sidebar_component.html.erb +5 -13
  37. data/app/components/avo/sidebar_profile_component.html.erb +1 -1
  38. data/app/components/avo/views/resource_edit_component.html.erb +28 -3
  39. data/app/components/avo/views/resource_edit_component.rb +4 -6
  40. data/app/components/avo/views/resource_index_component.html.erb +17 -9
  41. data/app/components/avo/views/resource_new_component.html.erb +8 -2
  42. data/app/components/avo/views/resource_show_component.html.erb +16 -6
  43. data/app/components/avo/views/resource_show_component.rb +0 -45
  44. data/app/controllers/avo/actions_controller.rb +23 -8
  45. data/app/controllers/avo/associations_controller.rb +3 -3
  46. data/app/controllers/avo/base_controller.rb +11 -21
  47. data/app/controllers/avo/private_controller.rb +1 -0
  48. data/app/controllers/avo/search_controller.rb +33 -13
  49. data/app/helpers/avo/application_helper.rb +1 -1
  50. data/app/javascript/js/controllers/fields/key_value_controller.js +1 -1
  51. data/app/javascript/js/controllers/fields/tags_field_controller.js +3 -3
  52. data/app/javascript/js/controllers/filter_controller.js +4 -1
  53. data/app/javascript/js/controllers/search_controller.js +9 -1
  54. data/app/javascript/js/controllers.js +0 -2
  55. data/app/views/avo/actions/show.html.erb +5 -2
  56. data/app/views/avo/dashboards/_chartkick_card.html.erb +1 -1
  57. data/app/views/avo/dashboards/_metric_card.html.erb +1 -1
  58. data/app/views/avo/partials/_global_search.html.erb +1 -1
  59. data/app/views/avo/partials/_logo.html.erb +1 -1
  60. data/app/views/avo/partials/_navbar.html.erb +9 -6
  61. data/app/views/avo/partials/_resource_search.html.erb +1 -1
  62. data/app/views/avo/partials/_table_header.html.erb +3 -2
  63. data/app/views/avo/private/_links_and_buttons.html.erb +2 -2
  64. data/app/views/layouts/avo/application.html.erb +50 -53
  65. data/lib/avo/base_action.rb +24 -6
  66. data/lib/avo/engine.rb +1 -1
  67. data/lib/avo/fields/belongs_to_field.rb +4 -4
  68. data/lib/avo/fields/has_and_belongs_to_many_field.rb +2 -2
  69. data/lib/avo/fields/has_base_field.rb +2 -0
  70. data/lib/avo/fields/has_many_field.rb +2 -2
  71. data/lib/avo/fields/has_one_field.rb +2 -2
  72. data/lib/avo/fields/tags_field.rb +5 -5
  73. data/lib/avo/hosts/base_host.rb +2 -0
  74. data/lib/avo/version.rb +1 -1
  75. data/lib/generators/avo/templates/cards/chartkick_card.tt +1 -1
  76. data/lib/generators/avo/templates/cards/chartkick_card_sample.tt +1 -1
  77. data/lib/generators/avo/templates/cards/metric_card.tt +1 -1
  78. data/lib/generators/avo/templates/cards/metric_card_sample.tt +1 -1
  79. data/lib/tasks/avo_tasks.rake +7 -3
  80. data/public/avo-assets/avo.css +225 -172
  81. data/public/avo-assets/avo.js +63 -63
  82. data/public/avo-assets/avo.js.map +3 -3
  83. metadata +5 -4
  84. data/app/assets/stylesheets/css/alerts.css +0 -35
  85. data/app/javascript/js/controllers/alerts_controller.js +0 -26
@@ -12,7 +12,6 @@
12
12
  @import './css/buttons.css';
13
13
  @import './css/typography.css';
14
14
  @import './css/tooltips.css';
15
- @import './css/alerts.css';
16
15
  @import './css/loader.css';
17
16
  @import './css/pagination.css';
18
17
  @import './css/breadcrumbs.css';
@@ -91,3 +90,36 @@ trix-editor {
91
90
  max-height: 320px !important;
92
91
  overflow-y: auto;
93
92
  }
93
+
94
+ .scroll-shadows {
95
+ background:
96
+ /* Shadow Cover TOP */
97
+ linear-gradient(
98
+ #F6F6F7 30%,
99
+ #F6F6F7
100
+ ) center top,
101
+
102
+ /* Shadow Cover BOTTOM */
103
+ linear-gradient(
104
+ #F6F6F7,
105
+ #F6F6F7 70%
106
+ ) center bottom,
107
+
108
+ /* Shadow TOP */
109
+ radial-gradient(
110
+ farthest-side at 50% 0,
111
+ rgba(0, 0, 0, 0.2),
112
+ rgba(0, 0, 0, 0)
113
+ ) center top,
114
+
115
+ /* Shadow BOTTOM */
116
+ radial-gradient(
117
+ farthest-side at 50% 100%,
118
+ rgba(0, 0, 0, 0.2),
119
+ rgba(0, 0, 0, 0)
120
+ ) center bottom;
121
+
122
+ background-repeat: no-repeat;
123
+ background-size: 100% 34px, 100% 34px, 100% 14px, 100% 14px;
124
+ background-attachment: local, local, scroll, scroll;
125
+ }
@@ -32,7 +32,7 @@
32
32
 
33
33
  .aa-DetachedFormContainer,
34
34
  .aa-DetachedContainer .aa-Panel {
35
- @apply bg-gray-100 border-b-0;
35
+ @apply bg-gray-50 border-b-0;
36
36
  }
37
37
 
38
38
  .aa-Form {
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
1
+ or<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
2
2
  <path d="M11 6a3 3 0 11-6 0 3 3 0 016 0zM14 17a6 6 0 00-12 0h12zM13 8a1 1 0 100 2h4a1 1 0 100-2h-4z"/>
3
3
  </svg>
@@ -3,7 +3,8 @@
3
3
  data-actions-picker-enabled-class="text-black hover:bg-blue-500 hover:text-white"
4
4
  data-actions-picker-disabled-class="cursor-wait text-gray-500"
5
5
  >
6
- <%= a_button style: :primary,
6
+ <%= a_button style: :outline,
7
+ color: :primary,
7
8
  class: "focus:outline-none",
8
9
  icon: 'arrow-circle-right',
9
10
  icon_class: 'transform rotate-90',
@@ -34,7 +35,7 @@
34
35
  'actions-picker-target': action.standalone ? 'standaloneAction' : 'resourceAction',
35
36
  'disabled': disabled,
36
37
  },
37
- class: "flex items-center px-4 py-4 w-full font-medium #{disabled ? 'text-gray-500' : 'text-black hover:bg-blue-500 hover:text-white'}" do %>
38
+ class: "flex items-center px-4 py-3 w-full font-semibold text-sm #{disabled ? 'text-gray-500' : 'text-black hover:bg-blue-500 hover:text-white'}" do %>
38
39
  <%= svg 'play', class: 'h-5 mr-1 inline' %> <%= action.action_name %>
39
40
  <% end %>
40
41
  <% end %>
@@ -13,7 +13,7 @@
13
13
  <%== message %>
14
14
  </p>
15
15
  </div>
16
- <div class="ml-4 flex-shrink-0 flex">
16
+ <div class="ml-4 flex-shrink-0 flex items-center">
17
17
  <button data-action="alert#close" class="inline-flex text-white focus:outline-none focus:text-gray-300 transition ease-in-out duration-150">
18
18
  <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
19
19
  <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
@@ -12,7 +12,10 @@ class Avo::AlertComponent < ViewComponent::Base
12
12
  end
13
13
 
14
14
  def icon
15
- return "x-circle" if is_error?
15
+ return "heroicons/solid/x-circle" if is_error?
16
+ return "heroicons/solid/exclamation" if is_warning?
17
+ return "heroicons/solid/exclamation-circle" if is_info?
18
+ return "heroicons/solid/check-circle" if is_success?
16
19
 
17
20
  "check-circle"
18
21
  end
@@ -21,15 +24,31 @@ class Avo::AlertComponent < ViewComponent::Base
21
24
  result = "max-w-lg w-full shadow-lg rounded px-4 py-3 rounded relative border text-white pointer-events-auto"
22
25
 
23
26
  result += if is_error?
24
- " bg-red-400 border-red-700"
25
- else
26
- " bg-green-400 border-green-700"
27
+ " bg-red-400 border-red-600"
28
+ elsif is_success?
29
+ " bg-green-500 border-green-600"
30
+ elsif is_warning?
31
+ " bg-orange-400 border-orange-600"
32
+ elsif is_info?
33
+ " bg-blue-400 border-blue-600"
27
34
  end
28
35
 
29
36
  result
30
37
  end
31
38
 
32
39
  def is_error?
33
- type.to_sym == :error
40
+ type.to_sym == :error || type.to_sym == :alert
41
+ end
42
+
43
+ def is_success?
44
+ type.to_sym == :success
45
+ end
46
+
47
+ def is_info?
48
+ type.to_sym == :notice || type.to_sym == :info
49
+ end
50
+
51
+ def is_warning?
52
+ type.to_sym == :warning
34
53
  end
35
54
  end
@@ -38,14 +38,15 @@ class Avo::ButtonComponent < ViewComponent::Base
38
38
  end
39
39
 
40
40
  def button_classes
41
- classes = "button-component inline-flex flex-grow-0 items-center text-sm font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 transform transition duration-100 cursor-pointer disabled:cursor-not-allowed disabled:opacity-70 border justify-center active:outline active:outline-1 #{@class}"
41
+ classes = "button-component inline-flex flex-grow-0 items-center font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 transform transition duration-100 cursor-pointer disabled:cursor-not-allowed disabled:opacity-70 border justify-center active:outline active:outline-1 #{@class}"
42
42
 
43
43
  classes += " rounded" if @rounded.present?
44
44
 
45
45
  classes += style_classes
46
46
 
47
- classes += @compact ? " px-1" : " px-4" # Same horizontal padding on all sizes
48
- classes += spacing_classes
47
+ classes += horizontal_padding_classes
48
+ classes += vertical_padding_classes
49
+ classes += text_size_classes
49
50
 
50
51
  classes
51
52
  end
@@ -57,10 +58,14 @@ class Avo::ButtonComponent < ViewComponent::Base
57
58
  def full_content
58
59
  result = ""
59
60
  icon_classes = @icon_class
60
- icon_classes += full_content_icon_classes
61
+ # space out the icon from the text if text is present
62
+ icon_classes += " mr-1" if content.present?
63
+ # add the icon height
64
+ icon_classes += icon_size_classes
61
65
 
66
+ # Add the icon
62
67
  result += helpers.svg(@icon, class: icon_classes) if @icon.present?
63
- result += "<span class='w-0 m-0'>&nbsp;</span>"
68
+
64
69
  if content.present?
65
70
  result += "<span>#{content}</span>"
66
71
  end
@@ -96,49 +101,77 @@ class Avo::ButtonComponent < ViewComponent::Base
96
101
 
97
102
  private
98
103
 
99
- def spacing_classes
104
+ def vertical_padding_classes
100
105
  case @size.to_sym
101
106
  when :xs
102
107
  " py-0"
103
108
  when :sm
104
109
  " py-1"
105
110
  when :md
106
- " py-2"
111
+ " py-1.5"
107
112
  when :lg
113
+ " py-2"
114
+ when :xl
108
115
  " py-3"
109
116
  else
110
117
  ""
111
118
  end
112
119
  end
113
120
 
121
+ def horizontal_padding_classes
122
+ return " px-1" if @compact
123
+
124
+ case @size.to_sym
125
+ when :xs
126
+ " px-2"
127
+ when :sm
128
+ " px-3"
129
+ when :md
130
+ " px-3"
131
+ when :lg
132
+ " px-5"
133
+ when :xl
134
+ " px-6"
135
+ else
136
+ "px-4"
137
+ end
138
+ end
139
+
140
+ def text_size_classes
141
+ case @size.to_sym
142
+ when :xs
143
+ " text-xs"
144
+ else
145
+ " text-sm"
146
+ end
147
+ end
148
+
114
149
  def style_classes
115
150
  case @style
116
151
  when :primary
117
152
  " bg-primary-500 text-white border-primary-500 hover:bg-primary-600 hover:border-primary-600 active:border-primary-700 active:outline-primary-700 active:bg-primary-600"
118
153
  when :outline
119
154
  " bg-white text-#{@color}-500 border-#{@color}-500 hover:bg-#{@color}-100 active:bg-#{@color}-100 active:border-#{@color}-500 active:outline-#{@color}-500"
155
+ when :text
156
+ " text-#{@color}-500 active:outline-#{@color}-500 hover:bg-gray-100 border-transparent"
120
157
  else
121
158
  ""
122
159
  end
123
160
  end
124
161
 
125
- def full_content_icon_classes
162
+ def icon_size_classes
126
163
  icon_classes = ""
127
164
 
128
165
  case @size
129
166
  when :xs
130
- icon_classes += " h-4"
131
- # When icon is solo we need to add an offset
132
- icon_classes += " my-1" if content.blank?
167
+ icon_classes += " h-4 my-1"
133
168
  when :sm
134
- icon_classes += " h-4"
135
- # When icon is solo we need to add an offset
136
- icon_classes += " my-1" if content.blank?
169
+ icon_classes += " h-4 my-1"
137
170
  when :md
138
- icon_classes += " h-5"
139
- # When icon is solo we need to add an offset
140
- icon_classes += " my-0.5" if content.blank?
171
+ icon_classes += " h-4 my-1"
141
172
  when :lg
173
+ icon_classes += " h-5 my-0.5"
174
+ when :xl
142
175
  icon_classes += " h-6"
143
176
  end
144
177
 
@@ -3,9 +3,21 @@
3
3
  class Avo::CardComponent < ViewComponent::Base
4
4
  def initialize(card: nil)
5
5
  @card = card
6
+
7
+ init_card
6
8
  end
7
9
 
8
10
  def render?
9
11
  !@card.nil?
10
12
  end
13
+
14
+ # Initializing the card byt running the query method.
15
+ # We'll still keep the query block around for compatibility reasons.
16
+ def init_card
17
+ if @card.respond_to? :query
18
+ @card.query
19
+ elsif @card.query_block.present?
20
+ @card.compute_result
21
+ end
22
+ end
11
23
  end
@@ -7,6 +7,9 @@
7
7
  data-via-association-id="<%= @field.id %>"
8
8
  data-via-reflection-id="<%= @field.model.id %>"
9
9
  data-via-reflection-class="<%= @field.model.class.to_s %>"
10
+ data-via-parent-resource-id="<%= params[:via_resource_id] %>"
11
+ data-via-parent-resource-class="<%= params[:via_relation_class] %>"
12
+ data-via-relation="<%= params[:via_relation] %>"
10
13
  ></div>
11
14
  <div class="relative w-full" autocomplete="off">
12
15
  <%= @form.text_field @foreign_key,
@@ -0,0 +1,40 @@
1
+ module Avo
2
+ module Fields
3
+ module Concerns
4
+ module ItemLabels
5
+ extend ActiveSupport::Concern
6
+
7
+ def value_for_item(item)
8
+ if @field.acts_as_taggable_on.present?
9
+ item["value"]
10
+ else
11
+ item
12
+ end
13
+ end
14
+
15
+ def label_from_item(item)
16
+ value = value_for_item item
17
+
18
+ if suggestions_are_a_hash?
19
+ return suggestions_by_id[value.to_s][:label] if suggestions_by_id[value.to_s].present?
20
+ end
21
+ value
22
+ end
23
+
24
+ def suggestions_by_id
25
+ return {} unless suggestions_are_a_hash?
26
+
27
+ @field.suggestions.map do |suggestion|
28
+ [suggestion[:value].to_s, suggestion]
29
+ end.to_h
30
+ end
31
+
32
+ def suggestions_are_a_hash?
33
+ return false if @field.suggestions.blank?
34
+
35
+ @field.suggestions.first.is_a? Hash
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -18,7 +18,7 @@
18
18
  data: {
19
19
  'tags-field-target': 'input',
20
20
  'whitelist-items': @field.suggestions.to_json,
21
- 'blacklist-items': @field.blacklist.to_json,
21
+ 'disallowed-items': @field.disallowed.to_json,
22
22
  'enforce-suggestions': @field.enforce_suggestions ? 1 : 0,
23
23
  'delimiters': @field.delimiters,
24
24
  'close-on-select': @field.close_on_select ? 1 : 0,
@@ -1,11 +1,7 @@
1
1
  <%= index_field_wrapper field: @field do %>
2
2
  <div class="flex gap-1 items-center flex-nowrap">
3
3
  <% value.take(3).each do |item| %>
4
- <% if @field.acts_as_taggable_on.present? %>
5
- <%= render Avo::Fields::TagsField::TagComponent.new(label: item['value']) %>
6
- <% else %>
7
- <%= render Avo::Fields::TagsField::TagComponent.new(label: item) %>
8
- <% end %>
4
+ <%= render Avo::Fields::TagsField::TagComponent.new(label: label_from_item(item)) %>
9
5
  <% end %>
10
6
  <% if value.count > 3 %>
11
7
  <%= render Avo::Fields::TagsField::TagComponent.new(label: '...', title: I18n.t('avo.x_items_more', count: value.count - 3)) %>
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Fields::TagsField::IndexComponent < Avo::Fields::IndexComponent
4
+ include Avo::Fields::Concerns::ItemLabels
5
+
4
6
  def value
5
7
  @field.field_value
6
8
  end
@@ -1,11 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # require_relative 'item_labels'
4
+
3
5
  class Avo::Fields::TagsField::ShowComponent < Avo::Fields::ShowComponent
4
- def label_from_item(item)
5
- if @field.acts_as_taggable_on.present?
6
- item['value']
7
- else
8
- item
9
- end
10
- end
6
+ include Avo::Fields::Concerns::ItemLabels
11
7
  end
@@ -1,4 +1,4 @@
1
- <div class="flex px-2 py-1 rounded bg-gray-200 text-sm text-gray-800 font-normal flex-shrink-0"
1
+ <div class="flex px-2 py-1 rounded bg-gray-100 text-sm text-gray-800 font-normal flex-shrink-0"
2
2
  <% if title.present? %>
3
3
  data-tippy="tooltip"
4
4
  title="<%= title %>"
@@ -14,7 +14,7 @@
14
14
  <% end %>
15
15
  <% end %>
16
16
  <div
17
- class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-20 shadow-modal rounded divide-y divide-gray-300 <%= 'hidden' unless params[:keep_filters_panel_open] %>"
17
+ class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-20 shadow-modal rounded divide-y divide-gray-300 <%= 'hidden' unless params[:keep_filters_panel_open] == '1' %>"
18
18
  data-toggle-panel-target="panel"
19
19
  >
20
20
  <% @filters.each do |filter| %>
@@ -1,4 +1,4 @@
1
- <td class="min-h-[3rem] px-3 leading-tight whitespace-nowrap h-full text-slate-800 <%= classes %>" data-field-id="<%= @field.id %>" data-field-type="<%= @field.type %>">
1
+ <td class="min-h-[3rem] px-3 leading-tight whitespace-nowrap h-full text-slate-800 text-base <%= classes %>" data-field-id="<%= @field.id %>" data-field-type="<%= @field.type %>">
2
2
  <% if @field.value.blank? && @dash_if_blank %>
3
3
 
4
4
  <% else %>
@@ -1,3 +1,3 @@
1
- <div class="absolute bg-gray-100 w-full h-full">
1
+ <div class="absolute bg-gray-50 w-full h-full">
2
2
  <%= helpers.svg 'avocado', class: 'relative transform -translate-x-1/2 -translate-y-1/2 h-20 text-gray-400 inset-auto top-1/2 left-1/2' %>
3
3
  </div>
@@ -1,5 +1,5 @@
1
1
  <tr
2
- class="bg-white hover:bg-blue-50 hover:shadow-row hover:z-[21] relative z-20 border-b"
2
+ class="bg-white hover:bg-gray-50 hover:shadow-row hover:z-[21] relative z-20 border-b"
3
3
  <%== item_selector_init @resource %>
4
4
  >
5
5
  <% if @resource.record_selector %>
@@ -25,9 +25,9 @@
25
25
  </div>
26
26
  <% per_page_options.each do |option| %>
27
27
  <% if parent_model.present? %>
28
- <%= link_to "Change to #{option} items per page", helpers.related_resources_path(parent_model, parent_model, per_page: option, keep_query_params: true), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
28
+ <%= link_to "Change to #{option} items per page", helpers.related_resources_path(parent_model, parent_model, per_page: option, keep_query_params: true, page: 1), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
29
29
  <% else %>
30
- <%= link_to "Change to #{option} items per page", helpers.resources_path(resource: resource, per_page: option, keep_query_params: true), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
30
+ <%= link_to "Change to #{option} items per page", helpers.resources_path(resource: resource, per_page: option, keep_query_params: true, page: 1), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
31
31
  <% end %>
32
32
  <% end %>
33
33
  </div>
@@ -1,7 +1,7 @@
1
1
  <div <%== data_attributes %>>
2
2
  <% if render_header? %>
3
- <div class="<%= white_panel_classes %> p-4 flex-1 flex flex-col xl:flex-row justify-between mb-6">
4
- <div class="overflow-hidden mr-4 flex flex-col">
3
+ <div class="flex-1 flex flex-col xl:flex-row justify-between mb-4">
4
+ <div class="overflow-hidden flex flex-col">
5
5
  <% if display_breadcrumbs? %>
6
6
  <div class="breadcrumbs truncate mb-2">
7
7
  <%= helpers.render_breadcrumbs(separator: helpers.svg('chevron-right', class: 'inline-block h-3 stroke-current relative top-[-1px] ml-1' )) if Avo.configuration.display_breadcrumbs %>
@@ -13,7 +13,7 @@
13
13
  </div>
14
14
 
15
15
  <% if description.present? %>
16
- <div class="text-base tracking-normal font-medium text-gray-600" data-target="description">
16
+ <div class="text-sm tracking-normal font-medium text-gray-600" data-target="description">
17
17
  <%== description %>
18
18
  </div>
19
19
  <% end %>
@@ -21,7 +21,7 @@ class Avo::PanelComponent < ViewComponent::Base
21
21
  private
22
22
 
23
23
  def white_panel_classes
24
- 'bg-white rounded shadow'
24
+ "bg-white rounded shadow"
25
25
  end
26
26
 
27
27
  def data_attributes
@@ -11,6 +11,26 @@ class Avo::ResourceComponent < Avo::BaseComponent
11
11
  @resource.authorization.authorize_action(:destroy, raise_exception: false)
12
12
  end
13
13
 
14
+ def can_detach?
15
+ authorize_association_for("detach")
16
+ end
17
+
18
+ def can_see_the_edit_button?
19
+ @resource.authorization.authorize_action(:edit, raise_exception: false)
20
+ end
21
+
22
+ def can_see_the_destroy_button?
23
+ @resource.authorization.authorize_action(:destroy, raise_exception: false)
24
+ end
25
+
26
+ def detach_path
27
+ helpers.resource_detach_path(params[:resource_name], params[:id], @reflection.name.to_s, @resource.model.id)
28
+ end
29
+
30
+ def destroy_path
31
+ helpers.resource_path(model: @resource.model, resource: @resource)
32
+ end
33
+
14
34
  def authorize_association_for(policy_method)
15
35
  association_policy = true
16
36
 
@@ -30,4 +50,34 @@ class Avo::ResourceComponent < Avo::BaseComponent
30
50
 
31
51
  association_policy
32
52
  end
53
+
54
+ def split_panel_fields
55
+ initialize_panels
56
+ @resource.get_fields.each do |field|
57
+ case field.class.to_s
58
+ when "Avo::Fields::HasOneField"
59
+ @has_one_panels << field
60
+ when "Avo::Fields::HasManyField"
61
+ @has_many_panels << field
62
+ when "Avo::Fields::HasAndBelongsToManyField"
63
+ @has_as_belongs_to_many_panels << field
64
+ else
65
+ @fields_by_panel[field.panel_name] ||= []
66
+ @fields_by_panel[field.panel_name] << field
67
+ end
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def initialize_panels
74
+ @fields_by_panel = {}
75
+ @has_one_panels = []
76
+ @has_many_panels = []
77
+ @has_as_belongs_to_many_panels = []
78
+ end
79
+
80
+ def via_resource?
81
+ params[:via_resource_class].present? && params[:via_resource_id].present?
82
+ end
33
83
  end
@@ -7,7 +7,9 @@
7
7
  <% end %>
8
8
  >
9
9
  <% if item.name.present? %>
10
- <div class="flex justify-between px-10 pt-2 pb-0 text-gray-600 ">
10
+ <div
11
+ class="flex justify-between px-10 pr-2 pt-2 pb-0 text-gray-400"
12
+ >
11
13
  <div class="flex items-center text-xs uppercase font-semibold leading-none">
12
14
  <%= item.name %>
13
15
  </div>
@@ -17,7 +19,7 @@
17
19
  data-menu-target="svg"
18
20
  data-menu-key-param="<%= key %>"
19
21
  >
20
- <%= helpers.svg 'heroicons/outline/chevron-down', class: "h-4"%>
22
+ <%= helpers.svg 'heroicons/outline/chevron-down', class: "h-4 mr-0.5"%>
21
23
  </div>
22
24
  <% end %>
23
25
  </div>
@@ -1,4 +1,4 @@
1
- <div class="flex justify-between p-4 py-2 text-gray-500">
1
+ <div class="flex justify-between px-4 pr-2 py-1 text-gray-500">
2
2
  <div class="flex items-center text-sm uppercase font-semibold leading-none space-x-1">
3
3
  <span class="min-w-[20px]"><%= icon %></span> <span><%= label %></span>
4
4
  </div>
@@ -2,7 +2,7 @@
2
2
  <%= send link_method, path, class: classes, active: active, target: target do %>
3
3
  <%= label %>
4
4
  <% if target == :_blank %>
5
- <%= helpers.svg('heroicons/outline/external-link', class: 'self-center ml-auto h-3') %>
5
+ <%= helpers.svg('heroicons/outline/external-link', class: 'self-center ml-auto h-3 mr-2') %>
6
6
  <% end %>
7
7
  <% end %>
8
8
  <% else %>
@@ -23,6 +23,6 @@ class Avo::Sidebar::LinkComponent < ViewComponent::Base
23
23
  end
24
24
 
25
25
  def classes
26
- "px-4 flex-1 flex mx-6 leading-none py-2 text-black rounded font-medium hover:bg-gray-150"
26
+ "px-4 pr-0 flex-1 flex mx-6 leading-none py-2 text-black rounded font-medium hover:bg-gray-100"
27
27
  end
28
28
  end
@@ -1,24 +1,16 @@
1
1
  <div
2
- class="fixed z-[60] application-sidebar hidden lg:flex h-full bg-white text-white w-64 border-r <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
2
+ class="fixed z-[60] t-0 application-sidebar w-64 hidden lg:flex flex-1 border-r lg:border-none bg-none h-[calc(100vh-4rem)] bg-gray-25 lg:bg-transparent <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
3
3
  data-mobile-target="sidebar"
4
4
  >
5
5
  <div class="flex flex-col w-full h-full">
6
- <div class="flex justify-between">
7
- <%= render partial: "avo/partials/logo" %>
8
- <div class="flex items-center mr-4">
9
- <%= helpers.a_button class: "lg:hidden", icon: 'menu-back', data: { action: 'click->mobile#toggleSidebar' } %>
10
- </div>
11
- </div>
12
- <div class="flex-1 flex flex-col justify-between overflow-auto mt-3">
6
+ <div class="flex-1 flex flex-col justify-between overflow-auto h-full pt-3 scroll-shadows">
13
7
  <div class="space-y-6 mb-4">
14
8
  <%= render Avo::Sidebar::LinkComponent.new label: 'Get started', path: helpers.avo.root_path, active: :exclusive if Rails.env.development? && Avo.configuration.home_path.nil? %>
15
9
 
16
10
  <% if Avo::App.has_main_menu? %>
17
- <div class="text-black">
18
- <% Avo::App.main_menu.items.each do |item| %>
19
- <%= render Avo::Sidebar::ItemSwitcherComponent.new item: item %>
20
- <% end %>
21
- </div>
11
+ <% Avo::App.main_menu.items.each do |item| %>
12
+ <%= render Avo::Sidebar::ItemSwitcherComponent.new item: item %>
13
+ <% end %>
22
14
  <% else %>
23
15
 
24
16
  <% if dashboards.present? %>
@@ -1,4 +1,4 @@
1
- <div class="text-black border-t border-gray-200 p-4 flex">
1
+ <div class="text-black border-gray-200 p-4 flex">
2
2
  <div class="flex-1 flex space-x-4">
3
3
  <% if avatar.present? %>
4
4
  <div class="relative aspect-square w-10 h-10 overflow-hidden rounded">