avo 2.30.1 → 2.31.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +9 -9
  4. data/app/assets/svgs/failed_to_load.svg +0 -1
  5. data/app/assets/svgs/grid-empty-state.svg +0 -1
  6. data/app/assets/svgs/table-empty-state.svg +0 -1
  7. data/app/components/avo/actions_component.rb +1 -1
  8. data/app/components/avo/field_wrapper_component.html.erb +2 -2
  9. data/app/components/avo/fields/common/files/controls_component.html.erb +29 -0
  10. data/app/components/avo/fields/common/files/controls_component.rb +19 -0
  11. data/app/components/avo/fields/common/files/list_viewer_component.html.erb +20 -0
  12. data/app/components/avo/fields/common/files/list_viewer_component.rb +41 -0
  13. data/app/components/avo/fields/common/files/view_type/grid_component.html.erb +27 -0
  14. data/app/components/avo/fields/common/{single_file_viewer_component.rb → files/view_type/grid_component.rb} +2 -9
  15. data/app/components/avo/fields/common/files/view_type/list_component.html.erb +22 -0
  16. data/app/components/avo/fields/common/files/view_type/list_component.rb +15 -0
  17. data/app/components/avo/fields/file_field/edit_component.html.erb +1 -1
  18. data/app/components/avo/fields/file_field/show_component.html.erb +1 -1
  19. data/app/components/avo/fields/files_field/edit_component.html.erb +1 -1
  20. data/app/components/avo/fields/files_field/show_component.html.erb +1 -1
  21. data/app/components/avo/fields/has_many_field/show_component.html.erb +1 -1
  22. data/app/components/avo/fields/has_one_field/show_component.html.erb +2 -2
  23. data/app/components/avo/item_switcher_component.html.erb +1 -1
  24. data/app/components/avo/resource_component.rb +1 -1
  25. data/app/components/avo/views/resource_edit_component.html.erb +4 -4
  26. data/app/components/avo/views/resource_index_component.html.erb +3 -3
  27. data/app/components/avo/views/resource_show_component.html.erb +3 -3
  28. data/app/controllers/avo/attachments_controller.rb +15 -8
  29. data/app/helpers/avo/application_helper.rb +8 -0
  30. data/app/views/avo/actions/show.html.erb +2 -2
  31. data/app/views/avo/associations/new.html.erb +2 -2
  32. data/app/views/avo/attachments/destroy.turbo_stream.erb +7 -0
  33. data/app/views/avo/dashboards/show.html.erb +2 -2
  34. data/app/views/avo/debug/index.html.erb +2 -2
  35. data/app/views/avo/home/index.html.erb +1 -1
  36. data/app/views/avo/partials/_view_toggle_button.html.erb +4 -0
  37. data/app/views/avo/private/design.html.erb +2 -2
  38. data/avo.gemspec +1 -1
  39. data/db/factories.rb +14 -0
  40. data/lib/avo/app.rb +1 -1
  41. data/lib/avo/concerns/policy_helpers.rb +31 -0
  42. data/lib/avo/engine.rb +0 -5
  43. data/lib/avo/fields/base_field.rb +4 -2
  44. data/lib/avo/fields/files_field.rb +4 -0
  45. data/lib/avo/filters/base_filter.rb +4 -0
  46. data/lib/avo/version.rb +1 -1
  47. data/lib/generators/avo/tailwindcss/install_generator.rb +4 -1
  48. data/lib/generators/avo/templates/resource_tools/partial.tt +2 -2
  49. data/lib/generators/avo/templates/tailwindcss/Procfile.dev +1 -1
  50. data/lib/generators/avo/templates/tool/view.tt +2 -2
  51. data/public/avo-assets/avo.base.css +25 -22
  52. metadata +14 -8
  53. data/app/components/avo/fields/common/files_list_viewer_component.html.erb +0 -5
  54. data/app/components/avo/fields/common/files_list_viewer_component.rb +0 -8
  55. data/app/components/avo/fields/common/single_file_viewer_component.html.erb +0 -57
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7162a2279663c821bd7bd45d46606874d27cec50facbe95868b5e6e9f7cad108
4
- data.tar.gz: 364cdaa46a18459fb2193334cce6c9a40b18b46e0570eb0a8f769f2c10237059
3
+ metadata.gz: d2d89325380615d00eeac9d1c507409b602452f320e96ce8818830b580cb5ffd
4
+ data.tar.gz: 787b185e5f37182a75dec5935ea5cd0a2d1cd1986981b79ff6393bfc021e649d
5
5
  SHA512:
6
- metadata.gz: 42f77ef3c0fd013c1940ecef19ff1e7d6f15386ce7dc211d9ca2eb3b18c162d31579f589ac20ef82b98f10e8bb309a6b76f63cda580186da594a9826b717c274
7
- data.tar.gz: 4bddfafbec2fad7372daea55f5926ae7e0bddb34984c1c848f926e5112f0fc86edf452573f9bad7e28a22753dfccd2f2fbabce15aec73fd773c3931524255566
6
+ metadata.gz: 02eba26931bf6ea8300df3fc44006ad2390ddb3dd0ca6b474b438bd29953a5203e97f6b301297bc119572a8bef80341b62fcd7cc6c4ecf1dd7b065441ed168ff
7
+ data.tar.gz: fde51151f29f9c2c06d00cbdb0e16be8d594eb72c20669ad54944adb5d4f105098bf5f84b9a3878b3324837a3ea4086f067dc5030151161409f599699b06ae55
data/Gemfile CHANGED
@@ -75,7 +75,7 @@ group :development do
75
75
 
76
76
  gem "htmlbeautifier"
77
77
 
78
- gem "hotwire-livereload", "~> 1.1"
78
+ gem "hotwire-livereload", "~> 1.2"
79
79
 
80
80
  gem "brakeman"
81
81
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (2.30.1)
4
+ avo (2.31.0)
5
5
  actionview (>= 6.0)
6
6
  active_link_to
7
7
  activerecord (>= 6.0)
@@ -13,7 +13,7 @@ PATH
13
13
  meta-tags
14
14
  pagy
15
15
  turbo-rails
16
- view_component
16
+ view_component (>= 2.54.0)
17
17
  zeitwerk (>= 2.6.2)
18
18
 
19
19
  GEM
@@ -196,7 +196,7 @@ GEM
196
196
  highline (2.1.0)
197
197
  hightop (0.3.0)
198
198
  activesupport (>= 5.2)
199
- hotwire-livereload (1.2.2)
199
+ hotwire-livereload (1.2.3)
200
200
  listen (>= 3.0.0)
201
201
  rails (>= 6.0.0)
202
202
  html_tokenizer (0.0.7)
@@ -233,10 +233,10 @@ GEM
233
233
  json (2.6.2)
234
234
  launchy (2.5.2)
235
235
  addressable (~> 2.8)
236
- listen (3.7.1)
236
+ listen (3.8.0)
237
237
  rb-fsevent (~> 0.10, >= 0.10.3)
238
238
  rb-inotify (~> 0.9, >= 0.9.10)
239
- loofah (2.19.1)
239
+ loofah (2.20.0)
240
240
  crass (~> 1.0.2)
241
241
  nokogiri (>= 1.5.9)
242
242
  mail (2.8.1)
@@ -268,7 +268,7 @@ GEM
268
268
  timeout
269
269
  net-smtp (0.3.3)
270
270
  net-protocol
271
- nio4r (2.5.8)
271
+ nio4r (2.5.9)
272
272
  nokogiri (1.14.3)
273
273
  mini_portile2 (~> 2.8.0)
274
274
  racc (~> 1.4)
@@ -331,7 +331,7 @@ GEM
331
331
  activerecord (>= 6.0.4)
332
332
  activesupport (>= 6.0.4)
333
333
  i18n
334
- rb-fsevent (0.11.1)
334
+ rb-fsevent (0.11.2)
335
335
  rb-inotify (0.10.1)
336
336
  ffi (~> 1.0)
337
337
  redis (4.8.1)
@@ -421,7 +421,7 @@ GEM
421
421
  tzinfo (2.0.6)
422
422
  concurrent-ruby (~> 1.0)
423
423
  unicode-display_width (2.4.0)
424
- view_component (2.82.0)
424
+ view_component (3.0.0)
425
425
  activesupport (>= 5.2.0, < 8.0)
426
426
  concurrent-ruby (~> 1.0)
427
427
  method_source (~> 1.0)
@@ -483,7 +483,7 @@ DEPENDENCIES
483
483
  gem-release
484
484
  groupdate
485
485
  hightop
486
- hotwire-livereload (~> 1.1)
486
+ hotwire-livereload (~> 1.2)
487
487
  htmlbeautifier
488
488
  httparty
489
489
  i18n-tasks (~> 1.0.12)
@@ -9,6 +9,5 @@
9
9
  <defs>
10
10
  <stop stop-color="#E3ECFA"/>
11
11
  <stop offset="1" stop-color="#DAE7FF"/>
12
- </linearGradient>
13
12
  </defs>
14
13
  </svg>
@@ -22,7 +22,6 @@
22
22
  <feColorMatrix type="matrix" values="0 0 0 0 0.788235 0 0 0 0 0.803922 0 0 0 0 0.85098 0 0 0 0.349 0"/>
23
23
  <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_577_1090"/>
24
24
  <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_577_1090" result="shape"/>
25
- </filter>
26
25
  <filter id="filter1_d_577_1090" x="38.6401" y="0" width="170.72" height="78.1333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
27
26
  <feFlood flood-opacity="0" result="BackgroundImageFix"/>
28
27
  <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
@@ -51,6 +51,5 @@
51
51
  </filter>
52
52
  <stop stop-color="#E3ECFA"/>
53
53
  <stop offset="1" stop-color="#DAE7FF"/>
54
- </linearGradient>
55
54
  </defs>
56
55
  </svg>
@@ -11,7 +11,7 @@ class Avo::ActionsComponent < ViewComponent::Base
11
11
  @exclude = exclude
12
12
  @color = color
13
13
  @style = style
14
- @label = label || t("avo.actions")
14
+ @label = label || I18n.t("avo.actions")
15
15
  end
16
16
 
17
17
  def render?
@@ -2,7 +2,7 @@
2
2
  class: classes,
3
3
  style: style,
4
4
  data: data do %>
5
- <div class="h-full <% if stacked? %> md:pt-4 <% else %> md:pt-0 md:h-14 <% end %> pt-4 flex self-start items-center <%= @field.get_html(:classes, view: view, element: :label) %> w-48 <% if compact? %> md:w-48 xl:w-64 <% else %> md:w-64 <% end %> px-6 uppercase font-semibold text-gray-500 text-sm" data-slot="label">
5
+ <div class="h-full <% if stacked? %> md:pt-4 <% else %> md:pt-0 md:h-14 <% end %> pt-4 flex self-start items-center flex-shrink-0 <%= @field.get_html(:classes, view: view, element: :label) %> w-48 <% if compact? %> md:w-48 xl:w-64 <% else %> md:w-64 <% end %> px-6 uppercase font-semibold text-gray-500 text-sm" data-slot="label">
6
6
  <% if form.present? %>
7
7
  <%= form.label field.id, label %>
8
8
  <% else %>
@@ -11,7 +11,7 @@
11
11
  <% if on_edit? && field.is_required? %> <span class="text-red-600 ml-1">*</span> <% end %>
12
12
  </div>
13
13
  <div class="flex-1 flex flex-row md:min-h-inherit py-2 <% if stacked? %> pb-4 <% else %><% end %> px-6 <%= @field.get_html(:classes, view: view, element: :content) %>" data-slot="value">
14
- <div class="self-center <% if full_width? || compact? || stacked? %> w-full <% else %> md:w-8/12 <% end %>">
14
+ <div class="self-center w-full <% unless full_width? || compact? || stacked? %> md:w-8/12 <% end %>">
15
15
  <% if on_show? %>
16
16
  <% if field.value.blank? and dash_if_blank %>
17
17
 
@@ -0,0 +1,29 @@
1
+ <div class="flex">
2
+ <% if can_download_file? %>
3
+ <%= a_link Rails.application.routes.url_helpers.rails_blob_path(file, only_path: true, disposition: :attachment),
4
+ icon: 'heroicons/outline/download',
5
+ color: :primary,
6
+ download: true,
7
+ class: 'text-center',
8
+ title: t('avo.download_file'),
9
+ data: { tippy: :tooltip },
10
+ compact: true,
11
+ size: :xs %>
12
+ <% end %>
13
+ </div>
14
+ <div>
15
+ <% if can_delete_file? %>
16
+ <%= a_link destroy_path,
17
+ icon: 'heroicons/outline/trash',
18
+ color: :red,
19
+ compact: true,
20
+ size: :xs,
21
+ class: 'text-center',
22
+ title: t('avo.delete_file', item: file.filename),
23
+ data: {
24
+ turbo_method: :delete,
25
+ turbo_confirm: t('avo.are_you_sure'),
26
+ tippy: :tooltip
27
+ } %>
28
+ <% end %>
29
+ </div>
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Fields::Common::Files::ControlsComponent < ViewComponent::Base
4
+ include Avo::ApplicationHelper
5
+ include Avo::Fields::Concerns::FileAuthorization
6
+
7
+ attr_reader :file, :field, :resource
8
+ delegate :id, to: :field
9
+
10
+ def initialize(field:, file:, resource:)
11
+ @field = field
12
+ @file = file
13
+ @resource = resource
14
+ end
15
+
16
+ def destroy_path
17
+ Avo::Services::URIService.parse(@resource.record_path).append_paths("active_storage_attachments", id, file.id).to_s
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ <%= content_tag :div, class: wrapper_classes do %>
2
+ <%= turbo_frame_tag @field.id do %>
3
+ <% unless @field.hide_view_type_switcher %>
4
+ <div class="justify-self-end flex justify-end items-center space-x-3">
5
+ <%= render partial: 'avo/partials/view_toggle_button',
6
+ locals: {
7
+ available_view_types: available_view_types,
8
+ view_type: view_type,
9
+ turbo_frame: @field.id,
10
+ }
11
+ %>
12
+ </div>
13
+ <% end %>
14
+ <div class="<%= classes %>">
15
+ <% @field.value.attachments.each do |file| %>
16
+ <%= render view_type_component(file) %>
17
+ <% end %>
18
+ </div>
19
+ <% end %>
20
+ <% end %>
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Fields::Common::Files::ListViewerComponent < ViewComponent::Base
4
+ include Turbo::FramesHelper
5
+
6
+ attr_reader :field, :resource
7
+
8
+ def initialize(field:, resource:)
9
+ @field = field
10
+ @resource = resource
11
+ end
12
+
13
+ def classes
14
+ base_classes = "py-4 rounded-xl max-w-full"
15
+
16
+ view_type_classes = if view_type == :list
17
+ "flex flex-col space-y-2"
18
+ else
19
+ "relative grid xs:grid-cols-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-6"
20
+ end
21
+
22
+ "#{base_classes} #{view_type_classes}"
23
+ end
24
+
25
+ def wrapper_classes
26
+ (field.stacked && !field.hide_view_type_switcher) ? "-mt-9" : ""
27
+ end
28
+
29
+ def available_view_types
30
+ [:list, :grid]
31
+ end
32
+
33
+ def view_type_component(file)
34
+ component = "Avo::Fields::Common::Files::ViewType::#{view_type.to_s.capitalize}Component".constantize
35
+ component.new(field: field, resource: resource, file: file)
36
+ end
37
+
38
+ def view_type
39
+ (resource.params.dig(:view_type) || field.view_type).to_sym
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ <div id="<%= dom_id file %>" class="relative min-h-full max-w-full flex-1 flex flex-col justify-between space-y-2">
2
+ <% if file.present? %>
3
+ <div class="flex flex-col justify-between h-full">
4
+ <% if file.representable? && is_image? %>
5
+ <%= image_tag helpers.main_app.url_for(file), class: 'rounded-lg object-cover w-full aspect-video' %>
6
+ <% elsif is_audio? %>
7
+ <%= audio_tag(helpers.main_app.url_for(file), controls: true, preload: false, class: 'w-full') %>
8
+ <% elsif is_video? %>
9
+ <%= video_tag(helpers.main_app.url_for(file), controls: true, preload: false, class: 'w-full') %>
10
+ <% else %>
11
+ <div class="relative flex flex-col justify-evenly items-center px-2 rounded-lg border bg-white border-gray-500 min-h-24">
12
+ <div class="flex flex-col justify-center items-center w-full">
13
+ <%= helpers.svg 'document-text', class: 'h-10 text-gray-600 mb-2' %>
14
+ </div>
15
+ </div>
16
+ <% end %>
17
+ <% if field.display_filename %>
18
+ <span class="text-gray-500 mt-1 text-sm truncate" title="<%= file.filename %>"><%= file.filename %></span>
19
+ <% end %>
20
+ </div>
21
+ <div class="flex space-x-2">
22
+ <%= render Avo::Fields::Common::Files::ControlsComponent.new(field: field, file: file, resource: resource) %>
23
+ </div>
24
+ <% else %>
25
+
26
+ <% end %>
27
+ </div>
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Avo::Fields::Common::SingleFileViewerComponent < ViewComponent::Base
4
- include Avo::ApplicationHelper
5
- include Avo::Fields::Concerns::FileAuthorization
6
-
7
- attr_reader :field
3
+ class Avo::Fields::Common::Files::ViewType::GridComponent < ViewComponent::Base
4
+ attr_reader :field, :resource
8
5
 
9
6
  def initialize(field:, resource:, file: nil)
10
7
  @file = file
@@ -12,10 +9,6 @@ class Avo::Fields::Common::SingleFileViewerComponent < ViewComponent::Base
12
9
  @resource = resource
13
10
  end
14
11
 
15
- def destroy_path
16
- Avo::Services::URIService.parse(@resource.record_path).append_paths("active_storage_attachments", id, file.id).to_s
17
- end
18
-
19
12
  def id
20
13
  field.id
21
14
  end
@@ -0,0 +1,22 @@
1
+ <div id="<%= dom_id file %>" class="relative min-h-full max-w-full flex-1 flex">
2
+ <% if file.present? %>
3
+ <div class="grid grid-cols-6 gap-2 items-center max-w-full w-full">
4
+ <div class="col-span-4 flex-1 flex flex-row items-center text-gray-700 overflow-x-auto mac-styled-scrollbar ">
5
+ <div class="rounded-full bg-slate-100 border border-gray-500 p-1.5 flex items-center justify-center">
6
+ <%= helpers.svg icon_for_file, class: "h-5 text-gray-600" %>
7
+ </div>
8
+ <p class="items-center h-full p-2 flex-shrink-0">
9
+ <%= file.filename %>
10
+ </p>
11
+ </div>
12
+ <div class="text-gray-700 flex-shrink-0 text-sm">
13
+ <%= helpers.number_to_human_size(file.byte_size) %>
14
+ </div>
15
+ <div class="flex space-x-2 justify-end col-span-1 flex-shrink-0">
16
+ <%= render Avo::Fields::Common::Files::ControlsComponent.new(field: field, file: file, resource: resource) %>
17
+ </div>
18
+ </div>
19
+ <% else %>
20
+
21
+ <% end %>
22
+ </div>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Fields::Common::Files::ViewType::ListComponent < Avo::Fields::Common::Files::ViewType::GridComponent
4
+ def icon_for_file
5
+ if is_image?
6
+ "photo"
7
+ elsif is_audio?
8
+ "speaker-wave"
9
+ elsif is_video?
10
+ "video-camera"
11
+ else
12
+ "document"
13
+ end
14
+ end
15
+ end
@@ -1,7 +1,7 @@
1
1
  <%= field_wrapper **field_wrapper_args do %>
2
2
  <% if @field.value.present? %>
3
3
  <div class="mb-2">
4
- <%= render Avo::Fields::Common::SingleFileViewerComponent.new resource: @resource, field: @field %>
4
+ <%= render Avo::Fields::Common::Files::ViewType::GridComponent.new resource: @resource, field: @field %>
5
5
  </div>
6
6
  <% end %>
7
7
 
@@ -1,3 +1,3 @@
1
1
  <%= field_wrapper **field_wrapper_args do %>
2
- <%= render Avo::Fields::Common::SingleFileViewerComponent.new resource: @resource, field: @field %>
2
+ <%= render Avo::Fields::Common::Files::ViewType::GridComponent.new resource: @resource, field: @field %>
3
3
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <%= field_wrapper **field_wrapper_args, full_width: true do %>
2
- <%= render Avo::Fields::Common::FilesListViewerComponent.new(field: @field, resource: @resource) if @field.value.present? %>
2
+ <%= render Avo::Fields::Common::Files::ListViewerComponent.new(field: @field, resource: @resource) if @field.value.present? %>
3
3
 
4
4
  <% if can_upload_file? %>
5
5
  <div class="mt-2">
@@ -1,3 +1,3 @@
1
1
  <%= field_wrapper **field_wrapper_args, full_width: true do %>
2
- <%= render Avo::Fields::Common::FilesListViewerComponent.new(field: @field, resource: @resource) if @field.value.present? %>
2
+ <%= render Avo::Fields::Common::Files::ListViewerComponent.new(field: @field, resource: @resource) if @field.value.present? %>
3
3
  <% end %>
@@ -1,3 +1,3 @@
1
1
  <turbo-frame id="<%= @field.turbo_frame %>" src="<%= @field.frame_url %>" target="_top" class="block">
2
- <%= render(Avo::LoadingComponent.new(title: @field.name)) %>
2
+ <%= render(Avo::LoadingComponent.new(title: @field.plural_name)) %>
3
3
  </turbo-frame>
@@ -4,7 +4,7 @@
4
4
  </turbo-frame>
5
5
  <% else %>
6
6
  <%= render Avo::PanelComponent.new(name: @field.name) do |c| %>
7
- <% c.tools do %>
7
+ <% c.with_tools do %>
8
8
  <% if !@field.is_readonly? && !@field.is_disabled? && can_attach? %>
9
9
  <%= a_link attach_path,
10
10
  icon: 'heroicons/outline/link',
@@ -25,7 +25,7 @@
25
25
  <% end %>
26
26
  <% end %>
27
27
 
28
- <% c.body do %>
28
+ <% c.with_body do %>
29
29
  <div class="py-8 flex justify-center items-center">
30
30
  <%= empty_state by_association: params[:related_name].present? %>
31
31
  </div>
@@ -4,7 +4,7 @@
4
4
  <% end %>
5
5
  <% elsif item.is_panel? %>
6
6
  <%= render Avo::PanelComponent.new(name: item.name, description: item.description, index: index) do |c| %>
7
- <% c.body do %>
7
+ <% c.with_body do %>
8
8
  <div class="divide-y">
9
9
  <% item.visible_items.each_with_index do |field, index| %>
10
10
  <%= render field
@@ -119,7 +119,7 @@ class Avo::ResourceComponent < Avo::BaseComponent
119
119
  end
120
120
 
121
121
  if filtered_fields.present?
122
- filtered_fields.find { |f| f.id == @reflection.name }.readonly
122
+ filtered_fields.find { |f| f.id == @reflection.name }.is_readonly?
123
123
  else
124
124
  false
125
125
  end
@@ -19,7 +19,7 @@
19
19
  <%= render Avo::ReferrerParamsComponent.new back_path: back_path %>
20
20
  <%= content_tag :div, class: 'space-y-12' do %>
21
21
  <%= render Avo::PanelComponent.new(name: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: 0, data: { panel_id: "main" }) do |c| %>
22
- <% c.tools do %>
22
+ <% c.with_tools do %>
23
23
  <%= a_link back_path,
24
24
  style: :text,
25
25
  icon: 'arrow-left' do %>
@@ -56,7 +56,7 @@
56
56
  <%# Extract the main panel and display the fields here. %>
57
57
  <%# This way we'll be able to render the footer buttons under the main fields. %>
58
58
  <% if main_panel.present? %>
59
- <% c.body do %>
59
+ <% c.with_body do %>
60
60
  <div class="divide-y">
61
61
  <% main_panel.items.each_with_index do |field, index| %>
62
62
  <%= render field
@@ -69,12 +69,12 @@
69
69
  <% end %>
70
70
  <% end %>
71
71
  <% if sidebar.present? %>
72
- <% c.sidebar do %>
72
+ <% c.with_sidebar do %>
73
73
  <%= render sidebar_component form: form %>
74
74
  <% end %>
75
75
  <% end %>
76
76
  <% if Avo.configuration.buttons_on_form_footers %>
77
- <% c.footer_tools do %>
77
+ <% c.with_footer_tools do %>
78
78
  <div class="mt-4">
79
79
  <%= a_link back_path,
80
80
  style: :text,
@@ -5,7 +5,7 @@
5
5
  **@resource.stimulus_data_attributes
6
6
  } do %>
7
7
  <%= render Avo::PanelComponent.new(name: title, description: description, data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
8
- <% c.tools do %>
8
+ <% c.with_tools do %>
9
9
  <% if can_attach? %>
10
10
  <%= a_link attach_path,
11
11
  icon: 'heroicons/outline/link',
@@ -29,7 +29,7 @@
29
29
  <% end %>
30
30
  <% end %>
31
31
  <% end %>
32
- <% c.body do %>
32
+ <% c.with_body do %>
33
33
  <div class="flex flex-col xs:flex-row xs:justify-between space-y-2 xs:space-y-0 py-4 <%= 'hidden' if @resource.search_query.nil? && @filters.empty? && available_view_types.count <= 1 %>">
34
34
  <% if show_search_input %>
35
35
  <div class="flex items-center px-4 w-64">
@@ -56,7 +56,7 @@
56
56
  <% end %>
57
57
  <% end %>
58
58
  <% end %>
59
- <% c.bare_content do %>
59
+ <% c.with_bare_content do %>
60
60
  <% if view_type.to_sym == :table %>
61
61
  <% if @models.present? %>
62
62
  <div class="mt-4">
@@ -8,7 +8,7 @@
8
8
  **@resource.stimulus_data_attributes
9
9
  } do %>
10
10
  <%= render Avo::PanelComponent.new(name: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: 0, data: { panel_id: "main" }) do |c| %>
11
- <% c.tools do %>
11
+ <% c.with_tools do %>
12
12
  <% if @resource.has_show_controls? %>
13
13
  <% @resource.render_show_controls.each do |control| %>
14
14
  <% if control.back_button? %>
@@ -155,7 +155,7 @@
155
155
  <% end %>
156
156
  <% end %>
157
157
  <% if main_panel.present? %>
158
- <% c.body do %>
158
+ <% c.with_body do %>
159
159
  <%# the overflow helps with long values %>
160
160
  <div class="divide-y overflow-auto">
161
161
  <% main_panel.items.each_with_index do |field, index| %>
@@ -169,7 +169,7 @@
169
169
  <% end %>
170
170
  <% end %>
171
171
  <% if sidebar.present? %>
172
- <% c.sidebar do %>
172
+ <% c.with_sidebar do %>
173
173
  <%= render sidebar_component %>
174
174
  <% end %>
175
175
  <% end %>
@@ -23,17 +23,24 @@ module Avo
23
23
  end
24
24
 
25
25
  def destroy
26
- raise Avo::NotAuthorizedError.new unless authorized_to :delete
26
+ if authorized_to :delete
27
+ attachment = ActiveStorage::Attachment.find(params[:attachment_id])
27
28
 
28
- attachment = ActiveStorage::Attachment.find(params[:attachment_id])
29
- path = resource_path(model: @model, resource: @resource)
29
+ flash[:notice] = if attachment.present?
30
+ @destroyed = attachment.destroy
30
31
 
31
- if attachment.present?
32
- attachment.destroy
33
-
34
- redirect_to params[:referrer] || path, notice: t("avo.attachment_destroyed")
32
+ t("avo.attachment_destroyed")
33
+ else
34
+ t("avo.failed_to_find_attachment")
35
+ end
35
36
  else
36
- redirect_back fallback_location: path, notice: t("avo.failed_to_find_attachment")
37
+ flash[:notice] = t("avo.not_authorized")
38
+ end
39
+
40
+ respond_to do |format|
41
+ format.turbo_stream do
42
+ render "destroy"
43
+ end
37
44
  end
38
45
  end
39
46
 
@@ -111,6 +111,14 @@ module Avo
111
111
  Avo::Engine.routes.find_script_name(params.permit!.to_h.symbolize_keys)
112
112
  end
113
113
 
114
+ def decode_filter_params(encoded_params)
115
+ Avo::Filters::BaseFilter.decode_filters(encoded_params)
116
+ end
117
+
118
+ def encode_filter_params(filter_params)
119
+ Avo::Filters::BaseFilter.encode_filters(filter_params)
120
+ end
121
+
114
122
  private
115
123
 
116
124
  # Taken from the original library
@@ -14,7 +14,7 @@
14
14
  data: @action.class.form_data_attributes do |form|
15
15
  %>
16
16
  <%= render Avo::ModalComponent.new do |c| %>
17
- <% c.heading do %>
17
+ <% c.with_heading do %>
18
18
  <%= @action.action_name %>
19
19
  <% end %>
20
20
  <div class="flex-1 flex">
@@ -33,7 +33,7 @@
33
33
  <% end %>
34
34
  </div>
35
35
  <% end %>
36
- <% c.controls do %>
36
+ <% c.with_controls do %>
37
37
  <%= a_button type: :button,
38
38
  data: { action: 'click->modal#close' },
39
39
  size: :sm,
@@ -11,7 +11,7 @@
11
11
  'turbo-frame': '_top'
12
12
  } do |form| %>
13
13
  <%= render Avo::ModalComponent.new do |c| %>
14
- <% c.heading do %>
14
+ <% c.with_heading do %>
15
15
  <%= t 'avo.choose_item', item: @related_resource.name.downcase %>
16
16
  <% end %>
17
17
 
@@ -39,7 +39,7 @@
39
39
  <% end %>
40
40
  </div>
41
41
 
42
- <% c.controls do %>
42
+ <% c.with_controls do %>
43
43
  <%= a_button 'data-action': 'click->modal#close', size: :sm, style: :outline, color: :gray do %>
44
44
  <%= t('avo.cancel') %>
45
45
  <% end %>
@@ -0,0 +1,7 @@
1
+ <%= turbo_stream.remove dom_id(@destroyed) if @destroyed.present? %>
2
+
3
+ <turbo-stream action="append" target="alerts">
4
+ <template>
5
+ <%= render Avo::FlashAlertsComponent.new flashes: flash %>
6
+ </template>
7
+ </turbo-stream>
@@ -1,5 +1,5 @@
1
1
  <%= render Avo::PanelComponent.new(name: @dashboard.name, description: @dashboard.description) do |c| %>
2
- <% c.bare_content do %>
2
+ <% c.with_bare_content do %>
3
3
  <% if @dashboard.items.present? %>
4
4
  <div class="min-h-24">
5
5
  <%= content_tag(:div, class: "grid gap-4 grid-cols-1 #{@dashboard.classes}") do %>
@@ -23,7 +23,7 @@
23
23
  <div class="w-full h-full">
24
24
  <% if Rails.env.development? %>
25
25
  <%= render Avo::PanelComponent.new do |c| %>
26
- <% c.body do %>
26
+ <% c.with_body do %>
27
27
  <div class="p-6">
28
28
  <%= t 'avo.empty_dashboard_message' %>. <%= link_to 'Docs', 'https://docs.avohq.io/2.0/dashboards.html', target: '_blank' %>
29
29
  </div>
@@ -15,9 +15,9 @@
15
15
  %>
16
16
  <div class="flex flex-col">
17
17
  <%= render Avo::PanelComponent.new(name: 'Debug Avo', description: 'Use this page to debug the Avo license.') do |c| %>
18
- <% c.tools do %>
18
+ <% c.with_tools do %>
19
19
  <% end %>
20
- <% c.bare_content do %>
20
+ <% c.with_bare_content do %>
21
21
  <div class="grid gap-4 sm:grid-cols-3">
22
22
  <div class="relative flex flex-col bg-white rounded shadow-panel p-4 space-y-4 h-full col-span-1">
23
23
  <div class="flex justify-between w-full">
@@ -1,6 +1,6 @@
1
1
  <div class="flex flex-col">
2
2
  <%= render Avo::PanelComponent.new(name: 'Welcome to Avo', description: 'This page is visible only in development. It will be hidden in other environments.') do |c| %>
3
- <% c.body do %>
3
+ <% c.with_body do %>
4
4
  <div class="flex flex-col justify-between py-6 min-h-24">
5
5
  <div class="px-6 space-y-4">
6
6
  <h3>About</h3>
@@ -11,6 +11,10 @@
11
11
  new_icon: 'grid-view-type',
12
12
  translation_key: 'avo.grid_view',
13
13
  },
14
+ list: {
15
+ new_view_type: 'list',
16
+ new_icon: 'queue-list',
17
+ },
14
18
  }
15
19
  %>
16
20
  <div class="flex">
@@ -1,6 +1,6 @@
1
1
  <div class="flex flex-col">
2
2
  <%= render Avo::PanelComponent.new(name: 'Welcome to Avo', description: 'This page is visible only in development. It will be hidden in other environments.') do |c| %>
3
- <% c.tools do %>
3
+ <% c.with_tools do %>
4
4
  <%= a_link('/admin', icon: 'arrow-left', color: :green, style: :primary, is_link: true) do %>
5
5
  Primary
6
6
  <% end %>
@@ -18,7 +18,7 @@
18
18
  <% end %>
19
19
  <% end %>
20
20
 
21
- <% c.body do %>
21
+ <% c.with_body do %>
22
22
  <div class="flex flex-col justify-between py-6 min-h-24">
23
23
  <%= render 'links_and_buttons' %>
24
24
  </div>
data/avo.gemspec CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "zeitwerk", ">= 2.6.2"
39
39
  spec.add_dependency "httparty"
40
40
  spec.add_dependency "active_link_to"
41
- spec.add_dependency "view_component"
41
+ spec.add_dependency "view_component", ">= 2.54.0"
42
42
  spec.add_dependency "turbo-rails"
43
43
  spec.add_dependency "addressable"
44
44
  spec.add_dependency "meta-tags"
data/db/factories.rb CHANGED
@@ -42,6 +42,20 @@ FactoryBot.define do
42
42
  progress { Faker::Number.between(from: 0, to: 100) }
43
43
  end
44
44
 
45
+ trait :with_files do
46
+ after(:create) do |project|
47
+ ["watch.jpg", "dummy-video.mp4"].each do |filename|
48
+ file = Rails.root.join("db", "seed_files", filename)
49
+ project.files.attach(io: file.open, filename: filename)
50
+ end
51
+
52
+ ["dummy-file.txt", "dummy-audio.mp3"].each do |filename|
53
+ file = Avo::Engine.root.join("spec", "dummy", filename)
54
+ project.files.attach(io: file.open, filename: filename)
55
+ end
56
+ end
57
+ end
58
+
45
59
  factory :comment do
46
60
  body { Faker::Lorem.paragraphs(number: rand(4...10)).join(" ") }
47
61
  posted_at { Time.now - rand(10...365).days }
data/lib/avo/app.rb CHANGED
@@ -63,7 +63,7 @@ module Avo
63
63
  if Rails::VERSION::MAJOR === 6
64
64
  ActiveStorage::Current.host = request.base_url
65
65
  elsif Rails::VERSION::MAJOR === 7
66
- ActiveStorage::Current.url_options = request.base_url
66
+ ActiveStorage::Current.url_options = {protocol: request.protocol, host: request.host, port: request.port}
67
67
  end
68
68
  end
69
69
  rescue => exception
@@ -0,0 +1,31 @@
1
+ module Avo
2
+ module Concerns
3
+ module PolicyHelpers
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ def inherit_association_from_policy(association_name, policy_class)
8
+ [:create, :edit, :update, :destroy, :show, :reorder, :act_on].each do |method_action|
9
+ define_policy_method(method_action, association_name, policy_class)
10
+ end
11
+
12
+ define_policy_method("view", association_name, policy_class, "index")
13
+ end
14
+
15
+ private
16
+
17
+ # Define a method for the given action and association name.
18
+ # Call the policy class with the given action passing the user and record.
19
+ # Example:
20
+ # def create_team_members?
21
+ # TeamMemberPolicy.new(user, record).create?
22
+ # end
23
+ def define_policy_method(method_action, association_name, policy_class, policy_action = method_action)
24
+ define_method "#{method_action}_#{association_name}?" do
25
+ policy_class.new(user, record).send "#{policy_action}?"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
data/lib/avo/engine.rb CHANGED
@@ -14,11 +14,6 @@ Gem.loaded_specs["avo"].dependencies.each do |d|
14
14
  end
15
15
  end
16
16
 
17
- # In development we should load the engine so we get the autoload for components
18
- if ENV["RAILS_ENV"] === "development"
19
- require "view_component/engine"
20
- end
21
-
22
17
  module Avo
23
18
  class Engine < ::Rails::Engine
24
19
  isolate_namespace Avo
@@ -40,7 +40,6 @@ module Avo
40
40
  attr_reader :stacked
41
41
 
42
42
  # Private options
43
- attr_reader :updatable
44
43
  attr_reader :computable # if allowed to be computable
45
44
  attr_reader :computed # if block is present
46
45
  attr_reader :computed_value # the value after computation
@@ -86,7 +85,6 @@ module Avo
86
85
 
87
86
  @args = args
88
87
 
89
- @updatable = !readonly
90
88
  @computable = true
91
89
  @computed = block.present?
92
90
  @computed_value = nil
@@ -257,6 +255,10 @@ module Avo
257
255
  !visible_in_reflection?
258
256
  end
259
257
 
258
+ def updatable
259
+ !is_readonly? && visible?
260
+ end
261
+
260
262
  private
261
263
 
262
264
  def model_or_class(model)
@@ -6,6 +6,8 @@ module Avo
6
6
  attr_accessor :direct_upload
7
7
  attr_accessor :accept
8
8
  attr_reader :display_filename
9
+ attr_reader :view_type
10
+ attr_reader :hide_view_type_switcher
9
11
 
10
12
  def initialize(id, **args, &block)
11
13
  super(id, **args, &block)
@@ -15,6 +17,8 @@ module Avo
15
17
  @direct_upload = args[:direct_upload].present? ? args[:direct_upload] : false
16
18
  @accept = args[:accept].present? ? args[:accept] : nil
17
19
  @display_filename = args[:display_filename].nil? ? true : args[:display_filename]
20
+ @view_type = args[:view_type] || :grid
21
+ @hide_view_type_switcher = args[:hide_view_type_switcher]
18
22
  end
19
23
 
20
24
  def view_component_name
@@ -21,6 +21,10 @@ module Avo
21
21
  {}
22
22
  end
23
23
 
24
+ def encode_filters(filter_params)
25
+ Base64.encode64(filter_params.to_json)
26
+ end
27
+
24
28
  def get_empty_message
25
29
  empty_message || I18n.t("avo.no_options_available")
26
30
  end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.30.1" unless const_defined?(:VERSION)
2
+ VERSION = "2.31.0" unless const_defined?(:VERSION)
3
3
  end
@@ -21,7 +21,7 @@ module Generators
21
21
  end
22
22
 
23
23
  if Rails.root.join("Procfile.dev").exist?
24
- append_to_file "Procfile.dev", "avo_css: bin/rails avo:tailwindcss:watch\n"
24
+ append_to_file "Procfile.dev", "avo_css: yarn avo:tailwindcss --watch\n"
25
25
  else
26
26
  say "Add default Procfile.dev"
27
27
  copy_file template_path("Procfile.dev"), "Procfile.dev"
@@ -38,6 +38,9 @@ module Generators
38
38
 
39
39
  say "Adding the CSS asset to the partial"
40
40
  prepend_to_file Rails.root.join("app", "views", "avo", "partials", "_pre_head.html.erb"), "<%= stylesheet_link_tag \"avo.tailwind.css\", media: \"all\" %>"
41
+
42
+ say "Ensure you have the following script in your package.json file.", :yellow
43
+ say %("scripts": { "avo:tailwindcss": "tailwindcss -i ./app/assets/stylesheets/avo.tailwind.css -o ./app/assets/builds/avo.tailwind.css --minify" }), :green
41
44
  end
42
45
 
43
46
  no_tasks do
@@ -1,12 +1,12 @@
1
1
  <div class="flex flex-col">
2
2
  <%%= render Avo::PanelComponent.new(name: "<%= human_name %>") do |c| %>
3
- <%% c.tools do %>
3
+ <%% c.with_tools do %>
4
4
  <%%= a_link('/avo', icon: 'heroicons/solid/academic-cap', color: :primary, style: :primary) do %>
5
5
  Dummy link
6
6
  <%% end %>
7
7
  <%% end %>
8
8
 
9
- <%% c.body do %>
9
+ <%% c.with_body do %>
10
10
  <div class="flex flex-col p-4 min-h-24">
11
11
  <div class="space-y-4">
12
12
  <h3>🪧 This partial is waiting to be updated</h3>
@@ -1,2 +1,2 @@
1
1
  web: bin/rails server -p 3000
2
- avo_css: bin/rails avo:tailwindcss:watch
2
+ avo_css: yarn avo:tailwindcss --watch
@@ -1,10 +1,10 @@
1
1
  <div class="flex flex-col">
2
2
  <%%= render Avo::PanelComponent.new(name: '<%= human_name %>', display_breadcrumbs: true) do |c| %>
3
- <%% c.tools do %>
3
+ <%% c.with_tools do %>
4
4
  <div class="text-sm italic">This is the panels tools section.</div>
5
5
  <%% end %>
6
6
 
7
- <%% c.body do %>
7
+ <%% c.with_body do %>
8
8
  <div class="flex flex-col justify-between py-6 min-h-24">
9
9
  <div class="px-6 space-y-4">
10
10
  <h3>What a nice new tool 👋</h3>
@@ -6386,6 +6386,10 @@ trix-toolbar .trix-button-group:not(:first-child){
6386
6386
  grid-column:1 / -1
6387
6387
  }
6388
6388
 
6389
+ .col-span-4{
6390
+ grid-column:span 4 / span 4
6391
+ }
6392
+
6389
6393
  .float-right{
6390
6394
  float:right
6391
6395
  }
@@ -6595,6 +6599,10 @@ trix-toolbar .trix-button-group:not(:first-child){
6595
6599
  margin-bottom:0.25rem
6596
6600
  }
6597
6601
 
6602
+ .-mt-9{
6603
+ margin-top:-2.25rem
6604
+ }
6605
+
6598
6606
  .block{
6599
6607
  display:block
6600
6608
  }
@@ -6631,6 +6639,10 @@ trix-toolbar .trix-button-group:not(:first-child){
6631
6639
  aspect-ratio:1 / 1
6632
6640
  }
6633
6641
 
6642
+ .aspect-video{
6643
+ aspect-ratio:16 / 9
6644
+ }
6645
+
6634
6646
  .h-36{
6635
6647
  height:9rem
6636
6648
  }
@@ -6711,10 +6723,6 @@ trix-toolbar .trix-button-group:not(:first-child){
6711
6723
  height:2.25rem
6712
6724
  }
6713
6725
 
6714
- .max-h-\[42rem\]{
6715
- max-height:42rem
6716
- }
6717
-
6718
6726
  .max-h-full{
6719
6727
  max-height:100%
6720
6728
  }
@@ -6918,6 +6926,10 @@ trix-toolbar .trix-button-group:not(:first-child){
6918
6926
  grid-template-columns:repeat(1, minmax(0, 1fr))
6919
6927
  }
6920
6928
 
6929
+ .grid-cols-6{
6930
+ grid-template-columns:repeat(6, minmax(0, 1fr))
6931
+ }
6932
+
6921
6933
  .flex-row{
6922
6934
  flex-direction:row
6923
6935
  }
@@ -6998,10 +7010,6 @@ trix-toolbar .trix-button-group:not(:first-child){
6998
7010
  gap:1.5rem
6999
7011
  }
7000
7012
 
7001
- .gap-3{
7002
- gap:0.75rem
7003
- }
7004
-
7005
7013
  .gap-1{
7006
7014
  gap:0.25rem
7007
7015
  }
@@ -7066,12 +7074,6 @@ trix-toolbar .trix-button-group:not(:first-child){
7066
7074
  margin-left:calc(0.75rem * calc(1 - var(--tw-space-x-reverse)))
7067
7075
  }
7068
7076
 
7069
- .space-y-3 > :not([hidden]) ~ :not([hidden]){
7070
- --tw-space-y-reverse:0;
7071
- margin-top:calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
7072
- margin-bottom:calc(0.75rem * var(--tw-space-y-reverse))
7073
- }
7074
-
7075
7077
  .divide-y > :not([hidden]) ~ :not([hidden]){
7076
7078
  --tw-divide-y-reverse:0;
7077
7079
  border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));
@@ -7561,6 +7563,11 @@ trix-toolbar .trix-button-group:not(:first-child){
7561
7563
  background-color:rgb(94 100 110 / var(--tw-bg-opacity))
7562
7564
  }
7563
7565
 
7566
+ .bg-slate-100{
7567
+ --tw-bg-opacity:1;
7568
+ background-color:rgb(241 245 249 / var(--tw-bg-opacity))
7569
+ }
7570
+
7564
7571
  .bg-opacity-25{
7565
7572
  --tw-bg-opacity:0.25
7566
7573
  }
@@ -7601,6 +7608,10 @@ trix-toolbar .trix-button-group:not(:first-child){
7601
7608
  padding:0.25rem
7602
7609
  }
7603
7610
 
7611
+ .p-1\.5{
7612
+ padding:0.375rem
7613
+ }
7614
+
7604
7615
  .px-1{
7605
7616
  padding-left:0.25rem;
7606
7617
  padding-right:0.25rem
@@ -9702,10 +9713,6 @@ trix-editor {
9702
9713
  grid-template-columns:repeat(4, minmax(0, 1fr))
9703
9714
  }
9704
9715
 
9705
- .xl\:grid-cols-3{
9706
- grid-template-columns:repeat(3, minmax(0, 1fr))
9707
- }
9708
-
9709
9716
  .xl\:flex-row{
9710
9717
  flex-direction:row
9711
9718
  }
@@ -9764,8 +9771,4 @@ trix-editor {
9764
9771
  .\32xl\:grid-cols-6{
9765
9772
  grid-template-columns:repeat(6, minmax(0, 1fr))
9766
9773
  }
9767
-
9768
- .\32xl\:grid-cols-4{
9769
- grid-template-columns:repeat(4, minmax(0, 1fr))
9770
- }
9771
9774
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: avo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.30.1
4
+ version: 2.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Marin
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-04-17 00:00:00.000000000 Z
12
+ date: 2023-04-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -101,14 +101,14 @@ dependencies:
101
101
  requirements:
102
102
  - - ">="
103
103
  - !ruby/object:Gem::Version
104
- version: '0'
104
+ version: 2.54.0
105
105
  type: :runtime
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
109
  - - ">="
110
110
  - !ruby/object:Gem::Version
111
- version: '0'
111
+ version: 2.54.0
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: turbo-rails
114
114
  requirement: !ruby/object:Gem::Requirement
@@ -1371,8 +1371,14 @@ files:
1371
1371
  - app/components/avo/fields/common/boolean_check_component.rb
1372
1372
  - app/components/avo/fields/common/boolean_group_component.html.erb
1373
1373
  - app/components/avo/fields/common/boolean_group_component.rb
1374
- - app/components/avo/fields/common/files_list_viewer_component.html.erb
1375
- - app/components/avo/fields/common/files_list_viewer_component.rb
1374
+ - app/components/avo/fields/common/files/controls_component.html.erb
1375
+ - app/components/avo/fields/common/files/controls_component.rb
1376
+ - app/components/avo/fields/common/files/list_viewer_component.html.erb
1377
+ - app/components/avo/fields/common/files/list_viewer_component.rb
1378
+ - app/components/avo/fields/common/files/view_type/grid_component.html.erb
1379
+ - app/components/avo/fields/common/files/view_type/grid_component.rb
1380
+ - app/components/avo/fields/common/files/view_type/list_component.html.erb
1381
+ - app/components/avo/fields/common/files/view_type/list_component.rb
1376
1382
  - app/components/avo/fields/common/gravatar_viewer_component.html.erb
1377
1383
  - app/components/avo/fields/common/gravatar_viewer_component.rb
1378
1384
  - app/components/avo/fields/common/heading_component.html.erb
@@ -1381,8 +1387,6 @@ files:
1381
1387
  - app/components/avo/fields/common/key_value_component.rb
1382
1388
  - app/components/avo/fields/common/progress_bar_component.html.erb
1383
1389
  - app/components/avo/fields/common/progress_bar_component.rb
1384
- - app/components/avo/fields/common/single_file_viewer_component.html.erb
1385
- - app/components/avo/fields/common/single_file_viewer_component.rb
1386
1390
  - app/components/avo/fields/common/status_viewer_component.html.erb
1387
1391
  - app/components/avo/fields/common/status_viewer_component.rb
1388
1392
  - app/components/avo/fields/concerns/item_labels.rb
@@ -1642,6 +1646,7 @@ files:
1642
1646
  - app/views/avo/actions/keep_modal_open.turbo_stream.erb
1643
1647
  - app/views/avo/actions/show.html.erb
1644
1648
  - app/views/avo/associations/new.html.erb
1649
+ - app/views/avo/attachments/destroy.turbo_stream.erb
1645
1650
  - app/views/avo/base/_boolean_filter.html.erb
1646
1651
  - app/views/avo/base/_multiple_select_filter.html.erb
1647
1652
  - app/views/avo/base/_select_filter.html.erb
@@ -1721,6 +1726,7 @@ files:
1721
1726
  - lib/avo/concerns/has_stimulus_controllers.rb
1722
1727
  - lib/avo/concerns/is_resource_item.rb
1723
1728
  - lib/avo/concerns/model_class_constantized.rb
1729
+ - lib/avo/concerns/policy_helpers.rb
1724
1730
  - lib/avo/concerns/visible_in_dashboard.rb
1725
1731
  - lib/avo/concerns/visible_items.rb
1726
1732
  - lib/avo/configuration.rb
@@ -1,5 +0,0 @@
1
- <div class="relative py-3 grid xs:grid-cols-2 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-3 rounded-xl">
2
- <% @field.value.attachments.each do |file| %>
3
- <%= render Avo::Fields::Common::SingleFileViewerComponent.new field: @field, resource: @resource, file: file %>
4
- <% end %>
5
- </div>
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Avo::Fields::Common::FilesListViewerComponent < ViewComponent::Base
4
- def initialize(field:, resource:)
5
- @field = field
6
- @resource = resource
7
- end
8
- end
@@ -1,57 +0,0 @@
1
- <div class="relative min-h-full max-w-full flex-1 flex flex-col justify-between space-y-3">
2
- <% if file.present? %>
3
- <div class="flex flex-col justify-between h-full">
4
- <% if file.representable? && is_image? %>
5
- <div class="max-h-[42rem] h-full flex">
6
- <%= image_tag helpers.main_app.url_for(file), class: 'rounded-lg object-cover' %>
7
- </div>
8
- <% elsif is_audio? %>
9
- <%= audio_tag(helpers.main_app.url_for(file), controls: true, preload: false, class: 'w-full') %>
10
- <% elsif is_video? %>
11
- <%= video_tag(helpers.main_app.url_for(file), controls: true, preload: false, class: 'w-full') %>
12
- <% else %>
13
- <div class="relative flex flex-col justify-evenly items-center px-2 rounded-lg border bg-white border-gray-500 min-h-24">
14
- <div class="flex flex-col justify-center items-center w-full">
15
- <%= helpers.svg 'document-text', class: 'h-10 text-gray-600 mb-2' %>
16
- </div>
17
- </div>
18
- <% end %>
19
- <% if field.display_filename %>
20
- <span class="text-gray-500 mt-2 text-sm truncate" title="<%= file.filename %>"><%= file.filename %></span>
21
- <% end %>
22
- </div>
23
- <div class="flex space-x-2">
24
- <div class="flex">
25
- <% if can_download_file? %>
26
- <%= a_link Rails.application.routes.url_helpers.rails_blob_path(file, only_path: true, disposition: :attachment),
27
- icon: 'heroicons/outline/download',
28
- color: :primary,
29
- download: true,
30
- class: 'text-center',
31
- title: t('avo.download_file'),
32
- data: { tippy: :tooltip },
33
- compact: true,
34
- size: :xs %>
35
- <% end %>
36
- </div>
37
- <div>
38
- <% if can_delete_file? %>
39
- <%= a_link destroy_path,
40
- icon: 'heroicons/outline/trash',
41
- color: :red,
42
- compact: true,
43
- size: :xs,
44
- class: 'text-center',
45
- title: t('avo.delete_file', item: file.filename),
46
- data: {
47
- turbo_method: :delete,
48
- turbo_confirm: t('avo.are_you_sure'),
49
- tippy: :tooltip
50
- } %>
51
- <% end %>
52
- </div>
53
- </div>
54
- <% else %>
55
-
56
- <% end %>
57
- </div>