avo 2.1.2.pre1 → 2.2.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 -1
- data/Gemfile.lock +7 -1
- data/app/components/avo/button_component.rb +6 -2
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +1 -0
- data/app/components/avo/fields/belongs_to_field/edit_component.rb +5 -2
- data/app/components/avo/filters_component.html.erb +19 -20
- data/app/components/avo/index/grid_item_component.html.erb +3 -1
- data/app/components/avo/index/table_row_component.html.erb +7 -5
- data/app/components/avo/panel_component.html.erb +4 -4
- data/app/components/avo/sidebar_profile_component.html.erb +14 -10
- data/app/components/avo/views/resource_edit_component.html.erb +2 -2
- data/app/components/avo/views/resource_new_component.html.erb +2 -2
- data/app/components/avo/views/resource_show_component.html.erb +2 -2
- data/app/controllers/avo/base_controller.rb +1 -1
- data/app/javascript/js/controllers/loading_button_controller.js +47 -10
- data/app/javascript/js/controllers/search_controller.js +28 -10
- data/app/views/avo/partials/_table_header.html.erb +12 -13
- data/app/views/layouts/avo/application.html.erb +3 -0
- data/bin/helpers.rb +7 -1
- data/bin/init +2 -2
- data/lib/avo/app.rb +7 -3
- data/lib/avo/base_resource.rb +1 -0
- data/lib/avo/dynamic_router.rb +1 -1
- data/lib/avo/engine.rb +6 -16
- data/lib/avo/fields/belongs_to_field.rb +2 -0
- data/lib/avo/reloader.rb +51 -0
- data/lib/avo/version.rb +1 -1
- data/public/avo-assets/avo.css +2 -2
- data/public/avo-assets/avo.js +2 -2
- data/public/avo-assets/avo.js.map +2 -2
- metadata +5 -7
- data/app/assets/builds/avo.css +0 -8810
- data/app/assets/builds/avo.js +0 -423
- data/app/assets/builds/avo.js.map +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ed006aa7c57368aa03156290f54f757484daff2434ad0f235fa1cdabd0489e4
|
4
|
+
data.tar.gz: 5596ead68c48c161977b477ad5b689be6228957b56f818d0a2ba015f3bf250be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68a9010fd5eec2c2c45527cc3bb46c751eefbde06894d4e8857f9d86c8417fdc13d835422479efd5361e16c677ecfa57a3ae0a375b4cd84598d5e88bd85dd2bd
|
7
|
+
data.tar.gz: e2adc97bfcac2b881d9780d60bdc323186362f76c3139f03b0282ac1275f64cba2555c46ad6617aab3fff886c12e1322debbd1e7b38cd107f082129a6f392900
|
data/Gemfile
CHANGED
@@ -34,7 +34,7 @@ gem "puma", "~> 5.6.4"
|
|
34
34
|
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
35
35
|
# gem "jbuilder", "~> 2.7"
|
36
36
|
# Use Redis adapter to run Action Cable in production
|
37
|
-
|
37
|
+
gem 'redis', '~> 4.0'
|
38
38
|
# Use Active Model has_secure_password
|
39
39
|
# gem 'bcrypt', '~> 3.1.7'
|
40
40
|
|
@@ -79,6 +79,8 @@ group :development do
|
|
79
79
|
# gem 'pry-rails'
|
80
80
|
|
81
81
|
gem 'htmlbeautifier'
|
82
|
+
|
83
|
+
gem "hotwire-livereload", "~> 1.1"
|
82
84
|
end
|
83
85
|
|
84
86
|
group :development, :test do
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (2.
|
4
|
+
avo (2.2.1)
|
5
5
|
active_link_to
|
6
6
|
addressable
|
7
7
|
breadcrumbs_on_rails
|
@@ -183,6 +183,9 @@ GEM
|
|
183
183
|
hashdiff (1.0.1)
|
184
184
|
hightop (0.3.0)
|
185
185
|
activesupport (>= 5.2)
|
186
|
+
hotwire-livereload (1.1.0)
|
187
|
+
listen (>= 3.0.0)
|
188
|
+
rails (>= 6.0.0)
|
186
189
|
hotwire-rails (0.1.3)
|
187
190
|
rails (>= 6.0.0)
|
188
191
|
stimulus-rails
|
@@ -297,6 +300,7 @@ GEM
|
|
297
300
|
rb-fsevent (0.11.0)
|
298
301
|
rb-inotify (0.10.1)
|
299
302
|
ffi (~> 1.0)
|
303
|
+
redis (4.6.0)
|
300
304
|
regexp_parser (2.2.0)
|
301
305
|
responders (3.0.1)
|
302
306
|
actionpack (>= 5.0)
|
@@ -435,6 +439,7 @@ DEPENDENCIES
|
|
435
439
|
gem-release
|
436
440
|
groupdate
|
437
441
|
hightop
|
442
|
+
hotwire-livereload (~> 1.1)
|
438
443
|
hotwire-rails
|
439
444
|
htmlbeautifier
|
440
445
|
httparty
|
@@ -452,6 +457,7 @@ DEPENDENCIES
|
|
452
457
|
rails (~> 6.1.0)
|
453
458
|
rails-controller-testing
|
454
459
|
ransack
|
460
|
+
redis (~> 4.0)
|
455
461
|
rspec-rails (~> 4.0.0)
|
456
462
|
rubocop
|
457
463
|
rubocop-shopify
|
@@ -24,8 +24,12 @@ class Avo::ButtonComponent < ViewComponent::Base
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def args
|
27
|
-
if @args[:
|
27
|
+
if @args[:loading]
|
28
28
|
@args[:"data-controller"] = "loading-button"
|
29
|
+
|
30
|
+
if @args[:confirm]
|
31
|
+
@args[:"data-avo-confirm"] = @args.delete(:confirm)
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
@args[:class] = button_classes
|
@@ -34,7 +38,7 @@ class Avo::ButtonComponent < ViewComponent::Base
|
|
34
38
|
end
|
35
39
|
|
36
40
|
def button_classes
|
37
|
-
classes = "button-component inline-flex flex-grow-0 items-center text-sm font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 transform transition duration-100 cursor-pointer disabled:cursor-not-allowed disabled:opacity-
|
41
|
+
classes = "button-component inline-flex flex-grow-0 items-center text-sm font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 transform transition duration-100 cursor-pointer disabled:cursor-not-allowed disabled:opacity-70 border justify-center active:outline active:outline-1 #{@class}"
|
38
42
|
|
39
43
|
classes += " rounded" if @rounded.present?
|
40
44
|
|
@@ -7,6 +7,7 @@
|
|
7
7
|
></div>
|
8
8
|
<div class="relative w-full" autocomplete="off">
|
9
9
|
<%= @form.text_field @foreign_key,
|
10
|
+
type: :text,
|
10
11
|
value: field_label,
|
11
12
|
class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
|
12
13
|
placeholder: @field.placeholder,
|
@@ -9,8 +9,11 @@ class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
|
|
9
9
|
|
10
10
|
def disabled
|
11
11
|
return true if @field.readonly
|
12
|
-
|
13
|
-
|
12
|
+
|
13
|
+
# When visiting the record through it's association we keep the field disabled by default
|
14
|
+
# We make an exception when the user deliberately instructs Avo to allow detaching in this scenario
|
15
|
+
return !@field.allow_via_detaching if @field.target_resource.present? && @field.target_resource.model_class.name == params[:via_resource_class]
|
16
|
+
return !@field.allow_via_detaching if @field.id.to_s == params[:via_relation].to_s
|
14
17
|
|
15
18
|
false
|
16
19
|
end
|
@@ -7,31 +7,30 @@
|
|
7
7
|
title: t('avo.click_to_reveal_filters'),
|
8
8
|
'data-button': 'resource-filters',
|
9
9
|
'data-action': 'click->toggle-panel#togglePanel',
|
10
|
-
'data-tippy': 'tooltip' do
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
'data-tippy': 'tooltip' do %>
|
11
|
+
<%= t 'avo.filters' %>
|
12
|
+
<% if params[:filters].present? %>
|
13
|
+
<span class="ml-1">(<%=JSON.parse(Base64.decode64(params[:filters])).count%> applied)</span>
|
14
|
+
<% end %>
|
15
15
|
<% end %>
|
16
|
-
|
17
|
-
<div
|
16
|
+
<div
|
18
17
|
class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-20 shadow-modal rounded hidden divide-y divide-gray-300"
|
19
18
|
data-toggle-panel-target="panel"
|
20
19
|
>
|
21
|
-
|
22
|
-
|
23
|
-
<% end %>
|
24
|
-
<div class="p-4 border-gray-300 border-t">
|
25
|
-
<% if params[:filters].present? %>
|
26
|
-
<%= a_link helpers.resources_path(resource: @resource, filters: nil, keep_query_params: true), color: :gray, size: :sm, class: 'w-full justify-center' do %>
|
27
|
-
<%= t('avo.reset_filters') %>
|
28
|
-
<% end %>
|
29
|
-
<% else %>
|
30
|
-
<%= a_button class: 'w-full justify-center', disabled: true do %>
|
31
|
-
<%= t('avo.reset_filters') %>
|
32
|
-
<% end %>
|
20
|
+
<% @filters.each do |filter| %>
|
21
|
+
<%= render partial: filter.class.template, locals: {filter: filter} %>
|
33
22
|
<% end %>
|
23
|
+
<div class="p-4 border-gray-300 border-t">
|
24
|
+
<% if params[:filters].present? %>
|
25
|
+
<%= a_link helpers.resources_path(resource: @resource, filters: nil, keep_query_params: true), color: :gray, size: :sm, class: 'w-full justify-center' do %>
|
26
|
+
<%= t('avo.reset_filters') %>
|
27
|
+
<% end %>
|
28
|
+
<% else %>
|
29
|
+
<%= a_button class: 'w-full justify-center', disabled: true do %>
|
30
|
+
<%= t('avo.reset_filters') %>
|
31
|
+
<% end %>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
34
|
</div>
|
35
35
|
</div>
|
36
36
|
</div>
|
37
|
-
</div>
|
@@ -3,7 +3,9 @@
|
|
3
3
|
<%== item_selector_init @resource %>
|
4
4
|
>
|
5
5
|
<div class="relative w-full pb-3/4 rounded-t overflow-hidden">
|
6
|
-
|
6
|
+
<% if @resource.record_selector %>
|
7
|
+
<%== item_selector_input floating: true, size: :lg %>
|
8
|
+
<% end %>
|
7
9
|
<% if cover.blank? %>
|
8
10
|
<%= link_to cover.link_to_resource do %>
|
9
11
|
<%= render Avo::Index::GridCoverEmptyStateComponent.new %>
|
@@ -2,11 +2,13 @@
|
|
2
2
|
class="bg-white hover:bg-blue-50 hover:shadow-row hover:z-[21] relative z-20 border-b"
|
3
3
|
<%== item_selector_init @resource %>
|
4
4
|
>
|
5
|
-
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
<% if @resource.record_selector %>
|
6
|
+
<td class="w-10">
|
7
|
+
<div class="flex justify-center h-full">
|
8
|
+
<%== item_selector_input floating: false %>
|
9
|
+
</div>
|
10
|
+
</td>
|
11
|
+
<% end %>
|
10
12
|
<% @resource.get_fields(reflection: @reflection).each_with_index do |field, index| %>
|
11
13
|
<%= render field.component_for_view(:index).new(field: field, resource: @resource, index: index, parent_model: @parent_model) %>
|
12
14
|
<% end %>
|
@@ -1,19 +1,19 @@
|
|
1
1
|
<div <%== data_attributes %>>
|
2
2
|
<% if render_header? %>
|
3
3
|
<div class="<%= white_panel_classes %> p-4 flex-1 flex flex-col xl:flex-row justify-between mb-6">
|
4
|
-
<div class="overflow-hidden mr-4">
|
4
|
+
<div class="overflow-hidden mr-4 flex flex-col">
|
5
5
|
<% if display_breadcrumbs? %>
|
6
6
|
<div class="breadcrumbs truncate mb-2">
|
7
7
|
<%= helpers.render_breadcrumbs(separator: helpers.svg('chevron-right', class: 'inline-block h-3 stroke-current relative top-[-1px] ml-1' )) if Avo.configuration.display_breadcrumbs %>
|
8
8
|
</div>
|
9
9
|
<% end %>
|
10
10
|
|
11
|
-
<div class="text-2xl tracking-normal font-semibold text-gray-800 truncate" data-target="title">
|
12
|
-
|
11
|
+
<div class="text-2xl tracking-normal font-semibold text-gray-800 truncate items-center flex flex-1" data-target="title">
|
12
|
+
<span><%= @title %></span>
|
13
13
|
</div>
|
14
14
|
|
15
15
|
<% if description.present? %>
|
16
|
-
<div class="text-base tracking-normal font-medium text-gray-600
|
16
|
+
<div class="text-base tracking-normal font-medium text-gray-600" data-target="description">
|
17
17
|
<%== description %>
|
18
18
|
</div>
|
19
19
|
<% end %>
|
@@ -1,19 +1,23 @@
|
|
1
1
|
<div class="text-black border-t border-gray-200 p-4 flex">
|
2
2
|
<div class="flex-1 flex space-x-4">
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
<% if avatar.present? %>
|
4
|
+
<div class="relative aspect-square w-10 h-10 overflow-hidden rounded">
|
5
|
+
<%= image_tag avatar, class: "object-cover min-w-full min-h-full h-full" %>
|
6
|
+
</div>
|
7
|
+
<% end %>
|
6
8
|
<div class="flex flex-col">
|
7
9
|
<div class="font-medium">
|
8
10
|
<%= name %>
|
9
11
|
</div>
|
10
|
-
|
11
|
-
|
12
|
-
|
12
|
+
<% if title.present? %>
|
13
|
+
<div class="text-xs text-gray-500 uppercase">
|
14
|
+
<%= title %>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
13
17
|
</div>
|
14
18
|
</div>
|
15
|
-
|
16
|
-
|
19
|
+
<% if can_destroy_user? %>
|
20
|
+
<div class="relative" data-controller="toggle-panel">
|
17
21
|
<a class="flex items-center h-full cursor-pointer" data-control="profile-dots" data-action="click->toggle-panel#togglePanel">
|
18
22
|
<%= helpers.svg 'three-dots', class: 'h-4' %>
|
19
23
|
</a>
|
@@ -31,6 +35,6 @@
|
|
31
35
|
<%= helpers.svg 'logout', class: 'h-4 mr-1' %> <%= t('avo.sign_out') %>
|
32
36
|
<% end %>
|
33
37
|
</div>
|
34
|
-
|
35
|
-
|
38
|
+
</div>
|
39
|
+
<% end %>
|
36
40
|
</div>
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<%= t('avo.cancel').capitalize %>
|
14
14
|
<% end %>
|
15
15
|
<% if can_see_the_save_button? %>
|
16
|
-
<%= a_button color: :green,
|
16
|
+
<%= a_button color: :green, loading: true, type: :submit, icon: 'save' do %>
|
17
17
|
<%= t('avo.save').capitalize %>
|
18
18
|
<% end %>
|
19
19
|
<% end %>
|
@@ -24,7 +24,7 @@
|
|
24
24
|
<%= t('avo.cancel').capitalize %>
|
25
25
|
<% end %>
|
26
26
|
<% if can_see_the_save_button? %>
|
27
|
-
<%= a_button color: :green,
|
27
|
+
<%= a_button color: :green, loading: true, type: :submit, icon: 'save' do %>
|
28
28
|
<%= t('avo.save').capitalize %>
|
29
29
|
<% end %>
|
30
30
|
<% end %>
|
@@ -18,7 +18,7 @@
|
|
18
18
|
<%= t('avo.cancel').capitalize %>
|
19
19
|
<% end %>
|
20
20
|
<% if can_see_the_save_button? %>
|
21
|
-
<%= a_button color: 'green',
|
21
|
+
<%= a_button color: 'green', loading: true, type: :submit, icon: 'save' do %>
|
22
22
|
<%= t('avo.save').capitalize %>
|
23
23
|
<% end %>
|
24
24
|
<% end %>
|
@@ -30,7 +30,7 @@
|
|
30
30
|
<%= t('avo.cancel').capitalize %>
|
31
31
|
<% end %>
|
32
32
|
<% if can_see_the_save_button? %>
|
33
|
-
<%= a_button color: :green,
|
33
|
+
<%= a_button color: :green, loading: true, type: :submit, icon: 'save' do %>
|
34
34
|
<%= t('avo.save').capitalize %>
|
35
35
|
<% end %>
|
36
36
|
<% end %>
|
@@ -35,12 +35,12 @@
|
|
35
35
|
method: :delete,
|
36
36
|
local: true,
|
37
37
|
title: t('avo.delete_item', item: @resource.model.model_name.name.downcase).capitalize,
|
38
|
-
|
38
|
+
loading: true,
|
39
|
+
confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
|
39
40
|
color: :red,
|
40
41
|
icon: 'trash',
|
41
42
|
form_class: 'flex flex-col sm:flex-row sm:inline-flex',
|
42
43
|
data: {
|
43
|
-
confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
|
44
44
|
control: :destroy,
|
45
45
|
'resource-id': @resource.model.id,
|
46
46
|
'tippy': 'tooltip',
|
@@ -44,7 +44,7 @@ module Avo
|
|
44
44
|
unless @index_params[:sort_by].eql? :created_at
|
45
45
|
@query = @query.unscope(:order)
|
46
46
|
end
|
47
|
-
@query = @query.order(
|
47
|
+
@query = @query.order("#{@resource.model_class.table_name}.#{@index_params[:sort_by]} #{@index_params[:sort_direction]}")
|
48
48
|
end
|
49
49
|
|
50
50
|
# Apply filters
|
@@ -1,3 +1,4 @@
|
|
1
|
+
/* eslint-disable no-alert */
|
1
2
|
import { Controller } from '@hotwired/stimulus'
|
2
3
|
|
3
4
|
export default class extends Controller {
|
@@ -6,17 +7,53 @@ export default class extends Controller {
|
|
6
7
|
<div class="double-bounce2"></div>
|
7
8
|
</div>`;
|
8
9
|
|
10
|
+
confirmed = false
|
11
|
+
|
9
12
|
connect() {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
this.context.scope.element.addEventListener('click', (e) => {
|
14
|
+
// If the user has to confirm the action
|
15
|
+
if (this.confirmationMessage) {
|
16
|
+
// Intervene only if not confirmed
|
17
|
+
if (!this.confirmed) {
|
18
|
+
e.preventDefault()
|
19
|
+
if (window.confirm(this.confirmationMessage)) {
|
20
|
+
this.applyLoader()
|
21
|
+
}
|
22
|
+
}
|
23
|
+
} else {
|
24
|
+
this.applyLoader()
|
25
|
+
}
|
20
26
|
})
|
21
27
|
}
|
28
|
+
|
29
|
+
get button() {
|
30
|
+
return this.context.scope.element
|
31
|
+
}
|
32
|
+
|
33
|
+
get confirmationMessage() {
|
34
|
+
return this.context.scope.element.getAttribute('data-avo-confirm')
|
35
|
+
}
|
36
|
+
|
37
|
+
applyLoader() {
|
38
|
+
const { button } = this
|
39
|
+
|
40
|
+
button.style.width = `${button.getBoundingClientRect().width}px`
|
41
|
+
button.style.height = `${button.getBoundingClientRect().height}px`
|
42
|
+
button.innerHTML = this.spinnerMarkup
|
43
|
+
button.classList.add('justify-center')
|
44
|
+
|
45
|
+
setTimeout(() => {
|
46
|
+
this.markConfirmed()
|
47
|
+
button.click()
|
48
|
+
button.setAttribute('disabled', 'disabled')
|
49
|
+
}, 1)
|
50
|
+
}
|
51
|
+
|
52
|
+
markConfirmed() {
|
53
|
+
this.confirmed = true
|
54
|
+
}
|
55
|
+
|
56
|
+
markUnconfirmed() {
|
57
|
+
this.confirmed = false
|
58
|
+
}
|
22
59
|
}
|
@@ -88,6 +88,9 @@ export default class extends Controller {
|
|
88
88
|
} else {
|
89
89
|
Turbo.visit(item._url, { action: 'advance' })
|
90
90
|
}
|
91
|
+
|
92
|
+
// On searchable belongs to the class `aa-Detached` remains on the body making it unscrollable
|
93
|
+
document.body.classList.remove('aa-Detached')
|
91
94
|
}
|
92
95
|
|
93
96
|
addSource(resourceName, data) {
|
@@ -102,7 +105,7 @@ export default class extends Controller {
|
|
102
105
|
return `${data.header.toUpperCase()} ${data.help}`
|
103
106
|
},
|
104
107
|
item({ item, createElement }) {
|
105
|
-
|
108
|
+
const children = []
|
106
109
|
|
107
110
|
if (item._avatar) {
|
108
111
|
let classes
|
@@ -117,22 +120,37 @@ export default class extends Controller {
|
|
117
120
|
break
|
118
121
|
}
|
119
122
|
|
120
|
-
|
123
|
+
children.push(
|
124
|
+
createElement('img', {
|
125
|
+
src: item._avatar,
|
126
|
+
alt: item._label,
|
127
|
+
class: `flex-shrink-0 w-8 h-8 my-[2px] inline mr-2 ${classes}`,
|
128
|
+
}),
|
129
|
+
)
|
121
130
|
}
|
122
|
-
element += `<div>${item._label}`
|
123
131
|
|
132
|
+
const labelChildren = [item._label]
|
124
133
|
if (item._description) {
|
125
|
-
|
134
|
+
labelChildren.push(
|
135
|
+
createElement(
|
136
|
+
'div',
|
137
|
+
{
|
138
|
+
class: 'aa-ItemDescription',
|
139
|
+
},
|
140
|
+
item._description,
|
141
|
+
),
|
142
|
+
)
|
126
143
|
}
|
127
144
|
|
128
|
-
|
145
|
+
children.push(createElement('div', null, labelChildren))
|
129
146
|
|
130
|
-
return createElement(
|
131
|
-
|
132
|
-
|
133
|
-
|
147
|
+
return createElement(
|
148
|
+
'div',
|
149
|
+
{
|
150
|
+
class: 'flex',
|
134
151
|
},
|
135
|
-
|
152
|
+
children,
|
153
|
+
)
|
136
154
|
},
|
137
155
|
noResults() {
|
138
156
|
return that.translationKeys.no_item_found.replace(
|
@@ -1,10 +1,11 @@
|
|
1
|
-
|
2
1
|
<thead class="bg-white border-b border-gray-200 pb-1">
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
<% if @resource.record_selector %>
|
3
|
+
<th class="rounded-lg">
|
4
|
+
<%== item_select_all_input %>
|
5
|
+
</th>
|
6
|
+
<% end %>
|
7
|
+
<% fields.each_with_index do |field, index| %>
|
8
|
+
<%
|
8
9
|
if params[:sort_by] == field.id.to_s
|
9
10
|
if params[:sort_direction] == 'asc'
|
10
11
|
sort_by = nil
|
@@ -28,8 +29,7 @@
|
|
28
29
|
sort_by = field.id
|
29
30
|
sort_direction = 'desc'
|
30
31
|
end
|
31
|
-
classes = "text-gray-500 tracking-tight leading-tight text-sm font-semibold"
|
32
|
-
%>
|
32
|
+
classes = "text-gray-500 tracking-tight leading-tight text-sm font-semibold" %>
|
33
33
|
<th class="text-left uppercase px-3 py-4 whitespace-nowrap rounded-l">
|
34
34
|
<% if field.sortable %>
|
35
35
|
<%= link_to params.permit!.merge(sort_by: sort_by, sort_direction: sort_direction), class: "flex items-center #{classes} #{'cursor-pointer' if field.sortable}", 'data-turbo-frame': params[:turbo_frame] do %>
|
@@ -37,14 +37,13 @@
|
|
37
37
|
<%= render partial: 'avo/partials/sortable_component', locals: {field: field} %>
|
38
38
|
<% end %>
|
39
39
|
<% else %>
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
<div class="flex items-center <%= classes %>">
|
41
|
+
<%= field.name %>
|
42
|
+
</div>
|
43
43
|
<% end %>
|
44
44
|
</th>
|
45
45
|
<% end %>
|
46
|
-
|
47
46
|
<th class="w-24">
|
48
|
-
<!--
|
47
|
+
<!-- Item controls cell -->
|
49
48
|
</th>
|
50
49
|
</thead>
|
@@ -16,6 +16,9 @@
|
|
16
16
|
<% else %>
|
17
17
|
<%= javascript_include_tag "avo", "data-turbo-track": "reload", defer: true %>
|
18
18
|
<%= stylesheet_link_tag "avo", "data-turbo-track": "reload", defer: true %>
|
19
|
+
<% if Rails.env.development? %>
|
20
|
+
<%= javascript_include_tag "hotwire-livereload", defer: true %>
|
21
|
+
<% end %>
|
19
22
|
<% end %>
|
20
23
|
</head>
|
21
24
|
<body class="bg-application os-mac">
|
data/bin/helpers.rb
CHANGED
@@ -22,8 +22,14 @@ end
|
|
22
22
|
|
23
23
|
def ask(question:, valid_answers: [])
|
24
24
|
puts "\n#{question} (#{valid_answers.join('/')})"
|
25
|
+
# An uppercase option is treated as a default answer. Otherwise, we disregard case, and always
|
26
|
+
# return the answer in lowercase.
|
27
|
+
default_answer = valid_answers.select { |val| val == val.upcase }.first&.downcase
|
25
28
|
|
26
|
-
|
29
|
+
valid_answers.map!(&:downcase)
|
30
|
+
|
31
|
+
input = gets.downcase.chomp
|
32
|
+
input = default_answer if input == ''
|
27
33
|
|
28
34
|
while !valid_answers.include?(input)
|
29
35
|
puts 'Invalid input, please try again.'
|
data/bin/init
CHANGED
@@ -17,7 +17,7 @@ app_root do
|
|
17
17
|
header 'Installing Yarn packages'
|
18
18
|
run! 'yarn'
|
19
19
|
|
20
|
-
if use_docker
|
20
|
+
if use_docker == 'y'
|
21
21
|
header 'Creating the Docker volume'
|
22
22
|
run! 'docker volume create --name=avo-db-data'
|
23
23
|
|
@@ -28,7 +28,7 @@ app_root do
|
|
28
28
|
header 'Preparing the database'
|
29
29
|
run! 'bin/rails db:setup'
|
30
30
|
|
31
|
-
if use_docker
|
31
|
+
if use_docker == 'y'
|
32
32
|
header 'Stopping the Docker image'
|
33
33
|
run! 'docker-compose stop'
|
34
34
|
end
|
data/lib/avo/app.rb
CHANGED
@@ -79,12 +79,16 @@ module Avo
|
|
79
79
|
def init_resources
|
80
80
|
self.resources = BaseResource.descendants
|
81
81
|
.select do |resource|
|
82
|
+
# Remove the BaseResource. We only need the descendants
|
82
83
|
resource != BaseResource
|
83
84
|
end
|
85
|
+
.uniq do |klass|
|
86
|
+
# On invalid resource configuration the resource classes get duplicated in `ObjectSpace`
|
87
|
+
# We need to de-duplicate them
|
88
|
+
klass.name
|
89
|
+
end
|
84
90
|
.map do |resource|
|
85
|
-
if resource.is_a? Class
|
86
|
-
resource.new
|
87
|
-
end
|
91
|
+
resource.new if resource.is_a? Class
|
88
92
|
end
|
89
93
|
end
|
90
94
|
|
data/lib/avo/base_resource.rb
CHANGED
data/lib/avo/dynamic_router.rb
CHANGED
data/lib/avo/engine.rb
CHANGED
@@ -19,7 +19,7 @@ module Avo
|
|
19
19
|
|
20
20
|
config.i18n.load_path += Dir[Avo::Engine.root.join('lib', 'generators', 'avo', 'templates', 'locales', '*.{rb,yml}')]
|
21
21
|
|
22
|
-
initializer "avo.autoload"
|
22
|
+
initializer "avo.autoload" do |app|
|
23
23
|
[
|
24
24
|
["app", "avo", "fields"],
|
25
25
|
["app", "avo", "filters"],
|
@@ -41,21 +41,11 @@ module Avo
|
|
41
41
|
::Avo::App.init_fields
|
42
42
|
end
|
43
43
|
|
44
|
-
initializer "avo.
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
app.
|
49
|
-
# Register reloader
|
50
|
-
app.reloaders << app.config.file_watcher.new([], {
|
51
|
-
Avo::Engine.root.join("lib", "avo").to_s => ["rb"]
|
52
|
-
}) {}
|
53
|
-
|
54
|
-
# What to do on file change
|
55
|
-
config.to_prepare do
|
56
|
-
Dir.glob(avo_root_path + "/lib/avo/**/*.rb".to_s).each { |c| load c }
|
57
|
-
Avo::App.boot
|
58
|
-
end
|
44
|
+
initializer "avo.reloader" do |app|
|
45
|
+
Avo::Reloader.new.tap do |reloader|
|
46
|
+
reloader.execute
|
47
|
+
app.reloaders << reloader
|
48
|
+
app.reloader.to_run { reloader.execute }
|
59
49
|
end
|
60
50
|
end
|
61
51
|
|