avo 2.16.1.pre.1.nativefields → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +4 -2
- data/Gemfile.lock +3 -1
- data/README.md +1 -1
- data/app/assets/config/avo_manifest.js +1 -0
- data/app/assets/svgs/placeholder.svg +1 -0
- data/app/components/avo/actions_component.html.erb +3 -3
- data/app/components/avo/base_component.rb +7 -4
- data/app/components/avo/field_wrapper_component.html.erb +8 -10
- data/app/components/avo/field_wrapper_component.rb +14 -12
- data/app/components/avo/fields/badge_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/belongs_to_field/edit_component.rb +9 -3
- data/app/components/avo/fields/belongs_to_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/belongs_to_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/belongs_to_field/show_component.rb +1 -1
- data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/boolean_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/boolean_group_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/boolean_group_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/code_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/common/heading_component.html.erb +5 -4
- data/app/components/avo/fields/common/heading_component.rb +6 -1
- data/app/components/avo/fields/country_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/date_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/date_time_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/edit_component.rb +6 -4
- data/app/components/avo/fields/external_image_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/file_field/edit_component.html.erb +2 -1
- data/app/components/avo/fields/file_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/files_field/edit_component.html.erb +2 -1
- data/app/components/avo/fields/files_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/files_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/gravatar_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/has_one_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/heading_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/heading_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/id_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/index_component.rb +11 -2
- data/app/components/avo/fields/number_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/progress_bar_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/select_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/show_component.rb +7 -1
- data/app/components/avo/fields/status_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/tags_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/text_field/edit_component.html.erb +3 -1
- data/app/components/avo/fields/text_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -1
- data/app/components/avo/filters_component.html.erb +2 -2
- data/app/components/avo/index/grid_cover_empty_state_component.html.erb +1 -1
- data/app/components/avo/index/grid_item_component.html.erb +15 -13
- data/app/components/avo/index/grid_item_component.rb +1 -1
- data/app/components/avo/index/ordering/buttons_component.html.erb +1 -1
- data/app/components/avo/index/resource_controls_component.rb +2 -2
- data/app/components/avo/index/table_row_component.html.erb +1 -1
- data/app/components/avo/panel_component.html.erb +11 -2
- data/app/components/avo/panel_component.rb +1 -0
- data/app/components/avo/resource_component.rb +18 -0
- data/app/components/avo/resource_sidebar_component.html.erb +19 -0
- data/app/components/avo/resource_sidebar_component.rb +26 -0
- data/app/components/avo/sidebar_profile_component.html.erb +1 -1
- data/app/components/avo/tab_switcher_component.html.erb +2 -2
- data/app/components/avo/views/resource_edit_component.html.erb +31 -25
- data/app/components/avo/views/resource_edit_component.rb +1 -1
- data/app/components/avo/views/resource_show_component.html.erb +8 -2
- data/app/components/avo/views/resource_show_component.rb +1 -1
- data/app/controllers/avo/actions_controller.rb +10 -2
- data/app/controllers/avo/application_controller.rb +4 -2
- data/app/controllers/avo/associations_controller.rb +10 -5
- data/app/controllers/avo/attachments_controller.rb +2 -1
- data/app/controllers/avo/base_controller.rb +6 -4
- data/app/controllers/avo/search_controller.rb +13 -4
- data/app/helpers/avo/application_helper.rb +3 -3
- data/app/helpers/avo/resources_helper.rb +2 -2
- data/app/javascript/avo.base.js +3 -1
- data/app/javascript/js/controllers/action_controller.js +1 -4
- data/app/javascript/js/controllers/actions_picker_controller.js +8 -9
- data/app/javascript/js/controllers/tabs_controller.js +14 -27
- data/app/views/avo/actions/show.html.erb +2 -2
- data/app/views/avo/home/failed_to_load.html.erb +3 -2
- data/config/brakeman.ignore +40 -0
- data/db/factories.rb +20 -0
- data/lib/avo/base_resource.rb +26 -0
- data/lib/avo/concerns/fetches_things.rb +1 -1
- data/lib/avo/concerns/has_fields.rb +22 -0
- data/lib/avo/concerns/is_resource_item.rb +4 -0
- data/lib/avo/configuration/branding.rb +9 -1
- data/lib/avo/fields/belongs_to_field.rb +3 -0
- data/lib/avo/fields/heading_field.rb +15 -0
- data/lib/avo/items_holder.rb +4 -0
- data/lib/avo/licensing/pro_license.rb +1 -0
- data/lib/avo/menu/builder.rb +1 -1
- data/lib/avo/menu/menu.rb +0 -2
- data/lib/avo/reloader.rb +27 -26
- data/lib/avo/services/encryption_service.rb +1 -1
- data/lib/avo/sidebar.rb +60 -0
- data/lib/avo/sidebar_builder.rb +24 -0
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/templates/field/components/index_component.html.erb.tt +1 -1
- data/lib/generators/avo/templates/initializer/avo.tt +2 -1
- data/lib/generators/avo/templates/locales/avo.en.yml +0 -1
- data/lib/generators/avo/templates/locales/{avo.nb-NO.yml → avo.nb.yml} +35 -10
- data/lib/generators/avo/templates/locales/avo.nn.yml +118 -0
- data/lib/generators/avo/templates/locales/avo.tr.yml +119 -0
- data/public/avo-assets/avo.base.css +57 -75
- data/public/avo-assets/avo.base.js +63 -63
- data/public/avo-assets/avo.base.js.map +3 -3
- metadata +13 -7
- data/app/views/avo/home/failed_to_load.html copy.erb +0 -23
- data/config/master.key +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<%=
|
|
1
|
+
<%= field_wrapper **field_wrapper_args, full_width: true do %>
|
|
2
2
|
<%= content_tag :div, class: "relative block overflow-x-auto max-w-full",
|
|
3
3
|
data: {
|
|
4
4
|
controller: "trix-field",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div data-controller="toggle-panel">
|
|
2
|
-
<div class="relative w-full flex justify-between
|
|
2
|
+
<div class="relative w-full flex justify-between">
|
|
3
3
|
<%= a_button class: 'focus:outline-none',
|
|
4
4
|
color: :primary,
|
|
5
5
|
size: :sm,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<% end %>
|
|
15
15
|
<% end %>
|
|
16
16
|
<div
|
|
17
|
-
class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-
|
|
17
|
+
class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-40 shadow-modal rounded divide-y divide-gray-300 <%= 'hidden' unless params[:keep_filters_panel_open] == '1' %>"
|
|
18
18
|
data-toggle-panel-target="panel"
|
|
19
19
|
>
|
|
20
20
|
<% @filters.each do |filter| %>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
<div class="absolute bg-gray-50 w-full h-full">
|
|
2
|
-
<%=
|
|
2
|
+
<%= image_tag Avo.configuration.branding.placeholder, class: 'relative transform -translate-x-1/2 -translate-y-1/2 h-20 text-gray-400 inset-auto top-1/2 left-1/2' %>
|
|
3
3
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
class="relative bg-white rounded shadow-modal flex flex-col group"
|
|
3
3
|
<%== item_selector_init @resource %>
|
|
4
4
|
>
|
|
5
|
-
|
|
5
|
+
<%= content_tag :div, class: "relative w-full pb-3/4 rounded-t overflow-hidden #{cover.present? ? cover.get_html(:classes, view: :index, element: :wrapper) : ""}", style: cover.present? ? cover.get_html(:style, view: :index, element: :wrapper) : "" do %>
|
|
6
6
|
<% if @resource.record_selector %>
|
|
7
7
|
<%== item_selector_input floating: true, size: :lg %>
|
|
8
8
|
<% end %>
|
|
@@ -17,9 +17,7 @@
|
|
|
17
17
|
<%= link_to_if cover.link_to_resource, image_tag(helpers.main_app.url_for(cover.value.variant(resize_to_limit: [480, 480])), class: 'absolute h-full w-full object-cover'), resource_view_path, class: 'absolute h-full w-full object-cover', title: title.value %>
|
|
18
18
|
<% else %>
|
|
19
19
|
<%= link_to resource_view_path do %>
|
|
20
|
-
|
|
21
|
-
<%= helpers.svg 'avocado', class: 'relative transform -translate-x-1/2 -translate-y-1/2 h-20 text-gray-400 inset-auto top-1/2 left-1/2' %>
|
|
22
|
-
</div>
|
|
20
|
+
<%= render Avo::Index::GridCoverEmptyStateComponent.new %>
|
|
23
21
|
<% end %>
|
|
24
22
|
<% end %>
|
|
25
23
|
<% elsif cover.value.present? %>
|
|
@@ -27,18 +25,22 @@
|
|
|
27
25
|
<% else %>
|
|
28
26
|
<%= render Avo::Index::GridCoverEmptyStateComponent.new %>
|
|
29
27
|
<% end %>
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
<% end %>
|
|
29
|
+
<%= content_tag :div, class: "grid grid-cols-1 place-content-between p-4 h-full" do %>
|
|
32
30
|
<div class="mb-4 h-full flex flex-col space-between">
|
|
33
|
-
|
|
34
|
-
<%=
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
<% if title.present? %>
|
|
32
|
+
<%= content_tag :div, class: "grid font-semibold leading-tight text-lg mb-2 #{title.get_html(:classes, view: :index, element: :wrapper)}", style: title.get_html(:style, view: :index, element: :wrapper) do %>
|
|
33
|
+
<%= link_to_if title.link_to_resource, title.value, resource_view_path %>
|
|
34
|
+
<% end %>
|
|
35
|
+
<% end %>
|
|
36
|
+
<% if body.present? %>
|
|
37
|
+
<%= content_tag :div, class: "text-sm break-words text-gray-500 #{body.get_html(:classes, view: :index, element: :wrapper)}", style: body.get_html(:style, view: :index, element: :wrapper) do %>
|
|
38
|
+
<%= body.value %>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% end %>
|
|
39
41
|
</div>
|
|
40
42
|
<div class="w-full place-self-end">
|
|
41
43
|
<%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, view_type: :grid)) %>
|
|
42
44
|
</div>
|
|
43
|
-
|
|
45
|
+
<% end %>
|
|
44
46
|
</div>
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
%>
|
|
23
23
|
<%= helpers.svg('switch-vertical', class: 'text-gray-400 h-6 hover:text-gray-600') %>
|
|
24
24
|
<% end %>
|
|
25
|
-
<div class="flex hidden absolute max-w-xs bg-white rounded p-2
|
|
25
|
+
<div class="flex hidden absolute max-w-xs bg-white rounded p-2" data-popover-target="content">
|
|
26
26
|
<%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, reflection: @reflection, direction: :higher, svg: 'arrow-up' %>
|
|
27
27
|
<%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, reflection: @reflection, direction: :lower, svg: 'arrow-down' %>
|
|
28
28
|
<%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, reflection: @reflection, direction: :to_top, svg: 'download-solid-reversed' %>
|
|
@@ -36,7 +36,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
|
|
|
36
36
|
|
|
37
37
|
if @parent_model.present?
|
|
38
38
|
args = {
|
|
39
|
-
via_resource_class: parent_resource.
|
|
39
|
+
via_resource_class: parent_resource.class.to_s,
|
|
40
40
|
via_resource_id: @parent_model.id
|
|
41
41
|
}
|
|
42
42
|
end
|
|
@@ -50,7 +50,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
|
|
|
50
50
|
|
|
51
51
|
if @parent_model.present?
|
|
52
52
|
args = {
|
|
53
|
-
via_resource_class: parent_resource.
|
|
53
|
+
via_resource_class: parent_resource.class.to_s,
|
|
54
54
|
via_resource_id: @parent_model.id
|
|
55
55
|
}
|
|
56
56
|
end
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</td>
|
|
18
18
|
<% end %>
|
|
19
19
|
<% @resource.get_fields(reflection: @reflection, only_root: true).each_with_index do |field, index| %>
|
|
20
|
-
<%= render field.component_for_view(:index).new(field: field, resource: @resource, index: index, parent_model: @parent_model) %>
|
|
20
|
+
<%= render field.component_for_view(:index).new(field: field, resource: @resource, index: index, parent_model: @parent_model, parent_resource: @parent_resource) %>
|
|
21
21
|
<% end %>
|
|
22
22
|
<% if Avo.configuration.resource_controls_on_the_right? %>
|
|
23
23
|
<td class="text-right whitespace-nowrap px-2" data-control="resource-controls">
|
|
@@ -24,8 +24,17 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
<% end %>
|
|
26
26
|
<% if body? %>
|
|
27
|
-
<div class="
|
|
28
|
-
|
|
27
|
+
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:gap-4 w-full">
|
|
28
|
+
<div class="flex-1 overflow-auto <% if sidebar? %> w-2/3 <% end %>">
|
|
29
|
+
<div class="relative <%= white_panel_classes %> <%= @body_classes %>">
|
|
30
|
+
<%= body %>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
<% if sidebar? %>
|
|
34
|
+
<div class="w-full sm:w-1/3 flex-shrink-0 h-full <%= white_panel_classes %>">
|
|
35
|
+
<%= sidebar %>
|
|
36
|
+
</div>
|
|
37
|
+
<% end %>
|
|
29
38
|
</div>
|
|
30
39
|
<% end %>
|
|
31
40
|
<% if bare_content? %>
|
|
@@ -85,6 +85,16 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
def sidebar
|
|
89
|
+
return if Avo::App.license.lacks_with_trial(:resource_sidebar)
|
|
90
|
+
|
|
91
|
+
@sidebar ||= search_for_sidebar
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def sidebar_component(form: nil)
|
|
95
|
+
Avo::ResourceSidebarComponent.new resource: @resource, fields: sidebar.items, params: params, view: view, form: form
|
|
96
|
+
end
|
|
97
|
+
|
|
88
98
|
def has_reflection_and_is_read_only
|
|
89
99
|
if @reflection.present? && @reflection.active_record.name && @reflection.name
|
|
90
100
|
fields = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name).get_field_definitions
|
|
@@ -105,4 +115,12 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
105
115
|
def via_resource?
|
|
106
116
|
(params[:via_resource_class].present? || params[:via_relation_class].present?) && params[:via_resource_id].present?
|
|
107
117
|
end
|
|
118
|
+
|
|
119
|
+
def search_for_sidebar
|
|
120
|
+
item = @resource.get_items.find do |item|
|
|
121
|
+
item.is_sidebar?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
item&.hydrate(view: view)
|
|
125
|
+
end
|
|
108
126
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<div class="resource-sidebar-component divide-y">
|
|
2
|
+
<% fields.each_with_index do |field, index| %>
|
|
3
|
+
<%= render field
|
|
4
|
+
.hydrate(
|
|
5
|
+
resource: resource,
|
|
6
|
+
model: resource.model,
|
|
7
|
+
user: resource.user,
|
|
8
|
+
view: view
|
|
9
|
+
).component_for_view(view)
|
|
10
|
+
.new(
|
|
11
|
+
field: field,
|
|
12
|
+
resource: resource,
|
|
13
|
+
form: form,
|
|
14
|
+
stacked: true,
|
|
15
|
+
index: index
|
|
16
|
+
)
|
|
17
|
+
%>
|
|
18
|
+
<% end %>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Avo::ResourceSidebarComponent < ViewComponent::Base
|
|
4
|
+
attr_reader :resource
|
|
5
|
+
attr_reader :params
|
|
6
|
+
attr_reader :view
|
|
7
|
+
attr_reader :form
|
|
8
|
+
|
|
9
|
+
def initialize(resource: nil, fields: nil, index: nil, params: nil, form: nil, view: nil)
|
|
10
|
+
@resource = resource
|
|
11
|
+
@fields = fields
|
|
12
|
+
@params = params
|
|
13
|
+
@view = view
|
|
14
|
+
@form = form
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def fields
|
|
18
|
+
@fields.filter do |field|
|
|
19
|
+
field.visible_on? view
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render?
|
|
24
|
+
Avo::App.license.has_with_trial(:resource_sidebar)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
<%= helpers.svg 'three-dots', class: 'h-4' %>
|
|
23
23
|
</a>
|
|
24
24
|
<div
|
|
25
|
-
class="hidden absolute flex flex-col inset-auto right-0 -mt-12 bg-white rounded min-w-[200px] shadow-context -translate-y-full"
|
|
25
|
+
class="hidden absolute flex flex-col inset-auto right-0 -mt-12 bg-white rounded min-w-[200px] shadow-context -translate-y-full z-40"
|
|
26
26
|
data-toggle-panel-target="panel"
|
|
27
27
|
>
|
|
28
28
|
<% if Avo::App.has_profile_menu? %>
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
</div>
|
|
21
21
|
</div>
|
|
22
22
|
<% else %>
|
|
23
|
-
<div class="flex flex-wrap gap-2" data-target="tab-switcher" data-style="pills">
|
|
23
|
+
<div class="flex flex-wrap gap-2 bg-white p-2" data-target="tab-switcher" data-style="pills">
|
|
24
24
|
<% visible_items.each do |tab| %>
|
|
25
25
|
<%= a_link tab_path(tab),
|
|
26
26
|
color: selected?(tab) ? :primary : :gray,
|
|
27
27
|
style: selected?(tab) ? :outline : :text,
|
|
28
28
|
size: :sm,
|
|
29
|
-
class: selected?(tab) ? "z-20" : "",
|
|
29
|
+
class: selected?(tab) ? "z-20 bg-primary-100" : "",
|
|
30
30
|
title: tab.description,
|
|
31
31
|
data: {
|
|
32
32
|
tippy: tab.description.present? ? 'tooltip' : '',
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
<%= render Avo::PanelComponent.new(name: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: 0, data: { panel_id: "main" }) do |c| %>
|
|
20
20
|
<% c.tools do %>
|
|
21
21
|
<%= a_link back_path,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
style: :text,
|
|
23
|
+
icon: 'arrow-left' do %>
|
|
24
|
+
<%= t('avo.cancel').capitalize %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% if can_see_the_destroy_button? %>
|
|
27
|
+
<%= a_link destroy_path,
|
|
28
28
|
method: :delete,
|
|
29
29
|
local: true,
|
|
30
30
|
style: :text,
|
|
@@ -37,36 +37,42 @@
|
|
|
37
37
|
control: :destroy,
|
|
38
38
|
'resource-id': @resource.model.id,
|
|
39
39
|
} do %>
|
|
40
|
-
|
|
40
|
+
<%= t('avo.delete').capitalize %>
|
|
41
|
+
<% end %>
|
|
41
42
|
<% end %>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<%= a_button color: :primary,
|
|
43
|
+
<% if can_see_the_actions_button? %>
|
|
44
|
+
<%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
|
|
45
|
+
<% end %>
|
|
46
|
+
<% if can_see_the_save_button? %>
|
|
47
|
+
<%= a_button color: :primary,
|
|
48
48
|
style: :primary,
|
|
49
49
|
loading: true,
|
|
50
50
|
type: :submit,
|
|
51
51
|
icon: 'save' do %>
|
|
52
|
-
|
|
52
|
+
<%= t('avo.save').capitalize %>
|
|
53
|
+
<% end %>
|
|
53
54
|
<% end %>
|
|
54
55
|
<% end %>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
<%# Extract the main panel and display the fields here. %>
|
|
57
|
+
<%# This way we'll be able to render the footer buttons under the main fields. %>
|
|
58
|
+
<% if main_panel.present? %>
|
|
59
|
+
<% c.body do %>
|
|
60
|
+
<div class="divide-y">
|
|
61
|
+
<% main_panel.items.each_with_index do |field, index| %>
|
|
62
|
+
<%= render field
|
|
63
|
+
.hydrate(resource: @resource, model: @resource.model, user: @resource.user, view: view_for(field))
|
|
64
|
+
.component_for_view(view_for field)
|
|
65
|
+
.new(field: field, resource: @resource, index: index, form: form, compact: sidebar.present?)
|
|
66
|
+
%>
|
|
66
67
|
<% end %>
|
|
67
68
|
</div>
|
|
68
69
|
<% end %>
|
|
69
70
|
<% end %>
|
|
71
|
+
<% if sidebar.present? %>
|
|
72
|
+
<% c.sidebar do %>
|
|
73
|
+
<%= render sidebar_component form: form %>
|
|
74
|
+
<% end %>
|
|
75
|
+
<% end %>
|
|
70
76
|
<% if Avo.configuration.buttons_on_form_footers %>
|
|
71
77
|
<% c.footer_tools do %>
|
|
72
78
|
<div class="mt-4">
|
|
@@ -31,7 +31,7 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def resource_view_path
|
|
34
|
-
helpers.resource_view_path(model:
|
|
34
|
+
helpers.resource_view_path(model: association_resource.model, resource: association_resource)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def can_see_the_destroy_button?
|
|
@@ -160,14 +160,20 @@
|
|
|
160
160
|
<% c.body do %>
|
|
161
161
|
<div class="divide-y">
|
|
162
162
|
<% main_panel.items.each_with_index do |field, index| %>
|
|
163
|
-
<%= render field
|
|
163
|
+
<%= render field
|
|
164
|
+
.hydrate(resource: @resource, model: @resource.model, user: @resource.user, view: view)
|
|
164
165
|
.component_for_view(view)
|
|
165
|
-
.new(field: field, resource: @resource, index: index)
|
|
166
|
+
.new(field: field, resource: @resource, index: index, compact: sidebar.present?)
|
|
166
167
|
%>
|
|
167
168
|
<% end %>
|
|
168
169
|
</div>
|
|
169
170
|
<% end %>
|
|
170
171
|
<% end %>
|
|
172
|
+
<% if sidebar.present? %>
|
|
173
|
+
<% c.sidebar do %>
|
|
174
|
+
<%= render sidebar_component %>
|
|
175
|
+
<% end %>
|
|
176
|
+
<% end %>
|
|
171
177
|
<% end %>
|
|
172
178
|
<% if @reflection.blank? %>
|
|
173
179
|
<%= content_tag :div, class: 'space-y-12 mt-12' do %>
|
|
@@ -26,7 +26,7 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
|
|
|
26
26
|
|
|
27
27
|
def back_path
|
|
28
28
|
if via_resource?
|
|
29
|
-
helpers.resource_path(model:
|
|
29
|
+
helpers.resource_path(model: association_resource.model_class, resource: association_resource, resource_id: params[:via_resource_id])
|
|
30
30
|
else
|
|
31
31
|
helpers.resources_path(resource: @resource)
|
|
32
32
|
end
|
|
@@ -48,11 +48,19 @@ module Avo
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def set_action
|
|
51
|
-
action_class = params[:action_id].gsub("avo_actions_", "").camelize.safe_constantize
|
|
52
|
-
|
|
53
51
|
@action = action_class.new(model: @model, resource: @resource, user: _current_user, view: :edit)
|
|
54
52
|
end
|
|
55
53
|
|
|
54
|
+
def action_class
|
|
55
|
+
klass_name = params[:action_id].gsub("avo_actions_", "").camelize
|
|
56
|
+
|
|
57
|
+
valid_klass = Avo::BaseAction.descendants.find do |action|
|
|
58
|
+
action.to_s == klass_name
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
valid_klass
|
|
62
|
+
end
|
|
63
|
+
|
|
56
64
|
def respond(response)
|
|
57
65
|
response[:type] ||= :reload
|
|
58
66
|
messages = get_messages response
|
|
@@ -143,10 +143,12 @@ module Avo
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
def set_related_model
|
|
146
|
+
association_name = BaseResource.valid_association_name(@model, params[:related_name])
|
|
147
|
+
|
|
146
148
|
@related_model = if @field.is_a? Avo::Fields::HasOneField
|
|
147
|
-
@model.send
|
|
149
|
+
@model.send association_name
|
|
148
150
|
else
|
|
149
|
-
eager_load_files(@related_resource, @model.send(
|
|
151
|
+
eager_load_files(@related_resource, @model.send(association_name)).find params[:related_id]
|
|
150
152
|
end
|
|
151
153
|
end
|
|
152
154
|
|
|
@@ -21,7 +21,8 @@ module Avo
|
|
|
21
21
|
@resource = @related_resource
|
|
22
22
|
@parent_model = @parent_resource.class.find_scope.find(params[:id])
|
|
23
23
|
@parent_resource.hydrate(model: @parent_model)
|
|
24
|
-
|
|
24
|
+
association_name = BaseResource.valid_association_name(@parent_model, params[:related_name])
|
|
25
|
+
@query = @authorization.apply_policy @parent_model.send(association_name)
|
|
25
26
|
@association_field = @parent_resource.get_field params[:related_name]
|
|
26
27
|
|
|
27
28
|
if @association_field.present? && @association_field.scope.present?
|
|
@@ -57,10 +58,12 @@ module Avo
|
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
def create
|
|
61
|
+
association_name = BaseResource.valid_association_name(@model, params[:related_name])
|
|
62
|
+
|
|
60
63
|
if reflection_class == "HasManyReflection"
|
|
61
|
-
@model.send(
|
|
64
|
+
@model.send(association_name) << @attachment_model
|
|
62
65
|
else
|
|
63
|
-
@model.send("#{
|
|
66
|
+
@model.send("#{association_name}=", @attachment_model)
|
|
64
67
|
end
|
|
65
68
|
|
|
66
69
|
respond_to do |format|
|
|
@@ -73,10 +76,12 @@ module Avo
|
|
|
73
76
|
end
|
|
74
77
|
|
|
75
78
|
def destroy
|
|
79
|
+
association_name = BaseResource.valid_association_name(@model, params[:related_name])
|
|
80
|
+
|
|
76
81
|
if reflection_class == "HasManyReflection"
|
|
77
|
-
@model.send(
|
|
82
|
+
@model.send(association_name).delete @attachment_model
|
|
78
83
|
else
|
|
79
|
-
@model.send("#{
|
|
84
|
+
@model.send("#{association_name}=", nil)
|
|
80
85
|
end
|
|
81
86
|
|
|
82
87
|
respond_to do |format|
|
|
@@ -8,8 +8,9 @@ module Avo
|
|
|
8
8
|
|
|
9
9
|
def create
|
|
10
10
|
blob = ActiveStorage::Blob.create_and_upload! io: params[:file], filename: params[:filename]
|
|
11
|
+
association_name = BaseResource.valid_attachment_name(@model, params[:attachment_key])
|
|
11
12
|
|
|
12
|
-
@model.send(
|
|
13
|
+
@model.send(association_name).attach blob
|
|
13
14
|
|
|
14
15
|
render json: {
|
|
15
16
|
url: main_app.url_for(blob),
|
|
@@ -84,7 +84,7 @@ module Avo
|
|
|
84
84
|
|
|
85
85
|
# If we're accessing this resource via another resource add the parent to the breadcrumbs.
|
|
86
86
|
if params[:via_resource_class].present? && params[:via_resource_id].present?
|
|
87
|
-
via_resource = Avo::App.
|
|
87
|
+
via_resource = Avo::App.get_resource(params[:via_resource_class]).dup
|
|
88
88
|
via_model = via_resource.class.find_scope.find params[:via_resource_id]
|
|
89
89
|
via_resource.hydrate model: via_model
|
|
90
90
|
|
|
@@ -141,8 +141,9 @@ module Avo
|
|
|
141
141
|
# find the record
|
|
142
142
|
via_resource = ::Avo::App.get_resource_by_model_name(params[:via_relation_class]).dup
|
|
143
143
|
@related_record = via_resource.model_class.find params[:via_resource_id]
|
|
144
|
+
association_name = BaseResource.valid_association_name(@model, params[:via_relation])
|
|
144
145
|
|
|
145
|
-
@model.send(
|
|
146
|
+
@model.send(association_name) << @related_record
|
|
146
147
|
end
|
|
147
148
|
end
|
|
148
149
|
|
|
@@ -370,7 +371,7 @@ module Avo
|
|
|
370
371
|
last_crumb_args = {}
|
|
371
372
|
# If we're accessing this resource via another resource add the parent to the breadcrumbs.
|
|
372
373
|
if params[:via_resource_class].present? && params[:via_resource_id].present?
|
|
373
|
-
via_resource = Avo::App.
|
|
374
|
+
via_resource = Avo::App.get_resource(params[:via_resource_class]).dup
|
|
374
375
|
via_model = via_resource.class.find_scope.find params[:via_resource_id]
|
|
375
376
|
via_resource.hydrate model: via_model
|
|
376
377
|
|
|
@@ -414,9 +415,10 @@ module Avo
|
|
|
414
415
|
# If this is an associated record return to the association show page
|
|
415
416
|
if is_associated_record?
|
|
416
417
|
parent_resource = ::Avo::App.get_resource_by_model_name(params[:via_relation_class]).dup
|
|
418
|
+
association_name = BaseResource.valid_association_name(@model, params[:via_relation])
|
|
417
419
|
|
|
418
420
|
return resource_view_path(
|
|
419
|
-
model: @model.send(
|
|
421
|
+
model: @model.send(association_name),
|
|
420
422
|
resource: parent_resource,
|
|
421
423
|
resource_id: params[:via_resource_id]
|
|
422
424
|
)
|
|
@@ -87,9 +87,14 @@ module Avo
|
|
|
87
87
|
# In these scenarios, try to find the grandparent for the new views where the parent is nil
|
|
88
88
|
# and initialize the parent record with the grandparent attached so the user has the required information
|
|
89
89
|
# to scope the query.
|
|
90
|
+
# Example usage: Got to a project, create a new review, and search for a user.
|
|
90
91
|
if parent.blank? && params[:via_parent_resource_id].present? && params[:via_parent_resource_class].present? && params[:via_relation].present?
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
parent_resource_class = BaseResource.valid_model_class params[:via_parent_resource_class]
|
|
93
|
+
|
|
94
|
+
reflection_class = BaseResource.valid_model_class params[:via_reflection_class]
|
|
95
|
+
|
|
96
|
+
grandparent = parent_resource_class.find params[:via_parent_resource_id]
|
|
97
|
+
parent = reflection_class.new(
|
|
93
98
|
params[:via_relation] => grandparent
|
|
94
99
|
)
|
|
95
100
|
end
|
|
@@ -97,8 +102,10 @@ module Avo
|
|
|
97
102
|
Avo::Hosts::AssociationScopeHost.new(block: attach_scope, query: query, parent: parent).handle
|
|
98
103
|
end
|
|
99
104
|
|
|
105
|
+
# This scope is applied if the search is being performed on a has_many association
|
|
100
106
|
def apply_has_many_scope
|
|
101
|
-
|
|
107
|
+
association_name = BaseResource.valid_association_name(parent, params[:via_association_id])
|
|
108
|
+
scope = parent.send(association_name)
|
|
102
109
|
|
|
103
110
|
Avo::Hosts::SearchScopeHost.new(block: @resource.search_query, params: params, scope: scope).handle
|
|
104
111
|
end
|
|
@@ -157,7 +164,9 @@ module Avo
|
|
|
157
164
|
def fetch_parent
|
|
158
165
|
return unless params[:via_reflection_id].present?
|
|
159
166
|
|
|
160
|
-
|
|
167
|
+
parent_class = BaseResource.valid_model_class params[:via_reflection_class]
|
|
168
|
+
|
|
169
|
+
parent_class.find params[:via_reflection_id]
|
|
161
170
|
end
|
|
162
171
|
end
|
|
163
172
|
end
|
|
@@ -22,13 +22,13 @@ module Avo
|
|
|
22
22
|
|
|
23
23
|
def a_button(**args, &block)
|
|
24
24
|
render Avo::ButtonComponent.new(is_link: false, **args) do
|
|
25
|
-
capture(&block) if block
|
|
25
|
+
capture(&block) if block.present?
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def a_link(path = nil, **args, &block)
|
|
30
30
|
render Avo::ButtonComponent.new(path, is_link: true, **args) do
|
|
31
|
-
capture(&block) if block
|
|
31
|
+
capture(&block) if block.present?
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
@@ -76,7 +76,7 @@ module Avo
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def input_classes(extra_classes = "", has_error: false)
|
|
79
|
-
classes = "appearance-none inline-flex bg-gray-25 disabled:cursor-not-allowed text-gray-600 disabled:opacity-50 rounded py-2 px-3 leading-tight border focus:border-gray-600 focus-visible:ring-0 focus:text-gray-700"
|
|
79
|
+
classes = "appearance-none inline-flex bg-gray-25 disabled:cursor-not-allowed text-gray-600 disabled:opacity-50 rounded py-2 px-3 leading-tight border focus:border-gray-600 focus-visible:ring-0 focus:text-gray-700 placeholder:text-gray-300"
|
|
80
80
|
|
|
81
81
|
classes += if has_error
|
|
82
82
|
" border-red-600"
|
|
@@ -25,8 +25,8 @@ module Avo
|
|
|
25
25
|
capture(&block)
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
alias_method :edit_field_wrapper, :field_wrapper
|
|
29
|
+
alias_method :show_field_wrapper, :field_wrapper
|
|
30
30
|
|
|
31
31
|
def filter_wrapper(name: nil, index: nil, **args, &block)
|
|
32
32
|
render layout: "layouts/avo/filter_wrapper", locals: {
|
data/app/javascript/avo.base.js
CHANGED
|
@@ -53,7 +53,6 @@ window.initTippy = initTippy
|
|
|
53
53
|
ActiveStorage.start()
|
|
54
54
|
|
|
55
55
|
document.addEventListener('turbo:load', () => {
|
|
56
|
-
document.body.classList.remove('turbo-loading')
|
|
57
56
|
initTippy()
|
|
58
57
|
isMac()
|
|
59
58
|
|
|
@@ -64,6 +63,9 @@ document.addEventListener('turbo:load', () => {
|
|
|
64
63
|
scrollTop = 0
|
|
65
64
|
}, 50)
|
|
66
65
|
}
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
document.body.classList.remove('turbo-loading')
|
|
68
|
+
}, 1)
|
|
67
69
|
})
|
|
68
70
|
|
|
69
71
|
document.addEventListener('turbo:frame-load', () => {
|