avo 2.9.2.pre1 → 2.10.0

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

Potentially problematic release.


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

Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -7
  3. data/README.md +4 -0
  4. data/app/assets/stylesheets/css/buttons.css +4 -1
  5. data/app/components/avo/actions_component.rb +6 -2
  6. data/app/components/avo/base_component.rb +2 -0
  7. data/app/components/avo/button_component.rb +3 -1
  8. data/app/components/avo/fields/common/key_value_component.html.erb +2 -2
  9. data/app/components/avo/fields/common/single_file_viewer_component.rb +1 -1
  10. data/app/components/avo/fields/date_field/edit_component.html.erb +1 -0
  11. data/app/components/avo/fields/date_time_field/edit_component.html.erb +10 -25
  12. data/app/components/avo/fields/date_time_field/index_component.html.erb +1 -9
  13. data/app/components/avo/fields/date_time_field/show_component.html.erb +1 -9
  14. data/app/components/avo/fields/edit_component.rb +5 -0
  15. data/app/components/avo/fields/show_component.rb +1 -1
  16. data/app/components/avo/index/ordering/button_component.rb +2 -12
  17. data/app/components/avo/index/resource_controls_component.html.erb +2 -2
  18. data/app/components/avo/index/resource_controls_component.rb +5 -1
  19. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  20. data/app/components/avo/index/table_row_component.html.erb +1 -1
  21. data/app/components/avo/item_switcher_component.html.erb +19 -0
  22. data/app/components/avo/item_switcher_component.rb +45 -0
  23. data/app/components/avo/panel_component.html.erb +23 -24
  24. data/app/components/avo/panel_component.rb +8 -5
  25. data/app/components/avo/tab_group_component.html.erb +53 -0
  26. data/app/components/avo/tab_group_component.rb +51 -0
  27. data/app/components/avo/tab_switcher_component.html.erb +21 -0
  28. data/app/components/avo/tab_switcher_component.rb +86 -0
  29. data/app/components/avo/views/resource_edit_component.html.erb +34 -56
  30. data/app/components/avo/views/resource_edit_component.rb +11 -1
  31. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  32. data/app/components/avo/views/resource_index_component.rb +3 -3
  33. data/app/components/avo/views/resource_show_component.html.erb +58 -89
  34. data/app/components/avo/views/resource_show_component.rb +2 -2
  35. data/app/controllers/avo/actions_controller.rb +1 -1
  36. data/app/controllers/avo/application_controller.rb +20 -3
  37. data/app/helpers/avo/application_helper.rb +0 -6
  38. data/app/helpers/avo/url_helpers.rb +1 -1
  39. data/app/javascript/avo.js +5 -1
  40. data/app/javascript/js/controllers/fields/date_field_controller.js +25 -87
  41. data/app/javascript/js/controllers/loading_button_controller.js +25 -21
  42. data/app/javascript/js/controllers/tabs_controller.js +86 -0
  43. data/app/javascript/js/controllers.js +2 -0
  44. data/app/views/avo/base/index.html.erb +1 -1
  45. data/app/views/avo/base/show.html.erb +1 -1
  46. data/app/views/avo/cards/show.html.erb +1 -1
  47. data/app/views/avo/debug/index.html.erb +1 -1
  48. data/app/views/avo/home/_actions.html.erb +1 -1
  49. data/app/views/avo/home/_dashboards.html.erb +19 -0
  50. data/app/views/avo/home/_filters.html.erb +1 -1
  51. data/app/views/avo/home/_resources.html.erb +1 -1
  52. data/app/views/avo/home/failed_to_load.html.erb +1 -1
  53. data/app/views/avo/home/index.html.erb +14 -2
  54. data/app/views/avo/partials/_javascript.html.erb +1 -1
  55. data/app/views/avo/partials/_tabs_toggle.html.erb +20 -0
  56. data/app/views/avo/private/design.html.erb +1 -1
  57. data/config/routes.rb +1 -1
  58. data/db/migrate/20210421064037_add_color_to_teams.rb +5 -0
  59. data/db/migrate/20210423075924_add_progress_to_projects.rb +5 -0
  60. data/db/migrate/20210525143134_add_slug_to_users.rb +6 -0
  61. data/lib/avo/app.rb +11 -4
  62. data/lib/avo/base_action.rb +2 -19
  63. data/lib/avo/base_card.rb +1 -7
  64. data/lib/avo/base_resource.rb +1 -95
  65. data/lib/avo/base_resource_tool.rb +3 -1
  66. data/lib/avo/concerns/handles_field_args.rb +1 -1
  67. data/lib/avo/concerns/has_fields.rb +247 -50
  68. data/lib/avo/concerns/has_html_attributes.rb +1 -1
  69. data/lib/avo/concerns/is_resource_item.rb +36 -0
  70. data/lib/avo/concerns/model_class_constantized.rb +23 -0
  71. data/lib/avo/dashboards/base_dashboard.rb +1 -1
  72. data/lib/avo/dsl/field_parser.rb +83 -0
  73. data/lib/avo/fields/base_field.rb +19 -2
  74. data/lib/avo/fields/date_field.rb +2 -0
  75. data/lib/avo/fields/date_time_field.rb +9 -21
  76. data/lib/avo/fields/field_extensions/visible_in_different_views.rb +18 -1
  77. data/lib/avo/fields/has_base_field.rb +20 -1
  78. data/lib/avo/fields/has_one_field.rb +4 -1
  79. data/lib/avo/grid_collector.rb +6 -3
  80. data/lib/avo/items_holder.rb +68 -0
  81. data/lib/avo/licensing/h_q.rb +10 -0
  82. data/lib/avo/main_panel.rb +3 -0
  83. data/lib/avo/menu/builder.rb +8 -7
  84. data/lib/avo/panel.rb +25 -0
  85. data/lib/avo/panel_builder.rb +23 -0
  86. data/lib/avo/services/uri_service.rb +71 -0
  87. data/lib/avo/tab.rb +78 -0
  88. data/lib/avo/tab_builder.rb +25 -0
  89. data/lib/avo/tab_group.rb +40 -0
  90. data/lib/avo/tab_group_builder.rb +43 -0
  91. data/lib/avo/version.rb +1 -1
  92. data/lib/avo.rb +1 -0
  93. data/lib/generators/avo/templates/locales/avo.fr.yml +115 -0
  94. data/lib/generators/avo/templates/resource/controller.tt +2 -0
  95. data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
  96. data/lib/generators/avo/templates/tool/view.tt +1 -1
  97. data/public/avo-assets/avo.css +27 -3
  98. data/public/avo-assets/avo.js +77 -77
  99. data/public/avo-assets/avo.js.map +3 -3
  100. metadata +28 -13
  101. data/app/assets/builds/action_cable.js +0 -2
  102. data/app/assets/builds/action_cable.js.map +0 -7
  103. data/app/assets/builds/application.js +0 -2
  104. data/app/assets/builds/application.js.map +0 -7
  105. data/app/assets/builds/avo.css +0 -9028
  106. data/app/assets/builds/avo.js +0 -512
  107. data/app/assets/builds/avo.js.map +0 -7
  108. data/app/assets/builds/avo_custom.js +0 -6
  109. data/app/assets/builds/avo_custom.js.map +0 -7
  110. data/lib/avo/concerns/has_tools.rb +0 -47
@@ -0,0 +1,21 @@
1
+ <div class="flex" data-target="tab-switcher">
2
+ <div class="button-group">
3
+ <% visible_items.each do |tab| %>
4
+ <%= a_link tab_path(tab),
5
+ color: selected?(tab) ? :gray : :primary,
6
+ rounded: false,
7
+ size: :sm,
8
+ class: selected?(tab) ? ' bg-gray-100 border-gray-300' : ' z-20',
9
+ title: tab.description,
10
+ data: {
11
+ tippy: tab.description.present? ? 'tooltip' : '',
12
+ control: "view-type-toggle-#{tab.name}",
13
+ selected: selected?(tab),
14
+ action: 'click->tabs#changeTab',
15
+ tabs_id_param: tab.name
16
+ } do %>
17
+ <%= tab.name %>
18
+ <% end %>
19
+ <% end %>
20
+ </div>
21
+ </div>
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::TabSwitcherComponent < Avo::BaseComponent
4
+ include Avo::UrlHelpers
5
+ include Avo::ApplicationHelper
6
+
7
+ attr_reader :active_tab_name
8
+ attr_reader :group
9
+ attr_reader :current_tab
10
+ attr_reader :tabs
11
+ attr_reader :view
12
+
13
+ def initialize(resource:, group:, current_tab:, active_tab_name:, view:)
14
+ @active_tab_name = active_tab_name
15
+ @resource = resource
16
+ @group = group
17
+ @current_tab = current_tab
18
+ @tabs = group.items
19
+ @view = view
20
+ end
21
+
22
+ def tab_path(tab)
23
+ if is_edit?
24
+ helpers.edit_resource_path(resource: @resource, model: @resource.model, keep_query_params: true, active_tab_name: tab.name, tab_turbo_frame: group.turbo_frame_id)
25
+ elsif is_new?
26
+ helpers.new_resource_path(resource: @resource, keep_query_params: true, active_tab_name: tab.name, tab_turbo_frame: group.turbo_frame_id)
27
+ else
28
+ helpers.resource_path(resource: @resource, model: @resource.model, keep_query_params: true, active_tab_name: tab.name, tab_turbo_frame: group.turbo_frame_id)
29
+ end
30
+ end
31
+
32
+ def is_edit?
33
+ @view == :edit
34
+ end
35
+
36
+ def is_new?
37
+ @view == :new
38
+ end
39
+
40
+ def is_initial_load?
41
+ params[:active_tab_name].blank?
42
+ end
43
+
44
+ # On initial load we want that each tab button to be the selected one.
45
+ # We do that so we don't get the wrongly selected item for a quick brief when first switching from one panel to another.
46
+ def selected?(tab)
47
+ if is_initial_load?
48
+ current_tab.name.to_s == tab.name.to_s
49
+ else
50
+ tab.name.to_s == active_tab_name.to_s
51
+ end
52
+ end
53
+
54
+ # Goes through all items and removes the ones that are not supposed to be visible.
55
+ # Example below:
56
+ # tabs do
57
+ # field :comments, as: :has_many
58
+ # end
59
+ # Because the developer hasn't specified that it should be visible on edit views (with the show_on: :edit option),
60
+ # the field should not be visible in the item switcher either.
61
+ def visible_items
62
+ tabs.select do |item|
63
+ visible = true
64
+
65
+ if item.items.blank?
66
+ visible = false
67
+ end
68
+
69
+ first_item = item.items.first
70
+ if item.items.count == 1 && first_item.is_field? && first_item.has_own_panel? && !first_item.visible_on?(view)
71
+ # Return nil if tab contians a has_many type of fields and it's hidden in current view
72
+ visible = false
73
+ end
74
+
75
+ if item.respond_to?(:visible_on?)
76
+ visible = item.visible_on? view
77
+ end
78
+
79
+ if item.respond_to?(:visible?)
80
+ visible = item.visible?
81
+ end
82
+
83
+ visible
84
+ end
85
+ end
86
+ end
@@ -1,77 +1,55 @@
1
1
  <%= content_tag :div,
2
- class: "space-y-12",
3
2
  data: {
4
3
  'model-id': @resource.model.id,
5
4
  selected_resources_name: @resource.model_key,
6
5
  selected_resources: [@resource.model.id],
7
6
  **@resource.stimulus_data_attributes
8
7
  } do %>
9
- <% @resource.panels.each_with_index do |resource_panel, index| %>
10
- <%= form_with model: @resource.model,
11
- scope: @resource.form_scope,
12
- url: form_url,
13
- local: true,
14
- multipart: true do |form| %>
15
- <%= render Avo::ReferrerParamsComponent.new back_path: back_path %>
16
- <%= render Avo::PanelComponent.new(title: resource_panel[:name], description: @resource.resource_description, display_breadcrumbs: true, index: index) do |c| %>
17
- <% c.tools do %>
18
- <%= a_link back_path,
19
- style: :text,
20
- icon: 'arrow-left' do %>
21
- <%= t('avo.cancel').capitalize %>
22
- <% end %>
23
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
24
- <% if can_see_the_save_button? %>
25
- <%= a_button color: :primary,
8
+ <%= form_with model: @resource.model,
9
+ scope: @resource.form_scope,
10
+ url: form_url,
11
+ method: form_method,
12
+ local: true,
13
+ html: {
14
+ novalidate: true
15
+ },
16
+ multipart: true do |form| %>
17
+ <%= render Avo::ReferrerParamsComponent.new back_path: back_path %>
18
+ <%= render Avo::PanelComponent.new(title: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: 0, data: { panel_id: "main" }) do |c| %>
19
+ <% c.tools do %>
20
+ <%= a_link back_path,
21
+ style: :text,
22
+ icon: 'arrow-left' do %>
23
+ <%= t('avo.cancel').capitalize %>
24
+ <% end %>
25
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
26
+ <% if can_see_the_save_button? %>
27
+ <%= a_button color: :primary,
26
28
  style: :primary,
27
29
  loading: true,
28
30
  type: :submit,
29
31
  icon: 'save' do %>
30
- <%= t('avo.save').capitalize %>
31
- <% end %>
32
+ <%= t('avo.save').capitalize %>
32
33
  <% end %>
33
34
  <% end %>
34
- <% if Avo.configuration.buttons_on_form_footers %>
35
- <% c.footer_tools do %>
36
- <%= a_link back_path, icon: 'arrow-left' do %>
37
- <%= t('avo.cancel').capitalize %>
38
- <% end %>
39
- <% if can_see_the_save_button? %>
40
- <%= a_button color: :green, loading: true, type: :submit, icon: 'save' do %>
41
- <%= t('avo.save').capitalize %>
42
- <% end %>
43
- <% end %>
35
+ <% end %>
36
+ <% if Avo.configuration.buttons_on_form_footers %>
37
+ <% c.footer_tools do %>
38
+ <%= a_link back_path, icon: 'arrow-left' do %>
39
+ <%= t('avo.cancel').capitalize %>
44
40
  <% end %>
45
- <% end %>
46
- <% c.body do %>
47
- <div class="divide-y">
48
- <% @resource.get_fields.each_with_index do |field, index| %>
49
- <%= render field.component_for_view(:edit).new(field: field, resource: @resource, index: index, form: form) unless field.computed %>
41
+ <% if can_see_the_save_button? %>
42
+ <%= a_button color: :green, loading: true, type: :submit, icon: 'save' do %>
43
+ <%= t('avo.save').capitalize %>
50
44
  <% end %>
51
- </div>
45
+ <% end %>
52
46
  <% end %>
53
47
  <% end %>
54
48
  <% end %>
55
- <% end %>
56
- <% if @reflection.blank? %>
57
- <% if has_one_panels.present? %>
58
- <% has_one_panels.each_with_index do |field, index| %>
59
- <%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
60
- <% end %>
61
- <% end %>
62
- <% if has_many_panels.present? %>
63
- <% has_many_panels.each_with_index do |field, index| %>
64
- <%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
65
- <% end %>
66
- <% end %>
67
- <% if has_as_belongs_to_many_panels.present? %>
68
- <% has_as_belongs_to_many_panels.each_with_index do |field, index| %>
69
- <%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
70
- <% end %>
71
- <% end %>
72
- <% if resource_tools.present? %>
73
- <% resource_tools.each do |tool, index| %>
74
- <%= render tool.partial, tool: tool %>
49
+ <%= content_tag :div, class: 'space-y-12' do %>
50
+ <% @resource.get_items.each_with_index do |item, index| %>
51
+ <% next if item.nil? %>
52
+ <%= render Avo::ItemSwitcherComponent.new resource: @resource, item: item, index: index + 1, view: @view, form: form %>
75
53
  <% end %>
76
54
  <% end %>
77
55
  <% end %>
@@ -15,6 +15,10 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
15
15
  split_panel_fields
16
16
  end
17
17
 
18
+ def title
19
+ @resource.default_panel_name
20
+ end
21
+
18
22
  def back_path
19
23
  if via_resource?
20
24
  helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
@@ -32,7 +36,7 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
32
36
  # The save button is dependent on the edit? policy method.
33
37
  # The update? method should be called only when the user clicks the Save button so the developer gets access to the params from the form.
34
38
  def can_see_the_save_button?
35
- @resource.authorization.authorize_action :edit, raise_exception: false
39
+ @resource.authorization.authorize_action @view, raise_exception: false
36
40
  end
37
41
 
38
42
  private
@@ -45,6 +49,12 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
45
49
  view == :edit
46
50
  end
47
51
 
52
+ def form_method
53
+ return :put if is_edit?
54
+
55
+ :post
56
+ end
57
+
48
58
  def form_url
49
59
  if is_edit?
50
60
  helpers.resource_path(
@@ -37,7 +37,7 @@
37
37
  <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.model_name.collection} %>
38
38
  </div>
39
39
  <% else %>
40
- <%# Offset for the space-y-2 property when the serach is missing %>
40
+ <%# Offset for the space-y-2 property when the search is missing %>
41
41
  <div class="-mb-2"></div>
42
42
  <% end %>
43
43
  <% if @filters.present? || available_view_types.count > 1 %>
@@ -113,11 +113,11 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
113
113
  end
114
114
  end
115
115
 
116
- helpers.new_resource_path(model: @resource.model_class, resource: @resource, **args)
116
+ helpers.new_resource_path(resource: @resource, **args)
117
117
  end
118
118
 
119
119
  def attach_path
120
- "#{Avo::App.root_path}#{request.env["PATH_INFO"]}/new"
120
+ Avo::App.root_path(paths: [request.env["PATH_INFO"], "new"])
121
121
  end
122
122
 
123
123
  def singular_resource_name
@@ -145,7 +145,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
145
145
  @reflection.active_record.to_s
146
146
  end
147
147
 
148
- def name
148
+ def name
149
149
  field.custom_name? ? field.name : field.plural_name
150
150
  end
151
151
 
@@ -1,105 +1,74 @@
1
1
  <%= content_tag :div,
2
- class: "space-y-12",
3
2
  data: {
4
3
  'model-id': @resource.model.id,
5
4
  selected_resources_name: @resource.model_key,
6
5
  selected_resources: [@resource.model.id],
7
6
  **@resource.stimulus_data_attributes
8
7
  } do %>
9
- <% @resource.panels.each_with_index do |resource_panel, index| %>
10
- <%= render Avo::PanelComponent.new(title: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: index) do |c| %>
11
- <% c.tools do %>
12
- <% if resource_panel[:name] == @resource.default_panel_name %>
13
- <% if @reflection.present? && @resource.model.present? %>
14
- <% if can_detach? %>
15
- <%= a_button url: detach_path,
16
- icon: 'detach',
17
- method: :delete,
18
- form_class: 'flex flex-col sm:flex-row sm:inline-flex',
19
- style: :text,
20
- data: {
21
- confirm: "Are you sure you want to detach this #{title}."
22
- } do %>
23
- <%= t('avo.detach_item', item: title).capitalize %>
24
- <% end %>
25
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
26
- <% end %>
27
- <% if can_see_the_edit_button? %>
28
- <%= a_link edit_path,
29
- color: :primary,
30
- style: :primary,
31
- icon: 'edit' do %>
32
- <%= t('avo.edit').capitalize %>
33
- <% end %>
34
- <% end %>
35
- <% else %>
36
- <%= a_link back_path,
37
- style: :text,
38
- icon: 'arrow-left' do %>
39
- <%= t('avo.go_back') %>
40
- <% end %>
41
- <% if can_see_the_destroy_button? %>
42
- <%= a_button url: helpers.resource_path(model: @resource.model, resource: @resource),
43
- method: :delete,
44
- local: true,
45
- style: :text,
46
- title: t('avo.delete_item', item: @resource.model.model_name.name.downcase).capitalize,
47
- loading: true,
48
- confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
49
- color: :red,
50
- icon: 'trash',
51
- form_class: 'flex flex-col sm:flex-row sm:inline-flex',
52
- data: {
53
- control: :destroy,
54
- 'resource-id': @resource.model.id,
55
- 'tippy': 'tooltip',
56
- } do %>
57
- <%= t('avo.delete').capitalize %>
58
- <% end %>
59
- <% end %>
60
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
61
- <% if @resource.authorization.authorize_action(:edit, raise_exception: false) %>
62
- <%= a_link edit_path,
63
- color: :primary,
64
- style: :primary,
65
- icon: 'edit' do %>
66
- <%= t('avo.edit').capitalize %>
67
- <% end %>
68
- <% end %>
8
+ <%= render Avo::PanelComponent.new(title: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: 0, data: { panel_id: "main" }) do |c| %>
9
+ <% c.tools do %>
10
+ <% if @reflection.present? && @resource.model.present? %>
11
+ <% if can_detach? %>
12
+ <%= a_button url: detach_path,
13
+ icon: 'detach',
14
+ method: :delete,
15
+ form_class: 'flex flex-col sm:flex-row sm:inline-flex',
16
+ style: :text,
17
+ data: {
18
+ confirm: "Are you sure you want to detach this #{title}."
19
+ } do %>
20
+ <%= t('avo.detach_item', item: title).capitalize %>
69
21
  <% end %>
22
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
70
23
  <% end %>
71
- <% end %>
72
- <% c.body do %>
73
- <% if fields_by_panel[resource_panel[:name]].present? %>
74
- <div class="divide-y">
75
- <% fields_by_panel[resource_panel[:name]].each_with_index do |field, index| %>
76
- <%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
77
- <% end %>
78
- </div>
24
+ <% if can_see_the_edit_button? %>
25
+ <%= a_link edit_path,
26
+ color: :primary,
27
+ style: :primary,
28
+ icon: 'edit' do %>
29
+ <%= t('avo.edit').capitalize %>
30
+ <% end %>
31
+ <% end %>
32
+ <% else %>
33
+ <%= a_link back_path,
34
+ style: :text,
35
+ icon: 'arrow-left' do %>
36
+ <%= t('avo.go_back') %>
37
+ <% end %>
38
+ <% if can_see_the_destroy_button? %>
39
+ <%= a_button url: helpers.resource_path(model: @resource.model, resource: @resource),
40
+ method: :delete,
41
+ local: true,
42
+ style: :text,
43
+ loading: true,
44
+ confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
45
+ color: :red,
46
+ icon: 'trash',
47
+ form_class: 'flex flex-col sm:flex-row sm:inline-flex',
48
+ data: {
49
+ control: :destroy,
50
+ 'resource-id': @resource.model.id,
51
+ 'tippy': 'tooltip',
52
+ } do %>
53
+ <%= t('avo.delete').capitalize %>
54
+ <% end %>
55
+ <% end %>
56
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
57
+ <% if @resource.authorization.authorize_action(:edit, raise_exception: false) %>
58
+ <%= a_link edit_path,
59
+ color: :primary,
60
+ style: :primary,
61
+ icon: 'edit' do %>
62
+ <%= t('avo.edit').capitalize %>
63
+ <% end %>
79
64
  <% end %>
80
65
  <% end %>
81
66
  <% end %>
82
67
  <% end %>
83
- <% if @reflection.blank? %>
84
- <% if has_one_panels.present? %>
85
- <% has_one_panels.each_with_index do |field, index| %>
86
- <%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
87
- <% end %>
88
- <% end %>
89
- <% if has_many_panels.present? %>
90
- <% has_many_panels.each_with_index do |field, index| %>
91
- <%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
92
- <% end %>
93
- <% end %>
94
- <% if has_as_belongs_to_many_panels.present? %>
95
- <% has_as_belongs_to_many_panels.each_with_index do |field, index| %>
96
- <%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
97
- <% end %>
98
- <% end %>
99
- <% if resource_tools.present? %>
100
- <% resource_tools.each do |tool, index| %>
101
- <%= render tool.partial, tool: tool %>
102
- <% end %>
68
+ <%= content_tag :div, class: 'space-y-12' do %>
69
+ <% @resource.get_items.each_with_index do |item, index| %>
70
+ <% next if item.nil? %>
71
+ <%= render Avo::ItemSwitcherComponent.new resource: @resource, reflection: @reflection, item: item, index: index + 1, view: @view %>
103
72
  <% end %>
104
73
  <% end %>
105
74
  <% if should_display_invalid_fields_errors? %>
@@ -19,7 +19,7 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
19
19
  return field.name if has_one_field?
20
20
  reflection_resource.name
21
21
  else
22
- @resource.panels.first[:name]
22
+ @resource.default_panel_name
23
23
  end
24
24
  end
25
25
 
@@ -52,6 +52,6 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
52
52
  end
53
53
 
54
54
  def has_one_field?
55
- field.present? and field.class == Avo::Fields::HasOneField
55
+ field.present? and field.instance_of? Avo::Fields::HasOneField
56
56
  end
57
57
  end
@@ -42,7 +42,7 @@ module Avo
42
42
  model = @resource.class.find_scope.find params[:id]
43
43
  end
44
44
 
45
- @action = action_class.new(model: model, resource: resource, user: _current_user)
45
+ @action = action_class.new(model: model, resource: resource, user: _current_user, view: :edit)
46
46
  end
47
47
 
48
48
  def respond(response)
@@ -13,7 +13,8 @@ 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
+ before_action :set_default_locale
17
+ around_action :set_force_locale, if: -> { params[:force_locale].present? }
17
18
  before_action :set_authorization
18
19
  before_action :_authenticate!
19
20
  before_action :set_container_classes
@@ -27,7 +28,7 @@ module Avo
27
28
  add_flash_types :info, :warning, :success, :error
28
29
 
29
30
  def init_app
30
- Avo::App.init request: request, context: context, root_path: avo.root_path.delete_suffix("/"), current_user: _current_user, view_context: view_context, params: params
31
+ Avo::App.init request: request, context: context, current_user: _current_user, view_context: view_context, params: params
31
32
 
32
33
  @license = Avo::App.license
33
34
  end
@@ -287,10 +288,26 @@ module Avo
287
288
  @resource.form_scope
288
289
  end
289
290
 
290
- def set_locale
291
+ def set_default_locale
291
292
  I18n.locale = params[:set_locale] || I18n.default_locale
292
293
 
293
294
  I18n.default_locale = I18n.locale
294
295
  end
296
+
297
+ # Temporary set the locale and reverting at the end of the request.
298
+ def set_force_locale
299
+ initial_locale = I18n.locale.to_s.dup
300
+ I18n.locale = params[:force_locale]
301
+ yield
302
+ I18n.locale = initial_locale
303
+ end
304
+
305
+ def default_url_options
306
+ if params[:force_locale].present?
307
+ { **super, force_locale: params[:force_locale] }
308
+ else
309
+ super
310
+ end
311
+ end
295
312
  end
296
313
  end
@@ -20,12 +20,6 @@ module Avo
20
20
  render Avo::EmptyStateComponent.new **args
21
21
  end
22
22
 
23
- def turbo_frame_wrap(name, &block)
24
- render Avo::TurboFrameWrapperComponent.new name do
25
- capture(&block)
26
- end
27
- end
28
-
29
23
  def a_button(**args, &block)
30
24
  render Avo::ButtonComponent.new(is_link: false, **args) do
31
25
  capture(&block) if block_given?
@@ -35,7 +35,7 @@ module Avo
35
35
  avo.send :"resources_#{resource.singular_route_key}_path", id, **args
36
36
  end
37
37
 
38
- def new_resource_path(model:, resource:, **args)
38
+ def new_resource_path(resource:, **args)
39
39
  avo.send :"new_resources_#{resource.singular_route_key}_path", **args
40
40
  end
41
41
 
@@ -74,7 +74,11 @@ document.addEventListener('turbo:frame-load', () => {
74
74
  document.addEventListener('turbo:before-fetch-response', async (e) => {
75
75
  if (e.detail.fetchResponse.response.status === 500) {
76
76
  const { id, src } = e.target
77
- e.target.src = `${window.Avo.configuration.root_path}/failed_to_load?turbo_frame=${id}&src=${src}`
77
+ // Don't try to redirect to failed to load if this is alread a redirection to failed to load and crashed somewhere.
78
+ // You'll end up with a request loop.
79
+ if (!e.detail.fetchResponse?.response?.url?.includes('/failed_to_load')) {
80
+ e.target.src = `${window.Avo.configuration.root_path}/failed_to_load?turbo_frame=${id}&src=${src}`
81
+ }
78
82
  }
79
83
  })
80
84