avo 1.22.3 → 1.24.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +4 -1
  4. data/README.md +6 -0
  5. data/app/assets/svgs/arrow-down.svg +3 -0
  6. data/app/assets/svgs/arrow-up.svg +3 -0
  7. data/app/assets/svgs/download-solid-reversed.svg +3 -0
  8. data/app/assets/svgs/download-solid.svg +3 -0
  9. data/app/assets/svgs/switch-vertical.svg +3 -0
  10. data/app/{views/avo/partials/_alert.html.erb → components/avo/alert_component.html.erb} +0 -0
  11. data/app/components/avo/alert_component.rb +11 -0
  12. data/app/components/avo/alerts_component.html.erb +3 -0
  13. data/app/components/avo/alerts_component.rb +5 -0
  14. data/app/components/avo/base_component.rb +7 -0
  15. data/app/components/avo/index/grid_item_component.html.erb +1 -1
  16. data/app/components/avo/index/ordering/base_component.rb +9 -0
  17. data/app/components/avo/index/ordering/button_component.html.erb +10 -0
  18. data/app/components/avo/index/ordering/button_component.rb +31 -0
  19. data/app/components/avo/index/ordering/buttons_component.html.erb +30 -0
  20. data/app/components/avo/index/ordering/buttons_component.rb +19 -0
  21. data/app/components/avo/index/resource_controls_component.html.erb +11 -4
  22. data/app/components/avo/index/resource_controls_component.rb +6 -1
  23. data/app/components/avo/index/table_row_component.html.erb +1 -1
  24. data/app/components/avo/navigation_link_component.rb +1 -1
  25. data/app/components/avo/resource_component.rb +1 -1
  26. data/app/{views/avo/partials/_turbo_frame_wrap.html.erb → components/avo/turbo_frame_wrapper_component.html.erb} +4 -2
  27. data/app/components/avo/turbo_frame_wrapper_component.rb +9 -0
  28. data/app/controllers/avo/application_controller.rb +14 -1
  29. data/app/controllers/avo/base_controller.rb +14 -2
  30. data/app/helpers/avo/application_helper.rb +1 -1
  31. data/app/helpers/avo/url_helpers.rb +4 -0
  32. data/app/javascript/js/application.js +4 -0
  33. data/app/javascript/js/controllers/modal_controller.js +9 -0
  34. data/app/javascript/js/controllers/search_controller.js +6 -1
  35. data/app/views/avo/actions/show.html.erb +7 -3
  36. data/app/views/layouts/avo/application.html.erb +1 -1
  37. data/config/routes.rb +3 -0
  38. data/lib/avo/base_action.rb +21 -0
  39. data/lib/avo/base_resource.rb +8 -0
  40. data/lib/avo/licensing/h_q.rb +55 -1
  41. data/lib/avo/licensing/pro_license.rb +1 -0
  42. data/lib/avo/version.rb +1 -1
  43. data/public/avo-assets/avo.css +4 -0
  44. data/public/avo-assets/avo.js +7645 -7375
  45. data/public/avo-assets/avo.js.map +3 -3
  46. metadata +19 -5
  47. data/app/views/avo/partials/_alerts.html.erb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0638fa0ab76323be01a732489a1ba96def10bde4897fd4231c7a1f4c41ece424'
4
- data.tar.gz: 394c86583f0219b3f9c5d2cf1017db4348105ae8bc26cfbac1f5ca56e9ace62a
3
+ metadata.gz: 3bd7ab9c0d5e4743874638117c67d87e723a4300ef8342be0c3ef868ce0a07e2
4
+ data.tar.gz: 8229f364c7341342e7ac25a8da2be3fc15d10a1feeeb7c5f8c2a6fc270c4cde6
5
5
  SHA512:
6
- metadata.gz: 292f817a4cc4498a4db69268def16be6ac719e5eee1178deb8cad7a26181aacb73ec5cbd9145fea1a0dc627a52f4bc7b9a3e61e5e6099946148239227b313e4d
7
- data.tar.gz: bad4042ec546cb0c56e209a8c42b59a74a655a5bd8278c7f98baf5025f81d3457b566316fc4f7247d51e68d4add071d6207996e922449106ce3627357e1a2545
6
+ metadata.gz: cd6e21cbe3c211a4fa669970b9de4ead47566712fc6dadbcbce0c08e2d1cbf21cdd73e63e05677cc53cff591fb7420651aa8f8048f2329a760de8458addf2ed0
7
+ data.tar.gz: 98c83f4ea0c66bdb5016c650c1f29fd14f0a98f45a4bd024f5d0285d34bbb8a296d3cce94457443e95f6cff4293cac11155ce13339c6c9d5c630af9119669aaa
data/Gemfile CHANGED
@@ -133,3 +133,5 @@ gem 'friendly_id', '~> 5.4.0'
133
133
  gem 'aws-sdk-s3', require: false
134
134
 
135
135
  gem 'net-smtp', require: false
136
+
137
+ gem 'acts_as_list'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (1.22.3)
4
+ avo (1.24.0)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
@@ -81,6 +81,8 @@ GEM
81
81
  minitest (>= 5.1)
82
82
  tzinfo (~> 2.0)
83
83
  zeitwerk (~> 2.3)
84
+ acts_as_list (1.0.4)
85
+ activerecord (>= 4.2)
84
86
  addressable (2.8.0)
85
87
  public_suffix (>= 2.0.2, < 5.0)
86
88
  ap (0.1.1)
@@ -397,6 +399,7 @@ PLATFORMS
397
399
 
398
400
  DEPENDENCIES
399
401
  active_link_to
402
+ acts_as_list
400
403
  addressable
401
404
  ap
402
405
  appraisal
data/README.md CHANGED
@@ -73,3 +73,9 @@ Please read the [UPGRADE_GUIDE.MD](https://docs.avohq.io/1.0/upgrade.html)
73
73
 
74
74
 
75
75
  ![Alt](https://repobeats.axiom.co/api/embed/1481a6a259064f02a7936470d12a50802a9c98a4.svg "Repobeats analytics image")
76
+
77
+ # Shoutouts
78
+
79
+ [Get a box of waffles and some of the best app monitoring from Appsignal](https://appsignal.com/r/93dbe69bfb) 🧇
80
+
81
+ [Get $100 in credits from Digital Ocean](https://www.digitalocean.com/?refcode=efc1fe881d74&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge) 💸
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
2
+ <path stroke-linecap="round" stroke-linejoin="round" d="M19 14l-7 7m0 0l-7-7m7 7V3" />
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
2
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5 10l7-7m0 0l7 7m-7-7v18" />
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" transform="rotate(-180)">
2
+ <path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
2
+ <path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
2
+ <path stroke-linecap="round" stroke-linejoin="round" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" />
3
+ </svg>
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::AlertComponent < ViewComponent::Base
4
+ attr_reader :type
5
+ attr_reader :message
6
+
7
+ def initialize(type, message)
8
+ @type = type
9
+ @message = message
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ <% helpers.flash.each do |type, message| %>
2
+ <%= render Avo::AlertComponent.new type.to_sym, message %>
3
+ <% end %>
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::AlertsComponent < ViewComponent::Base
4
+
5
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::BaseComponent < ViewComponent::Base
4
+ def has_with_trial(ability)
5
+ ::Avo::App.license.has_with_trial(ability)
6
+ end
7
+ end
@@ -32,7 +32,7 @@
32
32
  </div>
33
33
  </div>
34
34
  <div class="w-full place-self-end">
35
- <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model)) %>
35
+ <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, view_type: :grid)) %>
36
36
  </div>
37
37
  </div>
38
38
  </div>
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Index::Ordering::BaseComponent < ViewComponent::Base
4
+ private
5
+
6
+ def order_actions
7
+ @resource.class.order_actions
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ <%= button_to order_path(direction: direction, referrer: request.fullpath),
2
+ title: t("avo.order.#{direction}").capitalize,
3
+ method: :patch,
4
+ class: 'flex items-center',
5
+ data: {
6
+ target: "order:#{direction}",
7
+ 'tippy': 'tooltip',
8
+ } do %>
9
+ <%= helpers.svg(svg, class: 'text-gray-400 h-6 hover:text-gray-600') %>
10
+ <% end %>
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Index::Ordering::ButtonComponent < Avo::Index::Ordering::BaseComponent
4
+ attr_reader :resource
5
+ attr_reader :direction
6
+ attr_reader :svg
7
+
8
+ def initialize(resource: nil, direction: nil, svg: nil)
9
+ @resource = resource
10
+ @direction = direction
11
+ @svg = svg
12
+ end
13
+
14
+ def render?
15
+ order_actions[direction].present?
16
+ end
17
+
18
+ def order_path(args)
19
+ path = "#{::Avo::App.root_path}/resources/#{resource.route_key}/#{resource.model.id}/order"
20
+
21
+ if args.present?
22
+ string_args = args.map do |key, value|
23
+ "#{key}=#{value}"
24
+ end.join('&')
25
+
26
+ path = "#{path}?#{string_args}"
27
+ end
28
+
29
+ path
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ <div class="flex items-center justify-center">
2
+ <% unless always_visible? %>
3
+ <div class="popover inline-block"
4
+ data-controller="popover"
5
+ data-popover-translate-x="-100%"
6
+ data-popover-translate-y="-32px"
7
+ data-action="mouseover->popover#mouseOver mouseout->popover#mouseOut"
8
+ >
9
+ <%= button_tag nil,
10
+ title: t('avo.order.reorder_record').capitalize,
11
+ class: 'flex items-center',
12
+ data: {
13
+ 'tippy': 'tooltip',
14
+ } do
15
+ %>
16
+ <%= helpers.svg('switch-vertical', class: 'text-gray-400 h-6 hover:text-gray-600') %>
17
+ <% end %>
18
+ <% end %>
19
+ <% if always_visible? %>
20
+ <div class="flex max-w-xs rounded">
21
+ <% else %>
22
+ <div class="flex hidden absolute max-w-xs bg-white rounded p-2 z-40" data-popover-target="content">
23
+ <% end %>
24
+ <%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, direction: :higher, svg: 'arrow-up' %>
25
+ <%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, direction: :lower, svg: 'arrow-down' %>
26
+ <%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, direction: :to_top, svg: 'download-solid-reversed' %>
27
+ <%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, direction: :to_bottom, svg: 'download-solid' %>
28
+ </div>
29
+ </div>
30
+ </div>
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Index::Ordering::ButtonsComponent < Avo::Index::Ordering::BaseComponent
4
+ def initialize(resource: nil)
5
+ @resource = resource
6
+ end
7
+
8
+ def render?
9
+ can_order_any?
10
+ end
11
+
12
+ def can_order_any?
13
+ order_actions.present?
14
+ end
15
+
16
+ def always_visible?
17
+ @resource.class.ordering[:always_visible]
18
+ end
19
+ end
@@ -1,14 +1,18 @@
1
- <div class="space-x-2 flex flex-row justify-around w-full">
1
+ <div class="space-x-2 flex flex-row justify-around items-center w-full">
2
+ <% if view_is_table? && has_with_trial(:resource_ordering) %>
3
+ <%= render Avo::Index::Ordering::ButtonsComponent.new resource: @resource %>
4
+ <% end %>
5
+
2
6
  <% if can_view? %>
3
7
  <%= link_to helpers.svg('eye', class: 'text-gray-400 h-6 hover:text-gray-600'),
4
8
  show_path,
5
- title: t('avo.view_item', item: singular_resource_name).capitalize,
9
+ title: t('avo.view_item', item: singular_resource_name).capitalize,
6
10
  data: {
7
11
  target: 'control:view',
8
12
  control: :show,
9
13
  'tippy': 'tooltip',
10
14
  }
11
- %>
15
+ %>
12
16
  <% end %>
13
17
 
14
18
  <% if can_edit? %>
@@ -45,7 +49,10 @@
45
49
  <% end %>
46
50
 
47
51
  <% if can_delete? %>
48
- <%= form_with url: helpers.resource_path(model: @resource.model, resource: @resource), method: :delete, html: {
52
+ <%= form_with url: helpers.resource_path(model: @resource.model, resource: @resource),
53
+ method: :delete,
54
+ class: 'flex items-center',
55
+ html: {
49
56
  'data-turbo-frame': params[:turbo_frame]
50
57
  } do |form| %>
51
58
  <%= form.button helpers.svg('trash', class: 'text-gray-400 h-6 hover:text-gray-600'),
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
4
- def initialize(resource: nil, reflection: nil, parent_model: nil)
4
+ def initialize(resource: nil, reflection: nil, parent_model: nil, view_type: :table)
5
5
  @resource = resource
6
6
  @reflection = reflection
7
7
  @parent_model = parent_model
8
+ @view_type = view_type
8
9
  end
9
10
 
10
11
  def can_detach?
@@ -69,4 +70,8 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
69
70
  def is_has_many_association
70
71
  @reflection.is_a?(::ActiveRecord::Reflection::HasManyReflection) || @reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection)
71
72
  end
73
+
74
+ def view_is_table?
75
+ @view_type == :table
76
+ end
72
77
  end
@@ -12,7 +12,7 @@
12
12
  <% end %>
13
13
  <td class="text-right whitespace-nowrap px-2">
14
14
  <div class="flex items-center justify-end flex-grow-0 h-full w-full">
15
- <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model)) %>
15
+ <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, view_type: :table)) %>
16
16
  </div>
17
17
  </td>
18
18
  </tr>
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::NavigationLinkComponent < ViewComponent::Base
4
- def initialize(label: nil, path: nil, active: :inclusive, size: :md, target: "_self")
4
+ def initialize(label: nil, path: nil, active: :inclusive, size: :md, target: nil)
5
5
  @label = label
6
6
  @path = path
7
7
  @active = active
@@ -1,4 +1,4 @@
1
- class Avo::ResourceComponent < ViewComponent::Base
1
+ class Avo::ResourceComponent < Avo::BaseComponent
2
2
  def can_create?
3
3
  return authorize_association_for(:create) if @reflection.present?
4
4
 
@@ -3,7 +3,9 @@
3
3
  # When rendering the frames the flashed content gets lost.
4
4
  # By including the alerts partial, the stimulus will pick them up and display them to the user.
5
5
  %>
6
- <%= render partial: 'avo/partials/alerts' if flash.present? && name.present? %>
6
+ <% if helpers.turbo_frame_request? %>
7
+ <%= render Avo::AlertsComponent.new if helpers.flash.present? && name.present? %>
8
+ <% end %>
7
9
 
8
- <%= yield %>
10
+ <%= content %>
9
11
  <% if name.present? %></turbo-frame><% end %>
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::TurboFrameWrapperComponent < ViewComponent::Base
4
+ attr_reader :name
5
+
6
+ def initialize(name = nil)
7
+ @name = name
8
+ end
9
+ end
@@ -13,6 +13,7 @@ module Avo
13
13
  protect_from_forgery with: :exception
14
14
  before_action :init_app
15
15
  before_action :check_avo_license
16
+ before_action :set_locale
16
17
  before_action :set_authorization
17
18
  before_action :_authenticate!
18
19
  before_action :set_container_classes
@@ -23,7 +24,7 @@ module Avo
23
24
  rescue_from Pundit::NotAuthorizedError, with: :render_unauthorized
24
25
  rescue_from ActiveRecord::RecordInvalid, with: :exception_logger
25
26
 
26
- helper_method :_current_user, :resources_path, :resource_path, :new_resource_path, :edit_resource_path, :resource_attach_path, :resource_detach_path, :related_resources_path
27
+ helper_method :_current_user, :resources_path, :resource_path, :new_resource_path, :edit_resource_path, :resource_attach_path, :resource_detach_path, :related_resources_path, :turbo_frame_request?
27
28
  add_flash_types :info, :warning, :success, :error
28
29
 
29
30
  def init_app
@@ -78,6 +79,12 @@ module Avo
78
79
  instance_eval(&Avo.configuration.context)
79
80
  end
80
81
 
82
+ # This is coming from Turbo::Frames::FrameRequest module.
83
+ # Exposing it as public method
84
+ def turbo_frame_request?
85
+ super
86
+ end
87
+
81
88
  private
82
89
 
83
90
  def set_resource_name
@@ -245,5 +252,11 @@ module Avo
245
252
  def model_param_key
246
253
  @resource.form_scope
247
254
  end
255
+
256
+ def set_locale
257
+ I18n.locale = params[:locale] || I18n.default_locale
258
+
259
+ I18n.default_locale = I18n.locale
260
+ end
248
261
  end
249
262
  end
@@ -5,7 +5,7 @@ module Avo
5
5
  before_action :set_resource_name
6
6
  before_action :set_resource
7
7
  before_action :hydrate_resource
8
- before_action :set_model, only: [:show, :edit, :destroy, :update]
8
+ before_action :set_model, only: [:show, :edit, :destroy, :update, :order]
9
9
  before_action :set_model_to_fill
10
10
  before_action :fill_model, only: [:create, :update]
11
11
  before_action :authorize_action
@@ -84,7 +84,6 @@ module Avo
84
84
  def new
85
85
  @model = @resource.model_class.new
86
86
  @resource = @resource.hydrate(model: @model, view: :new, user: _current_user)
87
- # abort @model.course.inspect
88
87
 
89
88
  @page_title = @resource.default_panel_name
90
89
  add_breadcrumb resource_name.humanize, resources_path(resource: @resource)
@@ -191,6 +190,19 @@ module Avo
191
190
  end
192
191
  end
193
192
 
193
+ def order
194
+ direction = params[:direction].to_sym
195
+ order_actions = @resource.class.order_actions
196
+
197
+ if direction.present? && order_actions[direction].present?
198
+ order_actions[direction].call(@model)
199
+ end
200
+
201
+ respond_to do |format|
202
+ format.html { redirect_to params[:referrer] || resources_path(resource: @resource) }
203
+ end
204
+ end
205
+
194
206
  private
195
207
 
196
208
  def model_params
@@ -21,7 +21,7 @@ module Avo
21
21
  end
22
22
 
23
23
  def turbo_frame_wrap(name, &block)
24
- render layout: "avo/partials/turbo_frame_wrap", locals: {name: name} do
24
+ render Avo::TurboFrameWrapperComponent.new name do
25
25
  capture(&block)
26
26
  end
27
27
  end
@@ -73,5 +73,9 @@ module Avo
73
73
 
74
74
  avo.resources_associations_index_path(@parent_resource.model_class.model_name.route_key, @parent_resource.model.id, **existing_params, **args )
75
75
  end
76
+
77
+ def order_up_resource_path(model:, resource:, **args)
78
+ avo.send :"order_up_resources_#{resource.singular_model_key}_path", model, **args
79
+ end
76
80
  end
77
81
  end
@@ -1,4 +1,5 @@
1
1
  import { Application } from '@hotwired/stimulus'
2
+ import { Popover } from 'tailwindcss-stimulus-components'
2
3
 
3
4
  const application = Application.start()
4
5
 
@@ -6,4 +7,7 @@ const application = Application.start()
6
7
  application.debug = false
7
8
  window.Stimulus = application
8
9
 
10
+ // Register stimulus-components controller
11
+ application.register('popover', Popover)
12
+
9
13
  export { application }
@@ -8,4 +8,13 @@ export default class extends Controller {
8
8
 
9
9
  document.dispatchEvent(new Event('actions-modal:close'))
10
10
  }
11
+
12
+ delayedClose() {
13
+ const vm = this
14
+
15
+ setTimeout(() => {
16
+ vm.modalTarget.remove()
17
+ document.dispatchEvent(new Event('actions-modal:close'))
18
+ }, 500)
19
+ }
11
20
  }
@@ -177,11 +177,16 @@ export default class extends Controller {
177
177
  openOnFocus: true,
178
178
  detachedMediaQuery: '',
179
179
  getSources: ({ query }) => {
180
+ document.body.classList.add('search-loading')
180
181
  const endpoint = that.searchUrl(query)
181
182
 
182
183
  return that
183
184
  .debouncedFetch(endpoint)
184
- .then((response) => response.json())
185
+ .then((response) => {
186
+ document.body.classList.remove('search-loading')
187
+
188
+ return response.json()
189
+ })
185
190
  .then((data) => Object.keys(data).map((resourceName) => that.addSource(resourceName, data[resourceName])))
186
191
  },
187
192
  })
@@ -7,7 +7,11 @@
7
7
  data-resource-id="<%= params[:id] %>"
8
8
  class="hidden text-slate-800"
9
9
  >
10
- <%= form_with model: @model, scope: 'fields', url: "#{@resource.records_path}/actions/#{@action.param_id}", data: {'turbo-frame': '_top', 'action-target': 'form'} do |form| %>
10
+ <%= form_with model: @model,
11
+ scope: 'fields',
12
+ url: "#{@resource.records_path}/actions/#{@action.param_id}",
13
+ data: @action.class.form_data_attributes do |form|
14
+ %>
11
15
  <%= render Avo::ModalComponent.new do |c| %>
12
16
  <% c.heading do %>
13
17
  <%= @action.action_name %>
@@ -26,8 +30,8 @@
26
30
  <% end %>
27
31
 
28
32
  <% c.controls do %>
29
- <%= a_button @action.cancel_button_label, 'data-action': 'click->modal#close', size: :sm %>
30
- <%= a_button @action.confirm_button_label, type: :submit, color: :green, size: :sm %>
33
+ <%= a_button @action.cancel_button_label, data: { action: 'click->modal#close' }, size: :sm %>
34
+ <%= a_button @action.confirm_button_label, type: :submit, color: :green, size: :sm, data: @action.class.submit_button_data_attributes %>
31
35
  <% end %>
32
36
  <% end %>
33
37
  <% end %>
@@ -51,7 +51,7 @@
51
51
  <%= turbo_frame_tag 'destroy_attachment_form' %>
52
52
 
53
53
  <%= turbo_frame_tag 'alerts' do %>
54
- <%= render partial: 'avo/partials/alerts' %>
54
+ <%= render Avo::AlertsComponent.new %>
55
55
  <% end %>
56
56
  </div>
57
57
 
data/config/routes.rb CHANGED
@@ -17,6 +17,9 @@ Avo::Engine.routes.draw do
17
17
  get "/:resource_name/:id/active_storage_attachments/:attachment_name/:signed_attachment_id", to: "attachments#show"
18
18
  delete "/:resource_name/:id/active_storage_attachments/:attachment_name/:signed_attachment_id", to: "attachments#destroy"
19
19
 
20
+ # Ordering
21
+ patch "/:resource_name/:id/order", to: "resources#order"
22
+
20
23
  # Actions
21
24
  get "/:resource_name(/:id)/actions/:action_id", to: "actions#show"
22
25
  post "/:resource_name(/:id)/actions/:action_id", to: "actions#handle"
@@ -15,6 +15,7 @@ module Avo
15
15
  class_attribute :fields
16
16
  class_attribute :standalone, default: false
17
17
  class_attribute :visible
18
+ class_attribute :may_download_file, default: false
18
19
 
19
20
  attr_accessor :response
20
21
  attr_accessor :model
@@ -22,6 +23,26 @@ module Avo
22
23
  attr_accessor :user
23
24
  attr_accessor :fields_loader
24
25
 
26
+ class << self
27
+ def form_data_attributes
28
+ # We can't respond with a file download from Turbo se we disable it on the form
29
+ if may_download_file
30
+ { 'turbo': false }
31
+ else
32
+ { 'turbo-frame': '_top', 'action-target': 'form' }
33
+ end
34
+ end
35
+
36
+ # We can't respond with a file download from Turbo se we disable close the modal manually after a while (it's a hack, we know)
37
+ def submit_button_data_attributes
38
+ if may_download_file
39
+ { action: 'click->modal#delayedClose' }
40
+ else
41
+ {}
42
+ end
43
+ end
44
+ end
45
+
25
46
  def action_name
26
47
  return name if name.present?
27
48
 
@@ -36,6 +36,7 @@ module Avo
36
36
  class_attribute :unscoped_queries_on_index, default: false
37
37
  class_attribute :resolve_query_scope
38
38
  class_attribute :resolve_find_scope
39
+ class_attribute :ordering
39
40
 
40
41
  class << self
41
42
  def grid(&block)
@@ -80,6 +81,12 @@ module Avo
80
81
  def authorization
81
82
  Avo::Services::AuthorizationService.new Avo::App.current_user
82
83
  end
84
+
85
+ def order_actions
86
+ return {} if ordering.blank?
87
+
88
+ ordering.dig(:actions) || {}
89
+ end
83
90
  end
84
91
 
85
92
  def initialize
@@ -135,6 +142,7 @@ module Avo
135
142
  # we're matching the reflection inverse_of foriegn key with the field's foreign_key
136
143
  if field.is_a?(Avo::Fields::BelongsToField)
137
144
  if field.respond_to?(:foreign_key) &&
145
+ reflection.inverse_of.present? &&
138
146
  reflection.inverse_of.foreign_key == field.foreign_key
139
147
  is_valid = false
140
148
  end