avo 1.20.2.pre.2 → 1.22.0.pre.1

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.lock +57 -57
  3. data/README.md +1 -1
  4. data/app/assets/builds/avo.css +405 -224
  5. data/app/assets/builds/avo.js +16 -6
  6. data/app/assets/builds/avo.js.map +2 -2
  7. data/app/assets/stylesheets/avo.css +1 -1
  8. data/app/assets/stylesheets/css/fonts.css +53 -0
  9. data/app/assets/stylesheets/css/pagination.css +9 -9
  10. data/app/assets/stylesheets/css/search.css +3 -2
  11. data/app/assets/svgs/dashboards-icon.svg +6 -0
  12. data/app/assets/svgs/resources-icon.svg +13 -0
  13. data/app/assets/svgs/three-dots.svg +5 -0
  14. data/app/assets/svgs/tools-icon.svg +3 -0
  15. data/app/components/avo/button_component.html.erb +1 -0
  16. data/app/components/avo/button_component.rb +111 -0
  17. data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +14 -7
  18. data/app/components/avo/fields/belongs_to_field/autocomplete_component.rb +47 -3
  19. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +59 -38
  20. data/app/components/avo/fields/belongs_to_field/edit_component.rb +37 -0
  21. data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
  22. data/app/components/avo/index/field_wrapper_component.html.erb +8 -2
  23. data/app/components/avo/index/field_wrapper_component.rb +2 -1
  24. data/app/components/avo/index/resource_table_component.html.erb +3 -3
  25. data/app/components/avo/index/table_row_component.html.erb +1 -1
  26. data/app/components/avo/navigation_heading_component.html.erb +2 -2
  27. data/app/components/avo/navigation_heading_component.rb +5 -1
  28. data/app/components/avo/navigation_link_component.html.erb +2 -2
  29. data/app/components/avo/navigation_link_component.rb +5 -0
  30. data/app/components/avo/panel_component.html.erb +5 -3
  31. data/app/components/avo/sidebar_profile_component.html.erb +28 -0
  32. data/app/components/avo/sidebar_profile_component.rb +43 -0
  33. data/app/components/avo/views/resource_index_component.html.erb +8 -7
  34. data/app/components/avo/views/resource_show_component.html.erb +10 -0
  35. data/app/controllers/avo/application_controller.rb +14 -12
  36. data/app/controllers/avo/base_controller.rb +4 -1
  37. data/app/helpers/avo/application_helper.rb +7 -5
  38. data/app/helpers/avo/resources_helper.rb +2 -2
  39. data/app/javascript/js/controllers/fields/date_field_controller.js +10 -2
  40. data/app/javascript/js/controllers/item_selector_controller.js +29 -19
  41. data/app/javascript/js/controllers/search_controller.js +8 -3
  42. data/app/views/avo/base/_actions.html.erb +4 -3
  43. data/app/views/avo/base/_filters.html.erb +3 -1
  44. data/app/views/avo/partials/_global_search.html.erb +2 -2
  45. data/app/views/avo/partials/_logo.html.erb +3 -1
  46. data/app/views/avo/partials/_navbar.html.erb +11 -0
  47. data/app/views/avo/partials/_paginator.html.erb +4 -3
  48. data/app/views/avo/partials/_resource_search.html.erb +1 -1
  49. data/app/views/avo/partials/_sidebar.html.erb +35 -0
  50. data/app/views/avo/partials/_table_header.html.erb +3 -3
  51. data/app/views/avo/partials/_turbo_frame_wrap.html.erb +7 -1
  52. data/app/views/avo/partials/_view_toggle_button.html.erb +22 -16
  53. data/app/views/layouts/avo/application.html.erb +3 -13
  54. data/db/factories.rb +7 -3
  55. data/lib/avo/app.rb +4 -3
  56. data/lib/avo/base_resource.rb +16 -14
  57. data/lib/avo/version.rb +1 -1
  58. data/public/avo-assets/avo.css +405 -224
  59. data/public/avo-assets/avo.js +16 -6
  60. data/public/avo-assets/avo.js.map +2 -2
  61. data/public/avo-assets/fonts/inter-v7-latin-500.eot +0 -0
  62. data/public/avo-assets/fonts/inter-v7-latin-500.svg +351 -0
  63. data/public/avo-assets/fonts/inter-v7-latin-500.ttf +0 -0
  64. data/public/avo-assets/fonts/inter-v7-latin-500.woff +0 -0
  65. data/public/avo-assets/fonts/inter-v7-latin-500.woff2 +0 -0
  66. data/public/avo-assets/fonts/inter-v7-latin-600.eot +0 -0
  67. data/public/avo-assets/fonts/inter-v7-latin-600.svg +351 -0
  68. data/public/avo-assets/fonts/inter-v7-latin-600.ttf +0 -0
  69. data/public/avo-assets/fonts/inter-v7-latin-600.woff +0 -0
  70. data/public/avo-assets/fonts/inter-v7-latin-600.woff2 +0 -0
  71. data/public/avo-assets/fonts/inter-v7-latin-700.eot +0 -0
  72. data/public/avo-assets/fonts/inter-v7-latin-700.svg +352 -0
  73. data/public/avo-assets/fonts/inter-v7-latin-700.ttf +0 -0
  74. data/public/avo-assets/fonts/inter-v7-latin-700.woff +0 -0
  75. data/public/avo-assets/fonts/inter-v7-latin-700.woff2 +0 -0
  76. data/public/avo-assets/fonts/inter-v7-latin-regular.eot +0 -0
  77. data/public/avo-assets/fonts/inter-v7-latin-regular.svg +351 -0
  78. data/public/avo-assets/fonts/inter-v7-latin-regular.ttf +0 -0
  79. data/public/avo-assets/fonts/inter-v7-latin-regular.woff +0 -0
  80. data/public/avo-assets/fonts/inter-v7-latin-regular.woff2 +0 -0
  81. data/public/avo-assets/logo.png +0 -0
  82. metadata +32 -5
  83. data/app/assets/images/avo/logo.png +0 -0
  84. data/app/views/avo/partials/_profile_dropdown.html.erb +0 -25
  85. data/app/views/avo/sidebar/_sidebar.html.erb +0 -33
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::NavigationHeadingComponent < ViewComponent::Base
4
- def initialize(label: nil)
4
+ attr_reader :label
5
+ attr_reader :icon
6
+
7
+ def initialize(label: nil, icon: nil)
5
8
  @label = label
9
+ @icon = icon
6
10
  end
7
11
  end
@@ -1,3 +1,3 @@
1
- <%= active_link_to @path, class: 'text-gray-800 py-2 px-4 block font-normal hover:bg-gray-100 rounded-md mb-1 mx-3 text-sm leading-none', active: @active do %>
2
- <div class="w-4"></div> <%= @label %>
1
+ <%= active_link_to @path, class: 'px-4 block mx-6 leading-none py-3 text-black rounded font-medium hover:bg-blue-50', active: @active do %>
2
+ <%= @label %>
3
3
  <% end %>
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::NavigationLinkComponent < ViewComponent::Base
4
+ attr_reader :label
5
+ attr_reader :path
6
+ attr_reader :active
7
+ attr_reader :size
8
+
4
9
  def initialize(label: nil, path: nil, active: :inclusive, size: :md)
5
10
  @label = label
6
11
  @path = path
@@ -6,10 +6,12 @@
6
6
  <%= 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 %>
7
7
  </div>
8
8
  <% end %>
9
- <div class="text-2xl tracking-normal font-bold text-gray-800 truncate" data-target="title">
9
+
10
+ <div class="text-2xl tracking-normal font-semibold text-gray-800 truncate" data-target="title">
10
11
  <%= @title %>
11
12
  </div>
12
- <div class="text-sm tracking-normal text-gray-600" data-target="description">
13
+
14
+ <div class="text-base tracking-normal font-medium text-gray-600" data-target="description">
13
15
  <%== description %>
14
16
  </div>
15
17
  </div>
@@ -21,7 +23,7 @@
21
23
  </div>
22
24
  </div>
23
25
 
24
- <div class="relative bg-white rounded-xl shadow-xl mb-8 <%= @body_classes %>">
26
+ <div class="relative bg-white rounded-lg shadow mb-8 <%= @body_classes %>">
25
27
  <%= body %>
26
28
  </div>
27
29
 
@@ -0,0 +1,28 @@
1
+ <div class="text-black border-t border-gray-200 p-4 flex">
2
+ <div class="flex-1 flex space-x-4">
3
+ <div class="relative aspect-square w-10 h-10 overflow-hidden rounded">
4
+ <%= image_tag avatar, class: "object-cover min-w-full min-h-full h-full" %>
5
+ </div>
6
+ <div class="flex flex-col">
7
+ <div class="font-medium">
8
+ <%= name %>
9
+ </div>
10
+ <div class="text-xs text-gray-500 uppercase">
11
+ <%= title %>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ <div>
16
+ <% if can_destroy_user? %>
17
+ <a class="flex items-center h-full cursor-pointer">
18
+ <%= helpers.svg 'three-dots' %>
19
+ </a>
20
+
21
+ <%# if main_app.respond_to?(destroy_user_session_path)
22
+ <div class="hidden absolute inset-auto right-0 mr-6 mt-0 py-4 bg-white rounded-xl min-w-[200px] shadow-context" data-toggle-panel-target="panel">
23
+ <%= button_to t('avo.sign_out'), main_app.send(:destroy_user_session_path), method: :delete, form: { "data-turbo" => "false" }, class: "appearance-none bg-white text-left cursor-pointer text-green-600 font-semibold hover:text-white hover:bg-green-500 block px-4 py-1 w-full"
24
+ </div>
25
+ <%# end %>
26
+ <% end %>
27
+ </div>
28
+ </div>
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::SidebarProfileComponent < ViewComponent::Base
4
+ attr_reader :user
5
+
6
+ def initialize(user:)
7
+ @user = user
8
+ end
9
+
10
+ def avatar
11
+ if user.respond_to?(:avatar) && user.avatar.present?
12
+ user.avatar
13
+ else
14
+ ""
15
+ end
16
+ end
17
+
18
+ def name
19
+ if user.respond_to?(:name) && user.name.present?
20
+ user.name
21
+ elsif user.respond_to?(:email) && user.email.present?
22
+ user.email
23
+ else
24
+ "Avo user"
25
+ end
26
+ end
27
+
28
+ def title
29
+ if user.respond_to?(:avo_title) && user.avo_title.present?
30
+ user.avo_title
31
+ else
32
+ ""
33
+ end
34
+ end
35
+
36
+ def destroy_user_session_path
37
+ "destroy_#{Avo.configuration.current_user_resource_name}_session_path".to_sym
38
+ end
39
+
40
+ def can_destroy_user?
41
+ helpers.main_app.respond_to?(destroy_user_session_path)
42
+ end
43
+ end
@@ -1,12 +1,12 @@
1
1
  <div>
2
- <%= render Avo::PanelComponent.new(title: title, description: description, body_classes: 'py-4', data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
2
+ <%= render Avo::PanelComponent.new(title: title, description: description, body_classes: 'py-2', data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
3
3
  <% c.tools do %>
4
4
  <% if can_see_the_actions_button? %>
5
5
  <%= render 'actions' %>
6
6
  <% end %>
7
7
 
8
8
  <% if can_see_the_create_button? %>
9
- <%= a_link create_path, 'data-target': 'create' do %>
9
+ <%= a_link create_path, 'data-target': 'create', color: :primary, variant: :outlined do %>
10
10
  <%= svg 'plus' %> <%= t('avo.create_new_item', item: singular_resource_name.downcase ) %>
11
11
  <% end %>
12
12
  <% end %>
@@ -19,7 +19,7 @@
19
19
  <% end %>
20
20
 
21
21
  <% c.body do %>
22
- <div class="flex justify-between pt-2 pb-2 min-h-16"
22
+ <div class="flex justify-between min-h-16"
23
23
  data-selected-resources-name="<%= @resource.model_key %>"
24
24
  data-selected-resources="[]"
25
25
  >
@@ -27,11 +27,14 @@
27
27
  <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.model_key} if @resource.search_query.present? %>
28
28
  </div>
29
29
  <div class="flex justify-end items-center px-6 space-x-3">
30
- <%= render partial: 'avo/partials/view_toggle_button', locals: { available_view_types: available_view_types, view_type: view_type, turbo_frame: @turbo_frame } if @models.present? %>
31
30
  <%= render 'filters' if @filters.present? %>
31
+
32
+ <%= render partial: 'avo/partials/view_toggle_button', locals: { available_view_types: available_view_types, view_type: view_type, turbo_frame: @turbo_frame } if @models.present? %>
32
33
  </div>
33
34
  </div>
35
+ <% end %>
34
36
 
37
+ <% c.bare_content do %>
35
38
  <% if view_type.to_sym == :table %>
36
39
  <div class="w-full overflow-auto flex flex-col mt-4">
37
40
  <div class="relative flex-1 flex">
@@ -45,13 +48,11 @@
45
48
  <% end %>
46
49
  </div>
47
50
  <% end %>
48
- <% end %>
49
51
 
50
- <% c.bare_content do %>
51
52
  <% if view_type.to_sym == :grid %>
52
53
  <%= render Avo::Index::ResourceGridComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model) %>
53
54
 
54
- <div class="bg-white rounded-xl shadow-xl mt-8 py-6">
55
+ <div class="mt-8 py-6">
55
56
  <%= render partial: 'avo/partials/paginator', locals: { pagy: @pagy, turbo_frame: @turbo_frame || 'none' } %>
56
57
  </div>
57
58
  <% end %>
@@ -24,6 +24,16 @@
24
24
  <%= svg 'arrow-left' %> <%= t('avo.go_back') %>
25
25
  <% end %>
26
26
 
27
+ <%# -- %>
28
+
29
+ <%#= render Avo::ButtonComponent.new(icon: svg('arrow-left'), is_link: true, href: back_path) do %>
30
+ <%#= t('avo.go_back') %>
31
+ <%# end %>
32
+
33
+ <%#= render Avo::ButtonComponent.new(color: 'green', spinner: true, type: :submit, icon: svg('save')) do %>
34
+ <%#= t('avo.save').capitalize %>
35
+ <%# end %>
36
+
27
37
  <% if can_see_the_destroy_button? %>
28
38
  <%= form_with url: helpers.resource_path(model: @resource.model, resource: @resource), method: :delete, html: { 'data-turbo-frame': params[:turbo_frame] } do |form| %>
29
39
  <%= a_button title: t('avo.delete_item', item: @resource.model.model_name.name.downcase).capitalize,
@@ -1,6 +1,6 @@
1
1
  module Avo
2
2
  class ApplicationController < ::ActionController::Base
3
- include Pundit
3
+ include Pundit::Authorization
4
4
  include Pagy::Backend
5
5
  include Avo::ApplicationHelper
6
6
  include Avo::UrlHelpers
@@ -111,13 +111,7 @@ module Avo
111
111
  end
112
112
 
113
113
  def fill_model
114
- # We have to skip filling the the model if this is an attach action
115
- is_attach_action = params[model_param_key].blank? && params[:related_name].present? && params[:fields].present?
116
- # puts ['fill_model->', is_attach_action, model_param_key].inspect
117
-
118
- unless is_attach_action
119
- @model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
120
- end
114
+ @model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
121
115
  end
122
116
 
123
117
  def hydrate_resource
@@ -193,6 +187,18 @@ module Avo
193
187
  query
194
188
  end
195
189
 
190
+ # def authorize_user
191
+ # return if params[:controller] == 'avo/search'
192
+
193
+ # model = record = resource.model
194
+
195
+ # if ['show', 'edit', 'update'].include?(params[:action]) && params[:controller] == 'avo/resources'
196
+ # record = resource
197
+ # end
198
+
199
+ # # AuthorizationService::authorize_action _current_user, record, params[:action] return render_unauthorized unless
200
+ # end
201
+
196
202
  def _authenticate!
197
203
  instance_eval(&Avo.configuration.authenticate)
198
204
  end
@@ -237,9 +243,5 @@ module Avo
237
243
  def on_api_path
238
244
  request.original_url.match?(/.*#{Avo::App.root_path}\/avo_api\/.*/)
239
245
  end
240
-
241
- def model_param_key
242
- @resource.form_scope
243
- end
244
246
  end
245
247
  end
@@ -7,7 +7,6 @@ module Avo
7
7
  before_action :hydrate_resource
8
8
  before_action :set_model, only: [:show, :edit, :destroy, :update]
9
9
  before_action :set_model_to_fill
10
- before_action :fill_model, only: [:create, :update]
11
10
  before_action :authorize_action
12
11
  before_action :reset_pagination_if_filters_changed, only: :index
13
12
  before_action :cache_applied_filters, only: :index
@@ -113,6 +112,7 @@ module Avo
113
112
 
114
113
  def create
115
114
  # model gets instantiated and filled in the fill_model method
115
+ fill_model
116
116
  saved = @model.save
117
117
  @resource.hydrate(model: @model, view: :new, user: _current_user)
118
118
 
@@ -166,6 +166,7 @@ module Avo
166
166
 
167
167
  def update
168
168
  # model gets instantiated and filled in the fill_model method
169
+ fill_model
169
170
  saved = @model.save
170
171
  @resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
171
172
 
@@ -193,6 +194,8 @@ module Avo
193
194
  private
194
195
 
195
196
  def model_params
197
+ model_param_key = @resource.form_scope
198
+
196
199
  request_params = params.require(model_param_key).permit(permitted_params)
197
200
 
198
201
  if @resource.devise_password_optional && request_params[:password].blank? && request_params[:password_confirmation].blank?
@@ -68,14 +68,14 @@ module Avo
68
68
  end
69
69
  end
70
70
 
71
- def button_classes(extra_classes = nil, color: nil, variant: nil, size: :md)
72
- classes = "inline-flex flex-grow-0 items-center text-sm font-bold leading-none fill-current whitespace-nowrap transition duration-100 rounded-lg shadow-xl transform transition duration-100 active:translate-x-px active:translate-y-px cursor-pointer disabled:cursor-not-allowed #{extra_classes}"
71
+ def button_classes(extra_classes = nil, color: nil, variant: nil, size: :md, active: false)
72
+ classes = "inline-flex flex-grow-0 items-center text-sm font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 rounded transform transition duration-100 active:translate-x-px active:translate-y-px cursor-pointer disabled:cursor-not-allowed #{extra_classes}"
73
73
 
74
74
  if color.present?
75
75
  if variant.present? && (variant.to_sym == :outlined)
76
76
  classes += " bg-white border"
77
77
 
78
- classes += " hover:border-#{color}-700 border-#{color}-600 text-#{color}-600 hover:text-#{color}-700 disabled:border-gray-300 disabled:text-gray-600"
78
+ classes += " hover:border-#{color}-700 border-#{color}-500 text-#{color}-600 hover:text-#{color}-700 disabled:border-gray-300 disabled:text-gray-600"
79
79
  else
80
80
  classes += " text-white bg-#{color}-500 hover:bg-#{color}-600 disabled:bg-#{color}-300"
81
81
  end
@@ -88,9 +88,11 @@ module Avo
88
88
  when :xs
89
89
  " p-2 py-1"
90
90
  when :sm
91
- " p-3"
91
+ " py-1 px-4"
92
92
  when :md
93
- " p-4"
93
+ " py-2 px-4"
94
+ when :xl
95
+ " py-3 px-4"
94
96
  else
95
97
  " p-4"
96
98
  end
@@ -47,7 +47,7 @@ module Avo
47
47
 
48
48
  def item_selector_input(floating: false, size: :md)
49
49
  "<input type='checkbox'
50
- class='mx-3 #{"absolute inset-auto left-0 mt-2 z-10 hidden group-hover:block checked:block" if floating} #{size.to_sym == :lg ? "w-5 h-5" : "w-4 h-4"}'
50
+ class='mx-3 rounded #{"absolute inset-auto left-0 mt-2 z-10 hidden group-hover:block checked:block" if floating} #{size.to_sym == :lg ? "w-5 h-5" : "w-4 h-4"}'
51
51
  data-action='input->item-selector#toggle input->item-select-all#update'
52
52
  data-item-select-all-target='itemCheckbox'
53
53
  name='#{t "avo.select_item"}'
@@ -58,7 +58,7 @@ module Avo
58
58
 
59
59
  def item_select_all_input
60
60
  "<input type='checkbox'
61
- class='mx-3 w-4 h-4'
61
+ class='mx-3 rounded w-4 h-4'
62
62
  data-action='input->item-select-all#toggle'
63
63
  data-item-select-all-target='checkbox'
64
64
  name='#{t "avo.select_all"}'
@@ -1,8 +1,14 @@
1
1
  import { Controller } from 'stimulus'
2
2
  import { DateTime } from 'luxon'
3
- import { castBoolean } from '../../helpers/cast_boolean'
4
3
  import flatpickr from 'flatpickr'
5
4
 
5
+ import { castBoolean } from '../../helpers/cast_boolean'
6
+
7
+ // Get the DateTime with the TZ offset applied.
8
+ function universalTimestamp(timestampStr) {
9
+ return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000))
10
+ }
11
+
6
12
  export default class extends Controller {
7
13
  static targets = ['input']
8
14
 
@@ -43,7 +49,9 @@ export default class extends Controller {
43
49
  // this.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
44
50
  options.appTimezone = this.inputTarget.dataset.timezone
45
51
  } else {
46
- currentValue = new Date(this.inputTarget.value)
52
+ // Because the browser treats the date like a timestamp and updates it ot 00:00 hour, when on a western timezone the date will be converted with one day offset.
53
+ // Ex: 2022-01-30 will render as 2022-01-29 on an American timezone
54
+ currentValue = universalTimestamp(this.inputTarget.value)
47
55
  }
48
56
 
49
57
  options.defaultDate = currentValue
@@ -1,9 +1,9 @@
1
1
  import { Controller } from 'stimulus'
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ['panel']
4
+ static targets = ['panel'];
5
5
 
6
- checkbox = {}
6
+ checkbox = {};
7
7
 
8
8
  get actionsPanelPresent() {
9
9
  return this.actionsButtonElement !== null
@@ -17,6 +17,12 @@ export default class extends Controller {
17
17
  }
18
18
  }
19
19
 
20
+ get actionLinks() {
21
+ return document.querySelectorAll(
22
+ '.js-actions-dropdown a[data-actions-picker-target="resourceAction"]',
23
+ )
24
+ }
25
+
20
26
  set currentIds(value) {
21
27
  this.stateHolderElement.dataset.selectedResources = JSON.stringify(value)
22
28
 
@@ -32,8 +38,12 @@ export default class extends Controller {
32
38
  connect() {
33
39
  this.resourceName = this.element.dataset.resourceName
34
40
  this.resourceId = this.element.dataset.resourceId
35
- this.actionsButtonElement = document.querySelector(`[data-actions-dropdown-button="${this.resourceName}"]`)
36
- this.stateHolderElement = document.querySelector(`[data-selected-resources-name="${this.resourceName}"]`)
41
+ this.actionsButtonElement = document.querySelector(
42
+ `[data-actions-dropdown-button="${this.resourceName}"]`,
43
+ )
44
+ this.stateHolderElement = document.querySelector(
45
+ `[data-selected-resources-name="${this.resourceName}"]`,
46
+ )
37
47
  }
38
48
 
39
49
  addToSelected() {
@@ -45,7 +55,9 @@ export default class extends Controller {
45
55
  }
46
56
 
47
57
  removeFromSelected() {
48
- this.currentIds = this.currentIds.filter((item) => item.toString() !== this.resourceId)
58
+ this.currentIds = this.currentIds.filter(
59
+ (item) => item.toString() !== this.resourceId,
60
+ )
49
61
  }
50
62
 
51
63
  toggle(event) {
@@ -59,22 +71,20 @@ export default class extends Controller {
59
71
  }
60
72
 
61
73
  enableResourceActions() {
62
- (document.querySelectorAll('.js-actions-dropdown a[data-actions-picker-target="resourceAction"]'))
63
- .forEach((link) => {
64
- link.classList.add('text-gray-700')
65
- link.classList.remove('text-gray-500')
66
- link.setAttribute('data-href', link.getAttribute('href'))
67
- link.dataset.disabled = false
68
- })
74
+ this.actionLinks.forEach((link) => {
75
+ link.classList.add('text-gray-700')
76
+ link.classList.remove('text-gray-500')
77
+ link.setAttribute('data-href', link.getAttribute('href'))
78
+ link.dataset.disabled = false
79
+ })
69
80
  }
70
81
 
71
82
  disableResourceActions() {
72
- (document.querySelectorAll('.js-actions-dropdown a[data-actions-picker-target="resourceAction"]'))
73
- .forEach((link) => {
74
- link.classList.remove('text-gray-700')
75
- link.classList.add('text-gray-500')
76
- link.setAttribute('href', link.getAttribute('data-href'))
77
- link.dataset.disabled = true
78
- })
83
+ this.actionLinks.forEach((link) => {
84
+ link.classList.remove('text-gray-700')
85
+ link.classList.add('text-gray-500')
86
+ link.setAttribute('href', link.getAttribute('data-href'))
87
+ link.dataset.disabled = true
88
+ })
79
89
  }
80
90
  }
@@ -82,7 +82,9 @@ export default class extends Controller {
82
82
 
83
83
  document.querySelector('.aa-DetachedOverlay').remove()
84
84
 
85
- this.clearButtonTarget.classList.remove('hidden')
85
+ if (this.hasClearButtonTarget) {
86
+ this.clearButtonTarget.classList.remove('hidden')
87
+ }
86
88
  } else {
87
89
  Turbo.visit(item._url, { action: 'advance' })
88
90
  }
@@ -157,7 +159,7 @@ export default class extends Controller {
157
159
  this.buttonTarget.onclick = () => this.showSearchPanel()
158
160
 
159
161
  this.clearValueTargets.forEach((target) => {
160
- if (target.getAttribute('value')) {
162
+ if (target.getAttribute('value') && this.hasClearButtonTarget) {
161
163
  this.clearButtonTarget.classList.remove('hidden')
162
164
  }
163
165
  })
@@ -184,6 +186,9 @@ export default class extends Controller {
184
186
  },
185
187
  })
186
188
 
187
- this.buttonTarget.removeAttribute('disabled')
189
+ // When using search for belongs-to
190
+ if (this.buttonTarget.dataset.shouldBeDisabled !== 'true') {
191
+ this.buttonTarget.removeAttribute('disabled')
192
+ }
188
193
  }
189
194
  }
@@ -1,11 +1,12 @@
1
1
  <% if @actions.count > 0 %>
2
2
  <div class="relative z-40 js-actions-dropdown" data-controller="toggle-panel actions-picker">
3
3
  <%= a_button class: "focus:outline-none",
4
- color: 'sky',
4
+ color: 'primary',
5
+ size: :xl,
5
6
  'data-action': "click->toggle-panel#togglePanel",
6
7
  'data-actions-dropdown-button': @resource.model_key do
7
8
  %>
8
- <%= svg 'arrow-left', class: 'h-4 mr-1 transform -rotate-90' %> <%= t 'avo.actions' %>
9
+ <%= svg 'arrow-circle-right', class: 'h-4 mr-1 transform rotate-90' %> <%= t 'avo.actions' %>
9
10
  <% end %>
10
11
  <div
11
12
  class="absolute block inset-auto right-0 top-full bg-white min-w-300px mt-2 py-4 z-20 shadow-context rounded-xl overflow-hidden hidden"
@@ -26,7 +27,7 @@
26
27
  'data-turbo-frame': 'actions_show',
27
28
  'data-action': 'click->actions-picker#visitAction',
28
29
  'data-actions-picker-target': action.standalone ? 'standaloneAction' : 'resourceAction',
29
- class: "flex items-center w-full py-2 px-4 font-bold text-gray-700 hover:text-white hover:bg-blue-500 #{disabled ? 'text-gray-500' : 'text-gray-700'}",
30
+ class: "flex items-center w-full py-2 px-4 font-semibold text-gray-700 hover:text-white hover:bg-blue-500 #{disabled ? 'text-gray-500' : 'text-gray-700'}",
30
31
  'data-disabled': disabled do %>
31
32
  <%= svg 'play', class: 'h-5 mr-1 inline' %> <%= action.action_name %>
32
33
  <% end %>
@@ -1,7 +1,9 @@
1
1
  <div data-controller="toggle-panel">
2
2
  <div class="relative w-full flex justify-between z-30">
3
3
  <%= a_button class: 'focus:outline-none',
4
- color: 'slate',
4
+ color: 'secondary',
5
+ variant: 'outlined',
6
+ size: :sm,
5
7
  title: t('avo.click_to_reveal_filters'),
6
8
  'data-button': 'resource-filters',
7
9
  'data-action': 'click->toggle-panel#togglePanel',
@@ -1,5 +1,5 @@
1
- <div data-controller="search" class="global-search" data-turbo-remove-before-cache>
2
- <div class="inline-block"
1
+ <div data-controller="search" class="global-search rounded border border-gray-200 pr-4 min-w-[16rem]" data-turbo-remove-before-cache>
2
+ <div class="inline-block text-gray-500"
3
3
  data-search-target="autocomplete"
4
4
  data-search-resource="global"
5
5
  data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>", "placeholder": "<%= I18n.translate 'avo.search.placeholder' %>", "cancel_button": "<%= I18n.translate 'avo.search.cancel_button' %>"}'
@@ -1 +1,3 @@
1
- <%= image_tag '/avo-assets/logo.png', class: 'h-full', title: 'Avo' %>
1
+ <%= link_to root_path, class: 'logo-placeholder h-20 bg-white p-4 flex justify-start' do %>
2
+ <%= image_tag '/avo-assets/logo.png', class: 'h-full', title: 'Avo' %>
3
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <div class="relative bg-white p-2 h-20 w-full flex flex-shrink-0 items-center z-50 px-6 border-b" v-if="layout !== 'blank'">
2
+ <div>
3
+ <%= render partial: "avo/partials/header" %>
4
+ </div>
5
+ <div class="flex-1 flex ml-4 pl-4">
6
+ <%= render partial: "avo/partials/global_search" if ::Avo::App.license.has_with_trial(:global_search) && ::Avo.configuration.feature_enabled?(:global_search) %>
7
+ </div>
8
+ <div class="align-end">
9
+ <% # Notifications %>
10
+ </div>
11
+ </div>
@@ -8,12 +8,10 @@
8
8
  per_page_options = per_page_options.sort.uniq
9
9
  %>
10
10
 
11
- <div class="bg-white px-4 flex items-center justify-between aborder-t aborder-slate-200 sm:px-6 rounded-xl">
11
+ <div class="flex items-center justify-between aborder-t aborder-slate-200 rounded-xl">
12
12
  <div class="hidden sm:flex-2 sm:flex sm:items-center sm:justify-between">
13
13
  <div>
14
14
  <div class="text-sm leading-5 text-slate-600 flex items-center">
15
- <div class="mr-2"><%== pagy_info @pagy %></div>
16
-
17
15
  <div data-controller="per-page">
18
16
  <div class="flex items-center">
19
17
  <%= select_tag 'per_page',
@@ -39,7 +37,10 @@
39
37
  </div>
40
38
  </div>
41
39
  <div class="flex">
40
+
42
41
  <div class="flex-2 sm:flex sm:items-center sm:justify-between">
42
+ <div class="mr-4"><%== pagy_info @pagy %></div>
43
+
43
44
  <%# @todo: add first & last page. make the first and last buttons rounded %>
44
45
  <% if @pagy.pages > 1 %>
45
46
  <%== pagy_nav @pagy %>
@@ -5,5 +5,5 @@
5
5
  data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>"}'
6
6
  >
7
7
  </div>
8
- <div class="hidden relative inline-flex text-gray-400 text-sm border border-gray-300 rounded-full cursor-pointer" data-search-target="button"></div>
8
+ <div class="hidden relative inline-flex text-gray-400 text-sm border border-gray-300 rounded cursor-pointer" data-search-target="button"></div>
9
9
  </div>
@@ -0,0 +1,35 @@
1
+ <div class="application-sidebar flex h-full bg-white text-white w-72 z-50 border-r">
2
+ <div class="flex flex-col w-full">
3
+ <%= render partial: "avo/partials/logo" %>
4
+
5
+ <div class="flex-1 flex flex-col justify-between">
6
+ <div class="space-y-2">
7
+ <div class="w-full">
8
+ <%= render Avo::NavigationLinkComponent.new label: 'Get started', path: avo.root_path, active: :exclusive if Rails.env.development? && Avo.configuration.home_path.nil? %>
9
+ </div>
10
+
11
+ <div class="w-full">
12
+ <%= render Avo::NavigationHeadingComponent.new label: t('avo.resources'), icon: svg('resources-icon') %>
13
+ <% Avo::App.resources_navigation(_current_user).sort_by { |r| r.navigation_label }.each do |resource| %>
14
+ <%= render Avo::NavigationLinkComponent.new label: resource.navigation_label, path: resources_path(resource: resource) %>
15
+ <% end %>
16
+ </div>
17
+
18
+ <div class="w-full">
19
+ <% tools = Avo::App.get_sidebar_partials %>
20
+
21
+ <% if tools.present? %>
22
+ <%= render Avo::NavigationHeadingComponent.new label: t('avo.tools'), icon: svg('tools-icon') %>
23
+
24
+ <% tools.each do |partial| %>
25
+ <%= render partial: "/avo/sidebar/items/#{partial}" %>
26
+ <% end %>
27
+ <% end %>
28
+ </div>
29
+ </div>
30
+
31
+ <%= render_license_warnings %>
32
+ <%= render Avo::SidebarProfileComponent.new user: _current_user %>
33
+ </div>
34
+ </div>
35
+ </div>
@@ -1,5 +1,5 @@
1
1
 
2
- <thead class="bg-slate-100 border-t border-b border-gray-300 pb-1">
2
+ <thead class="bg-white border-b border-gray-200 pb-1">
3
3
  <th>
4
4
  <%== item_select_all_input %>
5
5
  </th>
@@ -28,9 +28,9 @@
28
28
  sort_by = field.id
29
29
  sort_direction = 'desc'
30
30
  end
31
- classes = "text-slate-600 tracking-tight leading-tight text-xs font-semibold"
31
+ classes = "text-gray-500 tracking-tight leading-tight text-sm font-semibold"
32
32
  %>
33
- <th class="text-left uppercase px-3 py-2 whitespace-nowrap">
33
+ <th class="text-left uppercase px-3 py-6 whitespace-nowrap">
34
34
  <% if field.sortable %>
35
35
  <%= link_to params.permit!.merge(sort_by: sort_by, sort_direction: sort_direction), class: "flex items-center #{classes} #{'cursor-pointer' if field.sortable}", 'data-turbo-frame': params[:turbo_frame] do %>
36
36
  <%= field.name %>
@@ -1,3 +1,9 @@
1
1
  <% if name.present? %><turbo-frame id="<%= name %>"><% end %>
2
- <%= yield %>
2
+ <%
3
+ # When rendering the frames the flashed content gets lost.
4
+ # By including the alerts partial, the stimulus will pick them up and display them to the user.
5
+ %>
6
+ <%= render partial: 'avo/partials/alerts'if flash.present? if flash.present? %>
7
+
8
+ <%= yield %>
3
9
  <% if name.present? %></turbo-frame><% end %>