avo 2.9.2.pre1 → 2.10.3.pre.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.lock +69 -69
- data/README.md +4 -0
- data/app/assets/stylesheets/css/buttons.css +4 -1
- data/app/components/avo/actions_component.rb +6 -2
- data/app/components/avo/base_component.rb +2 -0
- data/app/components/avo/button_component.rb +3 -1
- data/app/components/avo/fields/common/key_value_component.html.erb +2 -2
- data/app/components/avo/fields/common/single_file_viewer_component.rb +1 -1
- data/app/components/avo/fields/country_field/edit_component.html.erb +9 -3
- data/app/components/avo/fields/date_field/edit_component.html.erb +26 -7
- data/app/components/avo/fields/date_field/index_component.html.erb +7 -1
- data/app/components/avo/fields/date_field/show_component.html.erb +7 -1
- data/app/components/avo/fields/date_time_field/edit_component.html.erb +1 -0
- data/app/components/avo/fields/edit_component.rb +5 -0
- data/app/components/avo/fields/file_field/edit_component.html.erb +1 -0
- data/app/components/avo/fields/files_field/edit_component.html.erb +1 -0
- data/app/components/avo/fields/select_field/edit_component.html.erb +14 -10
- data/app/components/avo/fields/show_component.rb +1 -1
- data/app/components/avo/index/ordering/button_component.rb +1 -15
- data/app/components/avo/index/resource_controls_component.html.erb +2 -2
- data/app/components/avo/index/resource_controls_component.rb +5 -1
- data/app/components/avo/index/resource_table_component.html.erb +1 -1
- data/app/components/avo/index/table_row_component.html.erb +1 -1
- data/app/components/avo/item_switcher_component.html.erb +19 -0
- data/app/components/avo/item_switcher_component.rb +45 -0
- data/app/components/avo/panel_component.html.erb +23 -24
- data/app/components/avo/panel_component.rb +8 -5
- data/app/components/avo/tab_group_component.html.erb +53 -0
- data/app/components/avo/tab_group_component.rb +51 -0
- data/app/components/avo/tab_switcher_component.html.erb +21 -0
- data/app/components/avo/tab_switcher_component.rb +86 -0
- data/app/components/avo/views/resource_edit_component.html.erb +34 -56
- data/app/components/avo/views/resource_edit_component.rb +11 -1
- data/app/components/avo/views/resource_index_component.html.erb +2 -2
- data/app/components/avo/views/resource_index_component.rb +3 -3
- data/app/components/avo/views/resource_show_component.html.erb +58 -89
- data/app/components/avo/views/resource_show_component.rb +2 -2
- data/app/controllers/avo/actions_controller.rb +1 -1
- data/app/controllers/avo/application_controller.rb +33 -10
- data/app/controllers/avo/base_controller.rb +11 -3
- data/app/controllers/avo/reorder_controller.rb +25 -0
- data/app/helpers/avo/application_helper.rb +6 -6
- data/app/helpers/avo/url_helpers.rb +1 -5
- data/app/javascript/avo.js +5 -1
- data/app/javascript/js/controllers/fields/date_field_controller.js +15 -3
- data/app/javascript/js/controllers/loading_button_controller.js +25 -21
- data/app/javascript/js/controllers/search_controller.js +3 -0
- data/app/javascript/js/controllers/tabs_controller.js +86 -0
- data/app/javascript/js/controllers.js +2 -0
- data/app/views/avo/base/index.html.erb +1 -1
- data/app/views/avo/base/show.html.erb +1 -1
- data/app/views/avo/cards/show.html.erb +1 -1
- data/app/views/avo/debug/index.html.erb +1 -1
- data/app/views/avo/home/_actions.html.erb +1 -1
- data/app/views/avo/home/_dashboards.html.erb +19 -0
- data/app/views/avo/home/_filters.html.erb +1 -1
- data/app/views/avo/home/_resources.html.erb +1 -1
- data/app/views/avo/home/failed_to_load.html.erb +1 -1
- data/app/views/avo/home/index.html.erb +14 -2
- data/app/views/avo/partials/_javascript.html.erb +1 -1
- data/app/views/avo/partials/_tabs_toggle.html.erb +20 -0
- data/app/views/avo/private/design.html.erb +1 -1
- data/config/routes.rb +5 -4
- data/db/factories.rb +1 -0
- data/lib/avo/app.rb +12 -7
- data/lib/avo/base_action.rb +2 -19
- data/lib/avo/base_card.rb +1 -7
- data/lib/avo/base_resource.rb +6 -98
- data/lib/avo/base_resource_tool.rb +3 -1
- data/lib/avo/concerns/has_fields.rb +249 -50
- data/lib/avo/concerns/has_html_attributes.rb +1 -1
- data/lib/avo/concerns/is_resource_item.rb +36 -0
- data/lib/avo/concerns/model_class_constantized.rb +23 -0
- data/lib/avo/configuration.rb +2 -12
- data/lib/avo/dashboards/base_dashboard.rb +1 -1
- data/lib/avo/dsl/field_parser.rb +83 -0
- data/lib/avo/fields/base_field.rb +21 -2
- data/lib/avo/fields/country_field.rb +2 -0
- data/lib/avo/fields/date_field.rb +13 -9
- data/lib/avo/fields/field_extensions/has_include_blank.rb +17 -0
- data/lib/avo/fields/field_extensions/visible_in_different_views.rb +18 -1
- data/lib/avo/fields/file_field.rb +2 -0
- data/lib/avo/fields/files_field.rb +2 -0
- data/lib/avo/fields/has_base_field.rb +20 -1
- data/lib/avo/fields/has_one_field.rb +4 -1
- data/lib/avo/fields/key_value_field.rb +4 -4
- data/lib/avo/fields/select_field.rb +2 -0
- data/lib/avo/grid_collector.rb +6 -3
- data/lib/avo/items_holder.rb +68 -0
- data/lib/avo/licensing/h_q.rb +12 -0
- data/lib/avo/main_panel.rb +3 -0
- data/lib/avo/menu/builder.rb +8 -7
- data/lib/avo/panel.rb +25 -0
- data/lib/avo/panel_builder.rb +23 -0
- data/lib/avo/services/uri_service.rb +75 -0
- data/lib/avo/tab.rb +78 -0
- data/lib/avo/tab_builder.rb +25 -0
- data/lib/avo/tab_group.rb +40 -0
- data/lib/avo/tab_group_builder.rb +43 -0
- data/lib/avo/version.rb +1 -1
- data/lib/avo.rb +1 -0
- data/lib/generators/avo/action_generator.rb +3 -2
- data/lib/generators/avo/base_generator.rb +14 -0
- data/lib/generators/avo/card/chartkick_generator.rb +18 -0
- data/lib/generators/avo/card/metric_generator.rb +18 -0
- data/lib/generators/avo/card/partial_generator.rb +19 -0
- data/lib/generators/avo/controller_generator.rb +9 -3
- data/lib/generators/avo/dashboard_generator.rb +2 -2
- data/lib/generators/avo/eject_generator.rb +2 -3
- data/lib/generators/avo/field_generator.rb +2 -2
- data/lib/generators/avo/filter_generator.rb +3 -2
- data/lib/generators/avo/install_generator.rb +2 -2
- data/lib/generators/avo/locales_generator.rb +2 -2
- data/lib/generators/avo/named_base_generator.rb +14 -0
- data/lib/generators/avo/resource_generator.rb +2 -2
- data/lib/generators/avo/resource_tool_generator.rb +4 -4
- data/lib/generators/avo/templates/locales/avo.fr.yml +115 -0
- data/lib/generators/avo/templates/resource/controller.tt +2 -0
- data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
- data/lib/generators/avo/templates/tool/view.tt +1 -1
- data/lib/generators/avo/tool_generator.rb +4 -4
- data/lib/generators/avo/version_generator.rb +23 -0
- data/public/avo-assets/avo.css +31 -3
- data/public/avo-assets/avo.js +72 -72
- data/public/avo-assets/avo.js.map +3 -3
- metadata +32 -16
- data/app/assets/builds/action_cable.js +0 -2
- data/app/assets/builds/action_cable.js.map +0 -7
- data/app/assets/builds/application.js +0 -2
- data/app/assets/builds/application.js.map +0 -7
- data/app/assets/builds/avo.css +0 -9028
- data/app/assets/builds/avo.js +0 -512
- data/app/assets/builds/avo.js.map +0 -7
- data/app/assets/builds/avo_custom.js +0 -6
- data/app/assets/builds/avo_custom.js.map +0 -7
- data/lib/avo/concerns/has_tools.rb +0 -47
- data/lib/avo/fields/currency_field.rb +0 -15
- data/lib/generators/avo/chartkick_card_generator.rb +0 -16
- data/lib/generators/avo/metric_card_generator.rb +0 -16
- data/lib/generators/avo/partial_card_generator.rb +0 -17
@@ -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
|
-
|
10
|
-
|
11
|
-
<%
|
12
|
-
<% if
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
84
|
-
<%
|
85
|
-
<%
|
86
|
-
|
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.
|
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.
|
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)
|
@@ -11,9 +11,11 @@ module Avo
|
|
11
11
|
include Avo::UrlHelpers
|
12
12
|
|
13
13
|
protect_from_forgery with: :exception
|
14
|
+
around_action :set_avo_locale
|
15
|
+
around_action :set_force_locale, if: -> { params[:force_locale].present? }
|
16
|
+
before_action :set_default_locale, if: -> { params[:set_locale].present? }
|
14
17
|
before_action :init_app
|
15
18
|
before_action :check_avo_license
|
16
|
-
before_action :set_locale
|
17
19
|
before_action :set_authorization
|
18
20
|
before_action :_authenticate!
|
19
21
|
before_action :set_container_classes
|
@@ -27,7 +29,7 @@ module Avo
|
|
27
29
|
add_flash_types :info, :warning, :success, :error
|
28
30
|
|
29
31
|
def init_app
|
30
|
-
Avo::App.init request: request, context: context,
|
32
|
+
Avo::App.init request: request, context: context, current_user: _current_user, view_context: view_context, params: params
|
31
33
|
|
32
34
|
@license = Avo::App.license
|
33
35
|
end
|
@@ -260,23 +262,23 @@ module Avo
|
|
260
262
|
end
|
261
263
|
|
262
264
|
def on_root_path
|
263
|
-
[Avo
|
265
|
+
[Avo.configuration.root_path, "#{Avo.configuration.root_path}/"].include?(request.original_fullpath)
|
264
266
|
end
|
265
267
|
|
266
268
|
def on_resources_path
|
267
|
-
request.original_url.match?(/.*#{Avo
|
269
|
+
request.original_url.match?(/.*#{Avo.configuration.root_path}\/resources\/.*/)
|
268
270
|
end
|
269
271
|
|
270
272
|
def on_api_path
|
271
|
-
request.original_url.match?(/.*#{Avo
|
273
|
+
request.original_url.match?(/.*#{Avo.configuration.root_path}\/avo_api\/.*/)
|
272
274
|
end
|
273
275
|
|
274
276
|
def on_dashboards_path
|
275
|
-
request.original_url.match?(/.*#{Avo
|
277
|
+
request.original_url.match?(/.*#{Avo.configuration.root_path}\/dashboards\/.*/)
|
276
278
|
end
|
277
279
|
|
278
280
|
def on_debug_path
|
279
|
-
request.original_url.match?(/.*#{Avo
|
281
|
+
request.original_url.match?(/.*#{Avo.configuration.root_path}\/avo_private\/debug.*/)
|
280
282
|
end
|
281
283
|
|
282
284
|
def on_custom_tool_page
|
@@ -287,10 +289,31 @@ module Avo
|
|
287
289
|
@resource.form_scope
|
288
290
|
end
|
289
291
|
|
290
|
-
|
291
|
-
|
292
|
+
# Sets the locale set in avo.rb initializer
|
293
|
+
def set_avo_locale(&action)
|
294
|
+
locale = Avo.configuration.locale || I18n.default_locale
|
295
|
+
I18n.with_locale(locale, &action)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Enable the user to change the default locale with the `?set_locale=pt-BR` param
|
299
|
+
def set_default_locale
|
300
|
+
locale = params[:set_locale] || I18n.default_locale
|
301
|
+
|
302
|
+
I18n.default_locale = locale
|
303
|
+
end
|
292
304
|
|
293
|
-
|
305
|
+
# Temporary set the locale and reverting at the end of the request.
|
306
|
+
def set_force_locale(&action)
|
307
|
+
locale = params[:force_locale] || I18n.default_locale
|
308
|
+
I18n.with_locale(locale, &action)
|
309
|
+
end
|
310
|
+
|
311
|
+
def default_url_options
|
312
|
+
if params[:force_locale].present?
|
313
|
+
{ **super, force_locale: params[:force_locale] }
|
314
|
+
else
|
315
|
+
super
|
316
|
+
end
|
294
317
|
end
|
295
318
|
end
|
296
319
|
end
|
@@ -154,7 +154,7 @@ module Avo
|
|
154
154
|
|
155
155
|
respond_to do |format|
|
156
156
|
if saved
|
157
|
-
format.html { redirect_to after_create_path, notice: "#{@
|
157
|
+
format.html { redirect_to after_create_path, notice: "#{@resource.name} #{t("avo.was_successfully_created")}." }
|
158
158
|
else
|
159
159
|
flash.now[:error] = t "avo.you_missed_something_check_form"
|
160
160
|
format.html { render :new, status: :unprocessable_entity }
|
@@ -173,7 +173,7 @@ module Avo
|
|
173
173
|
|
174
174
|
respond_to do |format|
|
175
175
|
if saved
|
176
|
-
format.html { redirect_to after_update_path, notice: "#{@
|
176
|
+
format.html { redirect_to after_update_path, notice: "#{@resource.name} #{t("avo.was_successfully_updated")}." }
|
177
177
|
else
|
178
178
|
flash.now[:error] = t "avo.you_missed_something_check_form"
|
179
179
|
format.html { render :edit, status: :unprocessable_entity }
|
@@ -370,6 +370,8 @@ module Avo
|
|
370
370
|
def set_edit_title_and_breadcrumbs
|
371
371
|
@resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
|
372
372
|
@page_title = @resource.default_panel_name.to_s
|
373
|
+
|
374
|
+
last_crumb_args = {}
|
373
375
|
# If we're accessing this resource via another resource add the parent to the breadcrumbs.
|
374
376
|
if params[:via_resource_class].present? && params[:via_resource_id].present?
|
375
377
|
via_resource = Avo::App.get_resource_by_model_name params[:via_resource_class]
|
@@ -378,11 +380,17 @@ module Avo
|
|
378
380
|
|
379
381
|
add_breadcrumb via_resource.plural_name, resources_path(resource: @resource)
|
380
382
|
add_breadcrumb via_resource.model_title, resource_path(model: via_model, resource: via_resource)
|
383
|
+
puts ["via_resource.model_title->", via_resource.model_title].inspect
|
384
|
+
|
385
|
+
last_crumb_args = {
|
386
|
+
via_resource_class: params[:via_resource_class],
|
387
|
+
via_resource_id: params[:via_resource_id]
|
388
|
+
}
|
381
389
|
else
|
382
390
|
add_breadcrumb @resource.plural_name.humanize, resources_path(resource: @resource)
|
383
391
|
end
|
384
392
|
|
385
|
-
add_breadcrumb @resource.model_title, resource_path(model: @resource.model, resource: @resource)
|
393
|
+
add_breadcrumb @resource.model_title, resource_path(model: @resource.model, resource: @resource, **last_crumb_args)
|
386
394
|
add_breadcrumb t("avo.edit").humanize
|
387
395
|
end
|
388
396
|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_dependency "avo/application_controller"
|
2
|
+
|
3
|
+
module Avo
|
4
|
+
class ReorderController < ApplicationController
|
5
|
+
before_action :set_resource_name
|
6
|
+
before_action :set_resource
|
7
|
+
before_action :hydrate_resource
|
8
|
+
before_action :set_model
|
9
|
+
|
10
|
+
def order
|
11
|
+
direction = params[:direction].to_sym
|
12
|
+
|
13
|
+
if direction.present?
|
14
|
+
@resource
|
15
|
+
.hydrate(model: @model, params: params)
|
16
|
+
.ordering_host
|
17
|
+
.order direction
|
18
|
+
end
|
19
|
+
|
20
|
+
respond_to do |format|
|
21
|
+
format.html { redirect_to params[:referrer] || resources_path(resource: @resource) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
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?
|
@@ -138,5 +132,11 @@ module Avo
|
|
138
132
|
model.class
|
139
133
|
end
|
140
134
|
end
|
135
|
+
|
136
|
+
def root_path_without_url
|
137
|
+
Avo::App.root_path.to_s.delete_prefix(request.base_url.to_s).delete_suffix "/"
|
138
|
+
rescue
|
139
|
+
Avo.configuration.root_path
|
140
|
+
end
|
141
141
|
end
|
142
142
|
end
|
@@ -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(
|
38
|
+
def new_resource_path(resource:, **args)
|
39
39
|
avo.send :"new_resources_#{resource.singular_route_key}_path", **args
|
40
40
|
end
|
41
41
|
|
@@ -76,9 +76,5 @@ module Avo
|
|
76
76
|
|
77
77
|
avo.resources_associations_index_path(parent_model.model_name.route_key, record.id, **existing_params, **args)
|
78
78
|
end
|
79
|
-
|
80
|
-
def order_up_resource_path(model:, resource:, **args)
|
81
|
-
avo.send :"order_up_resources_#{resource.singular_route_key}_path", model, **args
|
82
|
-
end
|
83
79
|
end
|
84
80
|
end
|
data/app/javascript/avo.js
CHANGED
@@ -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
|
-
|
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
|
|
@@ -18,6 +18,7 @@ export default class extends Controller {
|
|
18
18
|
pickerFormat: String,
|
19
19
|
firstDayOfWeek: Number,
|
20
20
|
time24Hr: Boolean,
|
21
|
+
disableMobile: Boolean,
|
21
22
|
}
|
22
23
|
|
23
24
|
get browserZone() {
|
@@ -86,6 +87,9 @@ export default class extends Controller {
|
|
86
87
|
// Set the format of the displayed input field.
|
87
88
|
options.altFormat = this.pickerFormatValue
|
88
89
|
|
90
|
+
// Disable native input in mobile browsers
|
91
|
+
options.disableMobile = this.disableMobileValue
|
92
|
+
|
89
93
|
// Set first day of the week.
|
90
94
|
options.locale.firstDayOfWeek = this.firstDayOfWeekValue
|
91
95
|
|
@@ -99,7 +103,7 @@ export default class extends Controller {
|
|
99
103
|
|
100
104
|
options.dateFormat = 'Y-m-d H:i:S'
|
101
105
|
} else {
|
102
|
-
// Because the browser treats the date like a timestamp and updates it
|
106
|
+
// Because the browser treats the date like a timestamp and updates it at 00:00 hour, when on a western timezone the date will be converted with one day offset.
|
103
107
|
// Ex: 2022-01-30 will render as 2022-01-29 on an American timezone
|
104
108
|
options.defaultDate = universalTimestamp(this.initialValue)
|
105
109
|
}
|
@@ -111,12 +115,20 @@ export default class extends Controller {
|
|
111
115
|
|
112
116
|
onChange(selectedDates) {
|
113
117
|
let time
|
118
|
+
let args = {}
|
114
119
|
|
115
120
|
if (this.timezoneValue) {
|
116
|
-
|
121
|
+
args = { keepLocalTime: true }
|
122
|
+
} else {
|
123
|
+
args = { keepLocalTime: false }
|
124
|
+
}
|
125
|
+
|
126
|
+
if (this.enableTimeValue) {
|
127
|
+
time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', args)
|
117
128
|
} else {
|
118
|
-
time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime:
|
129
|
+
time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: true })
|
119
130
|
}
|
131
|
+
|
120
132
|
this.updateRealInput(time)
|
121
133
|
}
|
122
134
|
|
@@ -7,33 +7,37 @@ export default class extends Controller {
|
|
7
7
|
<div class="double-bounce2"></div>
|
8
8
|
</div>`;
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
10
|
+
static values = {
|
11
|
+
confirmationMessage: String,
|
12
|
+
confirmed: Boolean,
|
13
|
+
}
|
14
|
+
|
15
|
+
attemptSubmit(e) {
|
16
|
+
// If the user has to confirm the action
|
17
|
+
if (this.confirmationMessageValue) {
|
18
|
+
this.confirmAndApply(e)
|
19
|
+
} else {
|
20
|
+
this.applyLoader()
|
21
|
+
}
|
22
|
+
|
23
|
+
return null
|
24
|
+
}
|
25
|
+
|
26
|
+
confirmAndApply(e) {
|
27
|
+
// Intervene only if not confirmed
|
28
|
+
if (!this.confirmedValue) {
|
29
|
+
e.preventDefault()
|
30
|
+
|
31
|
+
if (window.confirm(this.confirmationMessageValue)) {
|
24
32
|
this.applyLoader()
|
25
33
|
}
|
26
|
-
}
|
34
|
+
}
|
27
35
|
}
|
28
36
|
|
29
37
|
get button() {
|
30
38
|
return this.context.scope.element
|
31
39
|
}
|
32
40
|
|
33
|
-
get confirmationMessage() {
|
34
|
-
return this.context.scope.element.getAttribute('data-avo-confirm')
|
35
|
-
}
|
36
|
-
|
37
41
|
applyLoader() {
|
38
42
|
const { button } = this
|
39
43
|
|
@@ -50,10 +54,10 @@ export default class extends Controller {
|
|
50
54
|
}
|
51
55
|
|
52
56
|
markConfirmed() {
|
53
|
-
this.
|
57
|
+
this.confirmedValue = true
|
54
58
|
}
|
55
59
|
|
56
60
|
markUnconfirmed() {
|
57
|
-
this.
|
61
|
+
this.confirmedValue = false
|
58
62
|
}
|
59
63
|
}
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import { AttributeObserver } from '@stimulus/mutation-observers'
|
2
|
+
import { Controller } from '@hotwired/stimulus'
|
3
|
+
import { castBoolean } from '../helpers/cast_boolean'
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
static targets = ['tab'];
|
7
|
+
|
8
|
+
static values = {
|
9
|
+
view: String,
|
10
|
+
activeTab: String,
|
11
|
+
};
|
12
|
+
|
13
|
+
get currentTab() {
|
14
|
+
return this.tabTargets.find(
|
15
|
+
(element) => element.dataset.tabId === this.activeTabValue,
|
16
|
+
)
|
17
|
+
}
|
18
|
+
|
19
|
+
targetTab(id) {
|
20
|
+
return this.tabTargets.find((element) => element.dataset.tabId === id)
|
21
|
+
}
|
22
|
+
|
23
|
+
changeTab(e) {
|
24
|
+
e.preventDefault()
|
25
|
+
|
26
|
+
const { params } = e
|
27
|
+
const { id } = params
|
28
|
+
|
29
|
+
this.setTheTargetPanelHeight(id)
|
30
|
+
|
31
|
+
this.hideTabs()
|
32
|
+
this.showTab(id)
|
33
|
+
this.markTabLoaded(id)
|
34
|
+
|
35
|
+
this.activeTabValue = id
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Sets the target container height to the previous panel height so we don't get jerky tab changes.
|
40
|
+
*/
|
41
|
+
setTheTargetPanelHeight(id) {
|
42
|
+
// Ignore this on edit.
|
43
|
+
// All tabs are loaded beforehand, they have their own height, and the page won't jiggle when the user toggles between them.
|
44
|
+
if (this.viewValue === 'edit' || this.viewValue === 'new') {
|
45
|
+
return
|
46
|
+
}
|
47
|
+
|
48
|
+
// We don't need to add a height to this panel because it was loaded before
|
49
|
+
if (castBoolean(this.targetTab(id).dataset.loaded)) {
|
50
|
+
return
|
51
|
+
}
|
52
|
+
|
53
|
+
// Get the height of the active panel
|
54
|
+
const { height } = this.currentTab.getBoundingClientRect()
|
55
|
+
// Set it to the target panel
|
56
|
+
this.targetTab(id).style.height = `${height}px`
|
57
|
+
|
58
|
+
// Wait until the panel loaded it's content and then remove the forced height
|
59
|
+
const observer = new AttributeObserver(this.targetTab(id), 'busy', {
|
60
|
+
elementUnmatchedAttribute: () => {
|
61
|
+
// The content is not available in an instant so delay the height reset a bit.
|
62
|
+
setTimeout(() => {
|
63
|
+
this.targetTab(id).style.height = ''
|
64
|
+
}, 300)
|
65
|
+
if (observer) observer.stop()
|
66
|
+
},
|
67
|
+
})
|
68
|
+
observer.start()
|
69
|
+
}
|
70
|
+
|
71
|
+
markTabLoaded(id) {
|
72
|
+
this.targetTab(id).dataset.loaded = true
|
73
|
+
}
|
74
|
+
|
75
|
+
showTab(id) {
|
76
|
+
this.tabTargets.forEach((element) => {
|
77
|
+
if (element.dataset.tabId === id) {
|
78
|
+
element.classList.remove('hidden')
|
79
|
+
}
|
80
|
+
})
|
81
|
+
}
|
82
|
+
|
83
|
+
hideTabs() {
|
84
|
+
this.tabTargets.map((element) => element.classList.add('hidden'))
|
85
|
+
}
|
86
|
+
}
|
@@ -27,6 +27,7 @@ import SearchController from './controllers/search_controller'
|
|
27
27
|
import SelectController from './controllers/select_controller'
|
28
28
|
import SelectFilterController from './controllers/select_filter_controller'
|
29
29
|
import SimpleMdeController from './controllers/fields/simple_mde_controller'
|
30
|
+
import TabsController from './controllers/tabs_controller'
|
30
31
|
import TagsFieldController from './controllers/fields/tags_field_controller'
|
31
32
|
import TextFilterController from './controllers/text_filter_controller'
|
32
33
|
import TippyController from './controllers/tippy_controller'
|
@@ -55,6 +56,7 @@ application.register('resource-show', ResourceShowController)
|
|
55
56
|
application.register('search', SearchController)
|
56
57
|
application.register('select', SelectController)
|
57
58
|
application.register('select-filter', SelectFilterController)
|
59
|
+
application.register('tabs', TabsController)
|
58
60
|
application.register('tags-field', TagsFieldController)
|
59
61
|
application.register('text-filter', TextFilterController)
|
60
62
|
application.register('tippy', TippyController)
|