avo 1.4.3 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +63 -54
- data/README.md +5 -4
- data/app/components/avo/views/resource_index_component.html.erb +3 -3
- data/app/controllers/avo/application_controller.rb +14 -7
- data/app/controllers/avo/relations_controller.rb +1 -1
- data/app/controllers/avo/search_controller.rb +50 -35
- data/app/helpers/avo/application_helper.rb +4 -6
- data/app/packs/entrypoints/application.css +2 -1
- data/app/packs/entrypoints/application.js +3 -0
- data/app/packs/js/controllers/search_controller.js +120 -0
- data/app/packs/js/helpers/debounce_promise.js +13 -0
- data/app/packs/stylesheets/search.css +79 -0
- data/app/views/avo/base/index.html.erb +2 -2
- data/app/views/avo/base/show.html.erb +2 -2
- data/app/views/avo/partials/_global_search.html.erb +17 -0
- data/app/views/avo/partials/_profile_dropdown.html.erb +12 -8
- data/app/views/avo/partials/_resource_search.html.erb +10 -0
- data/app/views/avo/partials/_turbo_frame_wrap.html.erb +3 -0
- data/app/views/layouts/avo/application.html.erb +2 -4
- data/avo.gemspec +1 -0
- data/config/routes.rb +2 -6
- data/lib/avo/app.rb +4 -2
- data/lib/avo/base_resource.rb +85 -7
- data/lib/avo/configuration.rb +2 -0
- data/lib/avo/fields/base_field.rb +6 -0
- data/lib/avo/fields/external_image_field.rb +4 -0
- data/lib/avo/fields/file_field.rb +4 -0
- data/lib/avo/licensing/license.rb +10 -0
- data/lib/avo/licensing/license_manager.rb +0 -2
- data/lib/avo/licensing/pro_license.rb +3 -1
- data/lib/avo/services/authorization_service.rb +1 -1
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/templates/initializer/avo.tt +1 -0
- data/lib/generators/avo/templates/resource/resource.tt +3 -0
- data/lib/generators/avo/templates/tool/sidebar_item.tt +1 -1
- data/public/avo-packs/css/{application-68691c73.css → application-d69fe930.css} +183 -6
- data/public/avo-packs/css/application-d69fe930.css.br +0 -0
- data/public/avo-packs/css/application-d69fe930.css.gz +0 -0
- data/public/avo-packs/css/application-d69fe930.css.map +1 -0
- data/public/avo-packs/css/application-d69fe930.css.map.br +0 -0
- data/public/avo-packs/css/application-d69fe930.css.map.gz +0 -0
- data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js +26 -0
- data/public/avo-packs/js/{application-6a0b9e58526ae6bef242.js.LICENSE.txt → application-7b3d507875f4bc1f6677.js.LICENSE.txt} +0 -0
- data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.br +0 -0
- data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.gz +0 -0
- data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.map +1 -0
- data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.map.br +0 -0
- data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.map.gz +0 -0
- data/public/avo-packs/manifest.json +15 -15
- metadata +35 -15
- data/public/avo-packs/css/application-68691c73.css.br +0 -0
- data/public/avo-packs/css/application-68691c73.css.gz +0 -0
- data/public/avo-packs/css/application-68691c73.css.map +0 -1
- data/public/avo-packs/css/application-68691c73.css.map.br +0 -0
- data/public/avo-packs/css/application-68691c73.css.map.gz +0 -0
- data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js +0 -26
- data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.br +0 -0
- data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.gz +0 -0
- data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.map +0 -1
- data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.map.br +0 -0
- data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.map.gz +0 -0
@@ -31,12 +31,10 @@ module Avo
|
|
31
31
|
render partial: "avo/partials/empty_state", locals: {resource_name: resource_name}
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
"
|
36
|
-
|
37
|
-
|
38
|
-
def turbo_frame_end(name)
|
39
|
-
"</turbo-frame>".html_safe if name.present?
|
34
|
+
def turbo_frame_wrap(name, &block)
|
35
|
+
render layout: "avo/partials/turbo_frame_wrap", locals: {name: name} do
|
36
|
+
capture(&block)
|
37
|
+
end
|
40
38
|
end
|
41
39
|
|
42
40
|
def a_button(label = nil, **args, &block)
|
@@ -4,7 +4,7 @@
|
|
4
4
|
@import './../../../node_modules/tippy.js/themes/light.css';
|
5
5
|
@import './../../../node_modules/trix/dist/trix.css';
|
6
6
|
@import './../../../node_modules/flatpickr/dist/flatpickr.css';
|
7
|
-
|
7
|
+
@import './../../../node_modules/@algolia/autocomplete-theme-classic/dist/theme.css';
|
8
8
|
|
9
9
|
@import './../stylesheets/tailwindcss/base.css';
|
10
10
|
|
@@ -15,6 +15,7 @@
|
|
15
15
|
@import './../stylesheets/loader.css';
|
16
16
|
@import './../stylesheets/pagination.css';
|
17
17
|
@import './../stylesheets/breadcrumbs.css';
|
18
|
+
@import './../stylesheets/search.css';
|
18
19
|
|
19
20
|
@import './../stylesheets/components/status.css';
|
20
21
|
@import './../stylesheets/components/code.css';
|
@@ -45,6 +45,9 @@ document.addEventListener('turbo:load', () => {
|
|
45
45
|
})
|
46
46
|
document.addEventListener('turbo:visit', () => document.body.classList.add('turbo-loading'))
|
47
47
|
document.addEventListener('turbo:submit-start', () => document.body.classList.add('turbo-loading'))
|
48
|
+
document.addEventListener('turbo:before-cache', () => {
|
49
|
+
document.querySelectorAll('[data-turbo-remove-before-cache]').forEach((element) => element.remove())
|
50
|
+
})
|
48
51
|
|
49
52
|
// Uncomment to copy all static images under ../images to the output folder and reference
|
50
53
|
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
|
@@ -0,0 +1,120 @@
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
2
|
+
import * as Mousetrap from 'mousetrap'
|
3
|
+
import { Controller } from 'stimulus'
|
4
|
+
import { Turbo } from '@hotwired/turbo-rails'
|
5
|
+
import { autocomplete } from '@algolia/autocomplete-js'
|
6
|
+
import debouncePromise from '@/js/helpers/debounce_promise'
|
7
|
+
|
8
|
+
export default class extends Controller {
|
9
|
+
static targets = ['autocomplete', 'button']
|
10
|
+
|
11
|
+
debouncedFetch = debouncePromise(fetch, this.debounceTimeout)
|
12
|
+
|
13
|
+
get translationKeys() {
|
14
|
+
let keys
|
15
|
+
try {
|
16
|
+
keys = JSON.parse(this.autocompleteTarget.dataset.translationKeys)
|
17
|
+
} catch (error) {
|
18
|
+
keys = {}
|
19
|
+
}
|
20
|
+
|
21
|
+
return keys
|
22
|
+
}
|
23
|
+
|
24
|
+
get debounceTimeout() {
|
25
|
+
return this.autocompleteTarget.dataset.debounceTimeout
|
26
|
+
}
|
27
|
+
|
28
|
+
get searchResource() {
|
29
|
+
return this.autocompleteTarget.dataset.searchResource
|
30
|
+
}
|
31
|
+
|
32
|
+
get isGlobalSearch() {
|
33
|
+
return this.searchResource === 'global'
|
34
|
+
}
|
35
|
+
|
36
|
+
get searchUrl() {
|
37
|
+
return this.isGlobalSearch ? '/avo/avo_api/search' : `/avo/avo_api/${this.searchResource}/search`
|
38
|
+
}
|
39
|
+
|
40
|
+
addSource(resourceName, data) {
|
41
|
+
const that = this
|
42
|
+
|
43
|
+
return {
|
44
|
+
sourceId: resourceName,
|
45
|
+
getItems: () => data.results,
|
46
|
+
onSelect({ item }) {
|
47
|
+
Turbo.visit(item._url, { action: 'replace' })
|
48
|
+
},
|
49
|
+
templates: {
|
50
|
+
header() {
|
51
|
+
return data.header
|
52
|
+
},
|
53
|
+
item({ item, createElement }) {
|
54
|
+
let element = ''
|
55
|
+
|
56
|
+
if (item._avatar) {
|
57
|
+
let classes
|
58
|
+
|
59
|
+
switch (item._avatar_type) {
|
60
|
+
default:
|
61
|
+
case 'circle':
|
62
|
+
classes = 'rounded-full'
|
63
|
+
break
|
64
|
+
case 'rounded':
|
65
|
+
classes = 'rounded'
|
66
|
+
break
|
67
|
+
}
|
68
|
+
|
69
|
+
element += `<img src="${item._avatar}" alt="${item._label}" class="flex-shrink-0 w-8 h-8 my-[2px] inline mr-2 ${classes}" />`
|
70
|
+
}
|
71
|
+
element += `<div>${item._label}`
|
72
|
+
|
73
|
+
if (item._description) {
|
74
|
+
element += `<div class="aa-ItemDescription">${item._description}</div>`
|
75
|
+
}
|
76
|
+
|
77
|
+
element += '</div>'
|
78
|
+
|
79
|
+
return createElement('div', {
|
80
|
+
class: 'flex',
|
81
|
+
dangerouslySetInnerHTML: {
|
82
|
+
__html: element,
|
83
|
+
},
|
84
|
+
})
|
85
|
+
},
|
86
|
+
noResults() {
|
87
|
+
return that.translationKeys.no_item_found.replace('%{item}', resourceName)
|
88
|
+
},
|
89
|
+
},
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
showSearchPanel() {
|
94
|
+
this.autocompleteTarget.querySelector('button').click()
|
95
|
+
}
|
96
|
+
|
97
|
+
connect() {
|
98
|
+
const that = this
|
99
|
+
|
100
|
+
this.buttonTarget.onclick = () => this.showSearchPanel()
|
101
|
+
|
102
|
+
if (this.isGlobalSearch) {
|
103
|
+
Mousetrap.bind(['command+k', 'ctrl+k'], () => this.showSearchPanel())
|
104
|
+
}
|
105
|
+
|
106
|
+
autocomplete({
|
107
|
+
container: this.autocompleteTarget,
|
108
|
+
placeholder: 'Search',
|
109
|
+
openOnFocus: true,
|
110
|
+
detachedMediaQuery: '',
|
111
|
+
getSources: ({ query }) => {
|
112
|
+
const endpoint = `${that.searchUrl}?q=${query}`
|
113
|
+
|
114
|
+
return that.debouncedFetch(endpoint)
|
115
|
+
.then((response) => response.json())
|
116
|
+
.then((data) => Object.keys(data).map((resourceName) => that.addSource(resourceName, data[resourceName])))
|
117
|
+
},
|
118
|
+
})
|
119
|
+
}
|
120
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
:root {
|
2
|
+
--aa-primary-color: --tw-ring-color;
|
3
|
+
--aa-selected-color: --tw-ring-color;
|
4
|
+
--aa-primary-color-rgb: 5, 150, 105;
|
5
|
+
}
|
6
|
+
|
7
|
+
.global-search {
|
8
|
+
.aa-DetachedSearchButton:focus,
|
9
|
+
.aa-DetachedSearchButton {
|
10
|
+
border: none !important;
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
.resource-search {
|
15
|
+
.aa-Autocomplete {
|
16
|
+
@apply w-full;
|
17
|
+
}
|
18
|
+
|
19
|
+
.aa-DetachedSearchButton {
|
20
|
+
@apply rounded-full border-gray-300;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
.aa-SourceHeader {
|
25
|
+
@apply uppercase text-xs font-semibold;
|
26
|
+
}
|
27
|
+
|
28
|
+
.aa-DetachedFormContainer,
|
29
|
+
.aa-DetachedContainer .aa-Panel {
|
30
|
+
@apply bg-blue-gray-100 border-b-0;
|
31
|
+
}
|
32
|
+
|
33
|
+
.aa-Form {
|
34
|
+
&:focus-within {
|
35
|
+
@apply ring-0 !important;
|
36
|
+
}
|
37
|
+
|
38
|
+
input {
|
39
|
+
@apply focus:ring-0 !important;
|
40
|
+
}
|
41
|
+
&:focus-within {
|
42
|
+
.aa-InputWrapperPrefix{
|
43
|
+
.aa-SubmitButton {
|
44
|
+
@apply ring-0;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
.aa-Input {
|
50
|
+
@apply focus:ring-green-600;
|
51
|
+
}
|
52
|
+
|
53
|
+
.aa-DetachedContainer {
|
54
|
+
.aa-PanelLayout {
|
55
|
+
@apply pt-0 space-y-3;
|
56
|
+
}
|
57
|
+
|
58
|
+
.aa-Source {
|
59
|
+
.aa-List {
|
60
|
+
@apply space-y-1;
|
61
|
+
|
62
|
+
.aa-Item {
|
63
|
+
@apply bg-white rounded-md px-3 py-4 shadow;
|
64
|
+
|
65
|
+
.aa-ItemDescription {
|
66
|
+
@apply text-gray-500 text-sm;
|
67
|
+
}
|
68
|
+
|
69
|
+
&[aria-selected=true]{
|
70
|
+
@apply bg-blue-700 text-white;
|
71
|
+
|
72
|
+
.aa-ItemDescription {
|
73
|
+
@apply text-white;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%=
|
1
|
+
<%= turbo_frame_wrap(params[:turbo_frame]) do %>
|
2
2
|
<%= render Avo::Views::ResourceIndexComponent.new(
|
3
3
|
resource: @resource,
|
4
4
|
resources: @resources,
|
@@ -12,4 +12,4 @@
|
|
12
12
|
parent_model: @parent_model,
|
13
13
|
)
|
14
14
|
%>
|
15
|
-
|
15
|
+
<% end %>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<%=
|
1
|
+
<%= turbo_frame_wrap(params[:turbo_frame]) do %>
|
2
2
|
<%= render Avo::Views::ResourceShowComponent.new(resource: @resource, reflection: @reflection) %>
|
3
|
-
|
3
|
+
<% end %>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<div data-controller="search" class="global-search" data-turbo-remove-before-cache>
|
2
|
+
<div class="inline-block"
|
3
|
+
data-search-target="autocomplete"
|
4
|
+
data-search-resource="global"
|
5
|
+
data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>"}'
|
6
|
+
data-debounce-timeout='<%= Avo.configuration.search_debounce %>'
|
7
|
+
>
|
8
|
+
</div>
|
9
|
+
<div class="relative top-[-5px] inline-flex text-gray-400 text-sm leading-5 py-0.5 px-1.5 border border-gray-300 rounded-md cursor-pointer"
|
10
|
+
data-search-target="button"
|
11
|
+
>
|
12
|
+
<span class="sr-only">Press </span>
|
13
|
+
<kbd class="font-sans"><abbr title="Command" class="no-underline">⌘</abbr></kbd>
|
14
|
+
<span class="sr-only"> and </span>
|
15
|
+
<kbd class="font-sans">K</kbd><span class="sr-only"> to search</span>
|
16
|
+
</div>
|
17
|
+
</div>
|
@@ -1,19 +1,23 @@
|
|
1
1
|
<div data-controller="toggle-panel">
|
2
2
|
<a href="javascript:void(0);" class="flex items-center cursor-pointer font-semibold text-gray-700" data-action="click->toggle-panel#togglePanel">
|
3
|
-
<% if _current_user.respond_to?
|
4
|
-
<%= image_tag _current_user.avatar, class: "h-12 rounded-full border-4 border-white mr-1"
|
3
|
+
<% if _current_user.respond_to?(:avatar) && _current_user.avatar.present? %>
|
4
|
+
<%= image_tag _current_user.avatar, class: "h-12 rounded-full border-4 border-white mr-1" %>
|
5
5
|
<% end %>
|
6
|
-
<% if _current_user.name.present? %>
|
6
|
+
<% if _current_user.respond_to?(:name) && _current_user.name.present? %>
|
7
7
|
<%= _current_user.name %>
|
8
|
-
<% elsif _current_user.email.present? %>
|
8
|
+
<% elsif _current_user.respond_to?(:email) && _current_user.email.present? %>
|
9
9
|
<%= _current_user.email %>
|
10
10
|
<% else %>
|
11
11
|
Avo user
|
12
12
|
<% end %>
|
13
|
-
|
13
|
+
<% if main_app.respond_to?(:destroy_user_session_path) %>
|
14
|
+
<%= svg 'chevron-down', class: "ml-1 h-4" %>
|
15
|
+
<% end %>
|
14
16
|
</a>
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
<% if main_app.respond_to?(:destroy_user_session_path) %>
|
19
|
+
<div class="hidden absolute inset-auto inset-auto right-0 mr-6 mt-0 py-4 bg-white rounded-xl min-w-[200px] shadow-context" data-toggle-panel-target="panel">
|
20
|
+
<%= button_to "Sign out", main_app.destroy_user_session_path, method: :delete, form: { "data-turbo" => "false" }, class: "appearance-none bg-white text-left cursor-pointer text-green-600 font-semibold hover:text-white hover:bg-green-500 block px-4 py-1 w-full" %>
|
21
|
+
</div>
|
22
|
+
<% end %>
|
19
23
|
</div>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div data-controller="search" class="resource-search flex items-center h-full w-full" data-turbo-remove-before-cache>
|
2
|
+
<div class="w-full"
|
3
|
+
data-search-target="autocomplete"
|
4
|
+
data-search-resource="<%= resource %>"
|
5
|
+
data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>"}'
|
6
|
+
data-debounce-timeout='<%= Avo.configuration.search_debounce %>'
|
7
|
+
>
|
8
|
+
</div>
|
9
|
+
<div class="relative inline-flex text-gray-400 text-sm border border-gray-300 rounded-full cursor-pointer" data-search-target="button"></div>
|
10
|
+
</div>
|
@@ -23,10 +23,8 @@
|
|
23
23
|
<div>
|
24
24
|
<%= render partial: "avo/partials/header" %>
|
25
25
|
</div>
|
26
|
-
<div class="flex-1 flex
|
27
|
-
|
28
|
-
<%# <resources-search :global="true"> %>
|
29
|
-
</div>
|
26
|
+
<div class="flex-1 flex ml-4 pl-4">
|
27
|
+
<%= render partial: "avo/partials/global_search" if ::Avo::App.license.has_with_trial(:global_search) %>
|
30
28
|
</div>
|
31
29
|
<div class="align-end">
|
32
30
|
<%= render partial: "avo/partials/profile_dropdown" %>
|
data/avo.gemspec
CHANGED
data/config/routes.rb
CHANGED
@@ -4,6 +4,8 @@ Avo::Engine.routes.draw do
|
|
4
4
|
get "resources", to: redirect("/avo")
|
5
5
|
|
6
6
|
scope "avo_api", as: "avo_api" do
|
7
|
+
get "/search", to: "search#index"
|
8
|
+
get "/:resource_name/search", to: "search#show"
|
7
9
|
post "/resources/:resource_name/:id/attachments/", to: "attachments#create"
|
8
10
|
end
|
9
11
|
|
@@ -27,10 +29,4 @@ Avo::Engine.routes.draw do
|
|
27
29
|
post "/:resource_name/:id/:related_name", to: "relations#create"
|
28
30
|
delete "/:resource_name/:id/:related_name/:related_id", to: "relations#destroy"
|
29
31
|
end
|
30
|
-
|
31
|
-
# get '/avo-api/search', to: 'search#index'
|
32
|
-
# get '/avo-api/:resource_name/search', to: 'search#resource'
|
33
|
-
|
34
|
-
# Tools
|
35
|
-
# get '/avo-tools/resource-overview', to: 'resource_overview#index'
|
36
32
|
end
|
data/lib/avo/app.rb
CHANGED
@@ -6,6 +6,7 @@ module Avo
|
|
6
6
|
class_attribute :request, default: nil
|
7
7
|
class_attribute :context, default: nil
|
8
8
|
class_attribute :license, default: nil
|
9
|
+
class_attribute :current_user, default: nil
|
9
10
|
|
10
11
|
class << self
|
11
12
|
def boot
|
@@ -20,9 +21,10 @@ module Avo
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
|
-
def init(request:, context:)
|
24
|
+
def init(request:, context:, current_user:)
|
24
25
|
self.request = request
|
25
26
|
self.context = context
|
27
|
+
self.current_user = current_user
|
26
28
|
|
27
29
|
self.license = Licensing::LicenseManager.new(Licensing::HQ.new(request).response).license
|
28
30
|
|
@@ -117,7 +119,7 @@ module Avo
|
|
117
119
|
|
118
120
|
def get_available_resources(user = nil)
|
119
121
|
resources.select do |resource|
|
120
|
-
Services::AuthorizationService.authorize user, resource.
|
122
|
+
Services::AuthorizationService.authorize user, resource.model_class, Avo.configuration.authorization_methods.stringify_keys["index"], raise_exception: false
|
121
123
|
end
|
122
124
|
.sort_by { |r| r.name }
|
123
125
|
end
|
data/lib/avo/base_resource.rb
CHANGED
@@ -11,7 +11,7 @@ module Avo
|
|
11
11
|
|
12
12
|
class_attribute :id, default: :id
|
13
13
|
class_attribute :title, default: :id
|
14
|
-
class_attribute :
|
14
|
+
class_attribute :search_query, default: nil
|
15
15
|
class_attribute :includes, default: []
|
16
16
|
class_attribute :model_class
|
17
17
|
class_attribute :translation_key
|
@@ -42,6 +42,18 @@ module Avo
|
|
42
42
|
|
43
43
|
self.filters_loader.use filter_class
|
44
44
|
end
|
45
|
+
|
46
|
+
def scope
|
47
|
+
authorization.apply_policy model_class
|
48
|
+
end
|
49
|
+
|
50
|
+
def authorization
|
51
|
+
Avo::Services::AuthorizationService.new Avo::App.current_user
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
self.class.model_class = model_class
|
45
57
|
end
|
46
58
|
|
47
59
|
def hydrate(model: nil, view: nil, user: nil, params: nil)
|
@@ -65,7 +77,7 @@ module Avo
|
|
65
77
|
field.hydrate(resource: self, panel_name: default_panel_name, user: user)
|
66
78
|
end
|
67
79
|
|
68
|
-
if Avo::App.license.
|
80
|
+
if Avo::App.license.has_with_trial(:custom_fields)
|
69
81
|
fields = fields.reject do |field|
|
70
82
|
field.custom?
|
71
83
|
end
|
@@ -96,9 +108,9 @@ module Avo
|
|
96
108
|
end
|
97
109
|
end
|
98
110
|
|
99
|
-
|
100
|
-
|
101
|
-
|
111
|
+
hydrate_fields(model: @model, view: @view)
|
112
|
+
|
113
|
+
fields
|
102
114
|
end
|
103
115
|
|
104
116
|
def get_grid_fields
|
@@ -119,8 +131,16 @@ module Avo
|
|
119
131
|
self.class.actions_loader.bag
|
120
132
|
end
|
121
133
|
|
134
|
+
def hydrate_fields(model: nil, view: nil)
|
135
|
+
fields.map do |field|
|
136
|
+
field.hydrate(model: @model, view: @view, resource: self)
|
137
|
+
end
|
138
|
+
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
122
142
|
def default_panel_name
|
123
|
-
return @params[:related_name].capitalize if @params[:related_name].present?
|
143
|
+
return @params[:related_name].capitalize if @params.present? && @params[:related_name].present?
|
124
144
|
|
125
145
|
case @view
|
126
146
|
when :show
|
@@ -226,7 +246,11 @@ module Avo
|
|
226
246
|
|
227
247
|
def fill_model(model, params)
|
228
248
|
# Map the received params to their actual fields
|
229
|
-
fields_by_database_id = get_field_definitions
|
249
|
+
fields_by_database_id = get_field_definitions
|
250
|
+
.reject do |field|
|
251
|
+
field.computed
|
252
|
+
end
|
253
|
+
.map { |field| [field.database_id(model).to_s, field] }.to_h
|
230
254
|
|
231
255
|
params.each do |key, value|
|
232
256
|
field = fields_by_database_id[key]
|
@@ -301,5 +325,59 @@ module Avo
|
|
301
325
|
end
|
302
326
|
end
|
303
327
|
end
|
328
|
+
|
329
|
+
def avo_path
|
330
|
+
"#{Avo.configuration.root_path}/resources/#{model_class.model_name.route_key}/#{model.id}"
|
331
|
+
end
|
332
|
+
|
333
|
+
def label_field
|
334
|
+
get_field_definitions.find do |field|
|
335
|
+
field.as_label.present?
|
336
|
+
end
|
337
|
+
rescue
|
338
|
+
nil
|
339
|
+
end
|
340
|
+
|
341
|
+
def label
|
342
|
+
label_field.value || model_title
|
343
|
+
rescue
|
344
|
+
model_title
|
345
|
+
end
|
346
|
+
|
347
|
+
def avatar_field
|
348
|
+
get_field_definitions.find do |field|
|
349
|
+
field.as_avatar.present?
|
350
|
+
end
|
351
|
+
rescue
|
352
|
+
nil
|
353
|
+
end
|
354
|
+
|
355
|
+
def avatar
|
356
|
+
return avatar_field.to_image if avatar_field.respond_to? :to_image
|
357
|
+
|
358
|
+
avatar_field.value
|
359
|
+
rescue
|
360
|
+
nil
|
361
|
+
end
|
362
|
+
|
363
|
+
def avatar_type
|
364
|
+
avatar_field.as_avatar
|
365
|
+
rescue
|
366
|
+
nil
|
367
|
+
end
|
368
|
+
|
369
|
+
def description_field
|
370
|
+
get_field_definitions.find do |field|
|
371
|
+
field.as_description.present?
|
372
|
+
end
|
373
|
+
rescue
|
374
|
+
nil
|
375
|
+
end
|
376
|
+
|
377
|
+
def description
|
378
|
+
description_field.value
|
379
|
+
rescue
|
380
|
+
nil
|
381
|
+
end
|
304
382
|
end
|
305
383
|
end
|