avo 1.18.2 → 1.19.0

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

Potentially problematic release.


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

Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +6 -6
  4. data/app/components/avo/fields/belongs_to_field/index_component.html.erb +1 -1
  5. data/app/components/avo/fields/belongs_to_field/show_component.html.erb +1 -1
  6. data/app/components/avo/fields/common/multiple_file_viewer_component.html.erb +1 -1
  7. data/app/components/avo/fields/common/single_file_viewer_component.html.erb +1 -2
  8. data/app/components/avo/fields/has_one_field/index_component.html.erb +1 -1
  9. data/app/components/avo/fields/has_one_field/show_component.html.erb +1 -1
  10. data/app/components/avo/fields/has_one_field/show_component.rb +6 -1
  11. data/app/components/avo/fields/index_component.rb +2 -2
  12. data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -1
  13. data/app/components/avo/index/grid_item_component.html.erb +4 -4
  14. data/app/components/avo/index/resource_controls_component.html.erb +1 -1
  15. data/app/components/avo/index/resource_controls_component.rb +22 -6
  16. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  17. data/app/components/avo/panel_component.rb +3 -4
  18. data/app/components/avo/resource_component.rb +5 -1
  19. data/app/components/avo/views/resource_edit_component.html.erb +2 -2
  20. data/app/components/avo/views/resource_edit_component.rb +3 -3
  21. data/app/components/avo/views/resource_index_component.html.erb +2 -8
  22. data/app/components/avo/views/resource_index_component.rb +7 -15
  23. data/app/components/avo/views/resource_new_component.html.erb +2 -2
  24. data/app/components/avo/views/resource_new_component.rb +6 -2
  25. data/app/components/avo/views/resource_show_component.html.erb +5 -4
  26. data/app/components/avo/views/resource_show_component.rb +11 -6
  27. data/app/controllers/avo/actions_controller.rb +1 -1
  28. data/app/controllers/avo/application_controller.rb +2 -61
  29. data/app/controllers/avo/attachments_controller.rb +3 -2
  30. data/app/controllers/avo/base_controller.rb +28 -21
  31. data/app/controllers/avo/home_controller.rb +5 -1
  32. data/app/controllers/avo/relations_controller.rb +3 -3
  33. data/app/controllers/avo/search_controller.rb +1 -1
  34. data/app/helpers/avo/application_helper.rb +5 -11
  35. data/app/helpers/avo/resources_helper.rb +1 -1
  36. data/app/helpers/avo/url_helpers.rb +77 -0
  37. data/app/packs/entrypoints/application.css +1 -0
  38. data/app/packs/entrypoints/application.js +7 -0
  39. data/app/packs/js/controllers/loading_button_controller.js +22 -0
  40. data/app/packs/stylesheets/components/status.css +22 -15
  41. data/app/packs/stylesheets/spinner.css +49 -0
  42. data/app/views/avo/actions/show.html.erb +2 -2
  43. data/app/views/avo/base/_actions.html.erb +3 -3
  44. data/app/views/avo/base/_filters.html.erb +1 -1
  45. data/app/views/avo/home/failed_to_load.html.erb +3 -0
  46. data/app/views/avo/partials/_failed_state.html.erb +16 -0
  47. data/app/views/avo/partials/_paginator.html.erb +2 -2
  48. data/app/views/avo/relations/new.html.erb +0 -2
  49. data/app/views/avo/sidebar/_sidebar.html.erb +1 -1
  50. data/app/views/kaminari/_page.html.erb +1 -1
  51. data/config/routes.rb +7 -5
  52. data/lib/avo/app.rb +4 -8
  53. data/lib/avo/base_action.rb +9 -1
  54. data/lib/avo/base_resource.rb +33 -7
  55. data/lib/avo/configuration.rb +2 -0
  56. data/lib/avo/fields/base_field.rb +8 -2
  57. data/lib/avo/fields/has_and_belongs_to_many_field.rb +1 -19
  58. data/lib/avo/fields/has_base_field.rb +35 -0
  59. data/lib/avo/fields/has_many_field.rb +1 -19
  60. data/lib/avo/fields/has_one_field.rb +3 -19
  61. data/lib/avo/services/authorization_service.rb +8 -4
  62. data/lib/avo/version.rb +1 -1
  63. data/lib/generators/avo/templates/locales/avo.en.yml +2 -0
  64. data/public/avo-packs/css/{application-f9191617.css → application-c3b50b28.css} +54 -12
  65. data/public/avo-packs/css/application-c3b50b28.css.br +0 -0
  66. data/public/avo-packs/css/{application-f9191617.css.gz → application-c3b50b28.css.gz} +0 -0
  67. data/public/avo-packs/css/application-c3b50b28.css.map +1 -0
  68. data/public/avo-packs/css/application-c3b50b28.css.map.br +0 -0
  69. data/public/avo-packs/css/application-c3b50b28.css.map.gz +0 -0
  70. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js +2 -0
  71. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.br +0 -0
  72. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.gz +0 -0
  73. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map +1 -0
  74. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map.br +0 -0
  75. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map.gz +0 -0
  76. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js +26 -0
  77. data/public/avo-packs/js/{application-cc89f096028eb1d4d971.js.LICENSE.txt → application-6fc968cfa52976c4582b.js.LICENSE.txt} +0 -0
  78. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.br +0 -0
  79. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.gz +0 -0
  80. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map +1 -0
  81. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map.br +0 -0
  82. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map.gz +0 -0
  83. data/public/avo-packs/manifest.json +21 -21
  84. metadata +27 -21
  85. data/public/avo-packs/css/application-f9191617.css.br +0 -0
  86. data/public/avo-packs/css/application-f9191617.css.map +0 -1
  87. data/public/avo-packs/css/application-f9191617.css.map.br +0 -0
  88. data/public/avo-packs/css/application-f9191617.css.map.gz +0 -0
  89. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js +0 -2
  90. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.br +0 -0
  91. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.gz +0 -0
  92. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map +0 -1
  93. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map.br +0 -0
  94. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map.gz +0 -0
  95. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js +0 -26
  96. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.br +0 -0
  97. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.gz +0 -0
  98. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map +0 -1
  99. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map.br +0 -0
  100. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map.gz +0 -0
@@ -23,13 +23,14 @@ module Avo
23
23
  def destroy
24
24
  blob = ActiveStorage::Blob.find(params[:signed_attachment_id])
25
25
  attachment = blob.attachments.find_by record_id: params[:id], record_type: @model.class.to_s
26
+ path = resource_path(model: @model, resource: @resource)
26
27
 
27
28
  if attachment.present?
28
29
  attachment.destroy
29
30
 
30
- redirect_to params[:referrer] || resource_path(@model), notice: t("avo.attachment_destroyed")
31
+ redirect_to params[:referrer] || path, notice: t("avo.attachment_destroyed")
31
32
  else
32
- redirect_back fallback_location: resource_path(@model), notice: t("avo.failed_to_find_attachment")
33
+ redirect_back fallback_location: path, notice: t("avo.failed_to_find_attachment")
33
34
  end
34
35
  end
35
36
  end
@@ -11,6 +11,7 @@ module Avo
11
11
  before_action :cache_applied_filters, only: :index
12
12
 
13
13
  def index
14
+ @view = :index
14
15
  @page_title = resource_name.humanize
15
16
  add_breadcrumb resource_name.humanize
16
17
 
@@ -58,6 +59,7 @@ module Avo
58
59
  end
59
60
 
60
61
  def show
62
+ @view = :show
61
63
  set_actions
62
64
 
63
65
  @resource = @resource.hydrate(model: @model, view: :show, user: _current_user, params: params)
@@ -70,25 +72,27 @@ module Avo
70
72
  via_model = via_resource.class.find_scope.find params[:via_resource_id]
71
73
  via_resource.hydrate model: via_model
72
74
 
73
- add_breadcrumb via_resource.plural_name, resources_path(via_resource.model_class)
74
- add_breadcrumb via_resource.model_title, resource_path(via_model)
75
+ add_breadcrumb via_resource.plural_name, resources_path(resource: via_resource)
76
+ add_breadcrumb via_resource.model_title, resource_path(model: via_model, resource: via_resource)
75
77
  else
76
- add_breadcrumb resource_name.humanize, resources_path(@resource.model_class)
78
+ add_breadcrumb resource_name.humanize, resources_path(resource: @resource)
77
79
  end
78
80
 
79
81
  add_breadcrumb @resource.model_title
80
82
  end
81
83
 
82
84
  def new
85
+ @view = :new
83
86
  @model = @resource.model_class.new
84
87
  @resource = @resource.hydrate(model: @model, view: :new, user: _current_user)
85
88
 
86
89
  @page_title = @resource.default_panel_name
87
- add_breadcrumb resource_name.humanize, resources_path(@resource.model_class)
90
+ add_breadcrumb resource_name.humanize, resources_path(resource: @resource)
88
91
  add_breadcrumb t("avo.new").humanize
89
92
  end
90
93
 
91
94
  def edit
95
+ @view = :edit
92
96
  @resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
93
97
 
94
98
  @page_title = @resource.default_panel_name
@@ -99,13 +103,13 @@ module Avo
99
103
  via_model = via_resource.class.find_scope.find params[:via_resource_id]
100
104
  via_resource.hydrate model: via_model
101
105
 
102
- add_breadcrumb via_resource.plural_name, resources_path(via_resource.model_class)
103
- add_breadcrumb via_resource.model_title, resource_path(via_model)
106
+ add_breadcrumb via_resource.plural_name, resources_path(resource: @resource)
107
+ add_breadcrumb via_resource.model_title, resource_path(model: via_model, resource: via_resource)
104
108
  else
105
- add_breadcrumb resource_name.humanize, resources_path(@resource.model_class)
109
+ add_breadcrumb resource_name.humanize, resources_path(resource: @resource)
106
110
  end
107
111
 
108
- add_breadcrumb @resource.model_title, resource_path(@resource.model)
112
+ add_breadcrumb @resource.model_title, resource_path(model: @resource.model, resource: @resource)
109
113
  add_breadcrumb t("avo.edit").humanize
110
114
  end
111
115
 
@@ -117,9 +121,10 @@ module Avo
117
121
  respond_to do |format|
118
122
  if saved
119
123
  redirect_path = if params[:via_relation_class].present? && params[:via_resource_id].present?
120
- resource_path(params[:via_relation_class].safe_constantize, resource_id: params[:via_resource_id])
124
+ parent_resource = ::Avo::App.get_resource_by_model_name params[:via_relation_class].safe_constantize
125
+ resource_path(model: params[:via_relation_class].safe_constantize, resource: parent_resource, resource_id: params[:via_resource_id])
121
126
  else
122
- resource_path(@model)
127
+ resource_path(model: @model, resource: @resource)
123
128
  end
124
129
 
125
130
  format.html { redirect_to redirect_path, notice: "#{@model.class.name} was successfully created." }
@@ -139,7 +144,7 @@ module Avo
139
144
 
140
145
  respond_to do |format|
141
146
  if saved
142
- format.html { redirect_to params[:referrer] || resource_path(@model), notice: "#{@model.class.name} was successfully updated." }
147
+ format.html { redirect_to params[:referrer] || resource_path(model: @model, resource: @resource), notice: "#{@model.class.name} was successfully updated." }
143
148
  format.json { render :show, status: :ok, location: @model }
144
149
  else
145
150
  flash[:error] = t "avo.you_missed_something_check_form"
@@ -153,19 +158,17 @@ module Avo
153
158
  @model.destroy!
154
159
 
155
160
  respond_to do |format|
156
- format.html { redirect_to params[:referrer] || resources_path(@model, turbo_frame: params[:turbo_frame], view_type: params[:view_type]), notice: t("avo.resource_destroyed", attachment_class: @attachment_class) }
161
+ format.html { redirect_to params[:referrer] || resources_path(resource: @resource, turbo_frame: params[:turbo_frame], view_type: params[:view_type]), notice: t("avo.resource_destroyed", attachment_class: @attachment_class) }
157
162
  format.json { head :no_content }
158
163
  end
159
164
  end
160
165
 
161
166
  private
162
167
 
163
- def model_route_key
164
- singular_name @resource.model_class
165
- end
166
-
167
168
  def model_params
168
- request_params = params.require(model_route_key).permit(permitted_params)
169
+ model_param_key = @resource.singular_model_key
170
+
171
+ request_params = params.require(model_param_key).permit(permitted_params)
169
172
 
170
173
  if @resource.devise_password_optional && request_params[:password].blank? && request_params[:password_confirmation].blank?
171
174
  request_params.delete(:password_confirmation)
@@ -248,9 +251,13 @@ module Avo
248
251
  model = @resource.class.find_scope.find params[:resource_id]
249
252
  end
250
253
 
251
- @actions = @resource.get_actions.map do |action|
252
- action.new(model: model, resource: @resource)
253
- end
254
+ @actions =
255
+ @resource
256
+ .get_actions
257
+ .map do |action|
258
+ action.new(model: model, resource: @resource, view: @view)
259
+ end
260
+ .select { |action| action.visible_in_view }
254
261
  end
255
262
 
256
263
  def applied_filters
@@ -282,7 +289,7 @@ module Avo
282
289
  end
283
290
 
284
291
  def applied_filters_cache_key
285
- "avo.base_controller.#{@resource.route_key}.applied_filters"
292
+ "avo.base_controller.#{@resource.model_key}.applied_filters"
286
293
  end
287
294
  end
288
295
  end
@@ -7,8 +7,12 @@ module Avo
7
7
  redirect_to Avo.configuration.home_path
8
8
  elsif !Rails.env.development?
9
9
  @page_title = "Get started"
10
- redirect_to resources_path Avo::App.resources.min_by { |resource| resource.route_key }.model_class
10
+ resource = Avo::App.resources.min_by { |resource| resource.model_key }
11
+ redirect_to resources_path(resource: resource)
11
12
  end
12
13
  end
14
+
15
+ def failed_to_load
16
+ end
13
17
  end
14
18
  end
@@ -48,8 +48,8 @@ module Avo
48
48
 
49
49
  respond_to do |format|
50
50
  if @model.save
51
- format.html { redirect_to resource_path(@model), notice: t("avo.attachment_class_attached", attachment_class: @attachment_class) }
52
- format.json { render :show, status: :created, location: resource_path(@model) }
51
+ format.html { redirect_to resource_path(model: @model, resource: @resource), notice: t("avo.attachment_class_attached", attachment_class: @attachment_class) }
52
+ format.json { render :show, status: :created, location: resource_path(model: @model, resource: @resource) }
53
53
  else
54
54
  format.html { render :new }
55
55
  format.json { render json: @model.errors, status: :unprocessable_entity }
@@ -65,7 +65,7 @@ module Avo
65
65
  end
66
66
 
67
67
  respond_to do |format|
68
- format.html { redirect_to params[:referrer] || resource_path(@model), notice: t("avo.attachment_class_detached", attachment_class: @attachment_class) }
68
+ format.html { redirect_to params[:referrer] || resource_path(model: @model, resource: @resource), notice: t("avo.attachment_class_detached", attachment_class: @attachment_class) }
69
69
  end
70
70
  end
71
71
 
@@ -59,7 +59,7 @@ module Avo
59
59
  result = {
60
60
  _id: model.id,
61
61
  _label: resource.label,
62
- _url: resource.avo_path,
62
+ _url: resource.record_path,
63
63
  model: model
64
64
  }
65
65
 
@@ -39,6 +39,10 @@ module Avo
39
39
 
40
40
  def a_button(label = nil, **args, &block)
41
41
  args[:class] = button_classes(args[:class], color: args[:color], variant: args[:variant], size: args[:size])
42
+ if args[:spinner]
43
+ args["data-controller"] = "loading-button"
44
+ # args["data-action"] = "click->loading-button#onClick"
45
+ end
42
46
 
43
47
  locals = {
44
48
  label: label,
@@ -159,17 +163,7 @@ module Avo
159
163
  if model.instance_of?(Class)
160
164
  model
161
165
  else
162
- model.class.base_class
163
- end
164
- end
165
-
166
- def singular_name(model_or_class)
167
- model_class = get_model_class model_or_class
168
-
169
- if ActiveModel::Naming.uncountable? model_class
170
- model_class.base_class.model_name.route_key.singularize.gsub('_index', '')
171
- else
172
- model_class.base_class.model_name.route_key.singularize
166
+ model.class
173
167
  end
174
168
  end
175
169
  end
@@ -42,7 +42,7 @@ module Avo
42
42
  end
43
43
 
44
44
  def item_selector_init(resource)
45
- "data-resource-name='#{resource.model_class.model_name.route_key}' data-resource-id='#{resource.model.id}' data-controller='item-selector'"
45
+ "data-resource-name='#{resource.model_key}' data-resource-id='#{resource.model.id}' data-controller='item-selector'"
46
46
  end
47
47
 
48
48
  def item_selector_input(floating: false, size: :md)
@@ -0,0 +1,77 @@
1
+ module Avo
2
+ module UrlHelpers
3
+ def resources_path(resource:, keep_query_params: false, **args)
4
+ return if resource.nil?
5
+
6
+ existing_params = {}
7
+ begin
8
+ if keep_query_params
9
+ existing_params =
10
+ Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
11
+ end
12
+ rescue
13
+ end
14
+
15
+ # This entry uses `route_key` instead of `model_key` because it's rails that needs `fish_index` to build the correct path
16
+ avo.send :"resources_#{resource.route_key}_path", **existing_params, **args
17
+ end
18
+
19
+ def resource_path(
20
+ model:,
21
+ resource:,
22
+ resource_id: nil,
23
+ keep_query_params: false,
24
+ **args
25
+ )
26
+ if model.respond_to? :id
27
+ id = model
28
+ elsif resource_id.present?
29
+ id = resource_id
30
+ end
31
+
32
+ avo.send :"resources_#{resource.singular_model_key}_path", id, **args
33
+ end
34
+
35
+ def new_resource_path(model:, resource:, **args)
36
+ avo.send :"new_resources_#{resource.singular_model_key}_path", **args
37
+ end
38
+
39
+ def edit_resource_path(model:, resource:, **args)
40
+ avo.send :"edit_resources_#{resource.singular_model_key}_path", model, **args
41
+ end
42
+
43
+ def resource_attach_path(resource, model_id, related_name, related_id = nil)
44
+ helpers.avo.resources_associations_new_path(resource.singular_model_key, model_id, related_name)
45
+ end
46
+
47
+ def resource_detach_path(
48
+ model_name, # teams
49
+ model_id, # 1
50
+ related_name, # admin
51
+ related_id = nil
52
+ )
53
+ avo.resources_associations_destroy_path(model_name, model_id, related_name, related_id)
54
+ end
55
+
56
+ def related_resources_path(
57
+ parent_model,
58
+ model,
59
+ keep_query_params: false,
60
+ **args
61
+ )
62
+ return if model.nil?
63
+
64
+ existing_params = {}
65
+
66
+ begin
67
+ if keep_query_params
68
+ existing_params =
69
+ Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
70
+ end
71
+ rescue
72
+ end
73
+
74
+ avo.resources_associations_index_path(@parent_resource.model_class.model_name.route_key, @parent_resource.model.id, **existing_params, **args )
75
+ end
76
+ end
77
+ end
@@ -17,6 +17,7 @@
17
17
  @import './../stylesheets/breadcrumbs.css';
18
18
  @import './../stylesheets/search.css';
19
19
  @import './../stylesheets/active-storage.css';
20
+ @import './../stylesheets/spinner.css';
20
21
 
21
22
  @import './../stylesheets/components/status.css';
22
23
  @import './../stylesheets/components/code.css';
@@ -46,6 +46,13 @@ document.addEventListener('turbo:load', () => {
46
46
  document.body.classList.remove('turbo-loading')
47
47
  initTippy()
48
48
  })
49
+
50
+ document.addEventListener('turbo:before-fetch-response', (e) => {
51
+ if (e.detail.fetchResponse.response.status === 500) {
52
+ const id = e.srcElement.getAttribute('id')
53
+ e.srcElement.src = `${window.Avo.configuration.root_path}/failed_to_load?turbo_frame=${id}`
54
+ }
55
+ })
49
56
  document.addEventListener('turbo:visit', () => document.body.classList.add('turbo-loading'))
50
57
  document.addEventListener('turbo:submit-start', () => document.body.classList.add('turbo-loading'))
51
58
  document.addEventListener('turbo:before-cache', () => {
@@ -0,0 +1,22 @@
1
+ import { Controller } from 'stimulus'
2
+
3
+ export default class extends Controller {
4
+ spinnerMarkup = `<div class="button-spinner">
5
+ <div class="double-bounce1"></div>
6
+ <div class="double-bounce2"></div>
7
+ </div>`;
8
+
9
+ connect() {
10
+ const button = this.context.scope.element
11
+ this.context.scope.element.addEventListener('click', () => {
12
+ button.style.width = `${button.getBoundingClientRect().width}px`
13
+ button.style.height = `${button.getBoundingClientRect().height}px`
14
+ button.innerHTML = this.spinnerMarkup
15
+ button.classList.add('justify-center')
16
+
17
+ setTimeout(() => {
18
+ button.setAttribute('disabled', 'disabled')
19
+ }, 1)
20
+ })
21
+ }
22
+ }
@@ -5,8 +5,8 @@
5
5
  position: relative;
6
6
  }
7
7
 
8
-
9
- .double-bounce1, .double-bounce2 {
8
+ .spinner .double-bounce1,
9
+ .spinner .double-bounce2 {
10
10
  width: 100%;
11
11
  height: 100%;
12
12
  border-radius: 50%;
@@ -16,26 +16,33 @@
16
16
  top: 0;
17
17
  left: 0;
18
18
 
19
- -webkit-animation: sk-bounce 2.0s infinite ease-in-out;
20
- animation: sk-bounce 2.0s infinite ease-in-out;
19
+ -webkit-animation: sk-bounce 2s infinite ease-in-out;
20
+ animation: sk-bounce 2s infinite ease-in-out;
21
21
  }
22
22
 
23
- .double-bounce2 {
24
- -webkit-animation-delay: -1.0s;
25
- animation-delay: -1.0s;
23
+ .spinner .double-bounce2 {
24
+ -webkit-animation-delay: -1s;
25
+ animation-delay: -1s;
26
26
  }
27
27
 
28
28
  @-webkit-keyframes sk-bounce {
29
- 0%, 100% { -webkit-transform: scale(0.0) }
30
- 50% { -webkit-transform: scale(1.0) }
29
+ 0%,
30
+ 100% {
31
+ -webkit-transform: scale(0);
32
+ }
33
+ 50% {
34
+ -webkit-transform: scale(1);
35
+ }
31
36
  }
32
37
 
33
38
  @keyframes sk-bounce {
34
- 0%, 100% {
35
- transform: scale(0.0);
36
- -webkit-transform: scale(0.0);
37
- } 50% {
38
- transform: scale(1.0);
39
- -webkit-transform: scale(1.0);
39
+ 0%,
40
+ 100% {
41
+ transform: scale(0);
42
+ -webkit-transform: scale(0);
43
+ }
44
+ 50% {
45
+ transform: scale(1);
46
+ -webkit-transform: scale(1);
40
47
  }
41
48
  }
@@ -0,0 +1,49 @@
1
+ .button-spinner {
2
+ width: 24px;
3
+ height: 24px;
4
+
5
+ position: relative;
6
+ margin: 100px auto;
7
+ }
8
+
9
+ .button-spinner > .double-bounce1,
10
+ .button-spinner > .double-bounce2 {
11
+ width: 100%;
12
+ height: 100%;
13
+ border-radius: 50%;
14
+ background-color: #333;
15
+ opacity: 0.5;
16
+ position: absolute;
17
+ top: 0;
18
+ left: 0;
19
+
20
+ -webkit-animation: sk-bounce 2s infinite ease-in-out;
21
+ animation: sk-bounce 2s infinite ease-in-out;
22
+ }
23
+
24
+ .button-spinner > .double-bounce2 {
25
+ -webkit-animation-delay: -1s;
26
+ animation-delay: -1s;
27
+ }
28
+
29
+ @-webkit-keyframes sk-bounce {
30
+ 0%,
31
+ 100% {
32
+ -webkit-transform: scale(0);
33
+ }
34
+ 50% {
35
+ -webkit-transform: scale(1);
36
+ }
37
+ }
38
+
39
+ @keyframes sk-bounce {
40
+ 0%,
41
+ 100% {
42
+ transform: scale(0);
43
+ -webkit-transform: scale(0);
44
+ }
45
+ 50% {
46
+ transform: scale(1);
47
+ -webkit-transform: scale(1);
48
+ }
49
+ }
@@ -3,11 +3,11 @@
3
3
  data-controller="action"
4
4
  data-no-confirmation="<%= @action.no_confirmation %>"
5
5
  data-action-target="controllerDiv"
6
- data-resource-name="<%= @resource.model_class.model_name.route_key %>"
6
+ data-resource-name="<%= @resource.model_key %>"
7
7
  data-resource-id="<%= params[:id] %>"
8
8
  class="hidden text-blue-gray-800"
9
9
  >
10
- <%= form_with model: @model, scope: 'fields', url: "#{avo.root_path}resources/#{@resource.model_class.model_name.route_key}/actions/#{@action.param_id}", data: {'turbo-frame': '_top', 'action-target': 'form'} do |form| %>
10
+ <%= form_with model: @model, scope: 'fields', url: "#{@resource.records_path}/actions/#{@action.param_id}", data: {'turbo-frame': '_top', 'action-target': 'form'} do |form| %>
11
11
  <%= render Avo::ModalComponent.new do |c| %>
12
12
  <% c.heading do %>
13
13
  <%= @action.action_name %>
@@ -3,7 +3,7 @@
3
3
  <%= a_button class: "focus:outline-none",
4
4
  color: 'light-blue',
5
5
  'data-action': "click->toggle-panel#togglePanel",
6
- 'data-actions-dropdown-button': @resource.model_class.model_name.route_key do
6
+ 'data-actions-dropdown-button': @resource.model_key do
7
7
  %>
8
8
  <%= svg 'arrow-left', class: 'h-4 mr-1 transform -rotate-90' %> <%= t 'avo.actions' %>
9
9
  <% end %>
@@ -14,8 +14,8 @@
14
14
  <%
15
15
  @actions.each_with_index do |action, index|
16
16
  path = action_name == 'show' ?
17
- "#{avo.root_path}resources/#{@resource.model_class.model_name.route_key}/#{@model.id}/actions/#{action.param_id}" :
18
- "#{avo.root_path}resources/#{@resource.model_class.model_name.route_key}/actions/#{action.param_id}"
17
+ "#{@resource.record_path}/actions/#{action.param_id}" :
18
+ "#{@resource.records_path}/actions/#{action.param_id}"
19
19
  if action_name == 'show' || action.standalone
20
20
  disabled = false
21
21
  else
@@ -24,7 +24,7 @@
24
24
 
25
25
  <div class="p-4 border-gray-300 border-t">
26
26
  <% if params[:filters].present? %>
27
- <%= a_link t('avo.reset_filters'), resources_path(@resource.model_class, filters: nil, keep_query_params: true), color: 'blue-gray', class: 'w-full justify-center' %>
27
+ <%= a_link t('avo.reset_filters'), resources_path(resource: @resource, filters: nil, keep_query_params: true), color: 'blue-gray', class: 'w-full justify-center' %>
28
28
  <% else %>
29
29
  <%= a_button t('avo.reset_filters'), color: 'blue-gray', class: 'w-full justify-center', disabled: true %>
30
30
  <% end %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_frame_wrap(params[:turbo_frame]) do %>
2
+ <%= render 'avo/partials/failed_state' %>
3
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <%
2
+ classes = 'absolute inset-auto left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2'
3
+ label = t 'avo.failed_to_load'
4
+ %>
5
+ <div class="relative flex-1 py-12">
6
+ <div class="relative block text-gray-300 h-40 w-full">
7
+ <%= svg 'avocado', class: "#{classes} h-20 text-gray-400" %>
8
+ <%= svg 'code', class: "#{classes} h-8 -ml-20 -mt-12" %>
9
+ <%= svg 'fire', class: "#{classes} h-8 -ml-10 -mt-24" %>
10
+ <%= svg 'color-swatch', class: "#{classes} h-8 ml-8 -mt-24" %>
11
+ <%= svg 'globe', class: "#{classes} h-8 ml-20 -mt-12" %>
12
+ <%= svg 'library', class: "#{classes} h-8 -ml-20 mt-4" %>
13
+ <%= svg 'photograph', class: "#{classes} h-8 ml-20 mt-4" %>
14
+ </div>
15
+ <div class="relative block text-center text-lg text-gray-400 font-semibold -mt-10"><%= label %> <%= params[:turbo_frame].to_s.humanize.downcase if params[:turbo_frame].present? %></div>
16
+ </div>
@@ -29,9 +29,9 @@
29
29
 
30
30
  <% per_page_options.each do |option| %>
31
31
  <% if @parent_resource.present? %>
32
- <%= link_to "Change to #{option} items per page", related_resources_path(@parent_resource.model, @resource.model_class, per_page: option, keep_query_params: true), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
32
+ <%= link_to "Change to #{option} items per page", related_resources_path(@parent_resource.model_class, @resource.model_class, per_page: option, keep_query_params: true), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
33
33
  <% else %>
34
- <%= link_to "Change to #{option} items per page", resources_path(@resource.model_class, per_page: option, keep_query_params: true), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
34
+ <%= link_to "Change to #{option} items per page", resources_path(resource: @resource, per_page: option, keep_query_params: true), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
35
35
  <% end %>
36
36
  <% end %>
37
37
  </div>
@@ -22,8 +22,6 @@
22
22
  </div>
23
23
  </div>
24
24
 
25
- <%#= form.hidden_field :resource_ids, value: params[:resource_ids], 'data-action-target': 'resourceIds' %>
26
-
27
25
  <% c.controls do %>
28
26
  <%= a_button t('avo.cancel'), 'data-action': 'click->modal#close', size: :sm %>
29
27
  <%= a_button t('avo.attach'), type: :submit, color: :green, size: :sm %>
@@ -12,7 +12,7 @@
12
12
 
13
13
  <div class="w-full">
14
14
  <% Avo::App.resources_navigation(_current_user).sort_by { |r| r.navigation_label }.each do |resource| %>
15
- <%= render Avo::NavigationLinkComponent.new label: resource.navigation_label, path: resources_path(resource.model_class) %>
15
+ <%= render Avo::NavigationLinkComponent.new label: resource.navigation_label, path: resources_path(resource: resource) %>
16
16
  <% end %>
17
17
 
18
18
  <% sidebar_partials = Avo::App.get_sidebar_partials %>
@@ -1,4 +1,4 @@
1
- <% url = resources_path(@resource.model_class, page: page, keep_query_params: true) %>
1
+ <% url = resources_path(resource: @resource, page: page, keep_query_params: true) %>
2
2
  <% if page.current? %>
3
3
  <button type="button" class="hidden md:inline-flex -ml-px relative items-center px-4 py-2 border bg-gray-100 text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 z-10 outline-none border-primary-300 active:bg-gray-100 transition ease-in-out duration-150">
4
4
  <%= page %>
data/config/routes.rb CHANGED
@@ -10,6 +10,8 @@ Avo::Engine.routes.draw do
10
10
  post "/resources/:resource_name/:id/attachments/", to: "attachments#create"
11
11
  end
12
12
 
13
+ get "failed_to_load", to: "home#failed_to_load"
14
+
13
15
  scope "resources", as: "resources" do
14
16
  # Attachments
15
17
  get "/:resource_name/:id/active_storage_attachments/:attachment_name/:signed_attachment_id", to: "attachments#show"
@@ -24,10 +26,10 @@ Avo::Engine.routes.draw do
24
26
  instance_eval(&Avo::App.draw_routes)
25
27
 
26
28
  # Relations
27
- get "/:resource_name/:id/:related_name/new", to: "relations#new"
28
- get "/:resource_name/:id/:related_name/", to: "relations#index"
29
- get "/:resource_name/:id/:related_name/:related_id", to: "relations#show"
30
- post "/:resource_name/:id/:related_name", to: "relations#create"
31
- delete "/:resource_name/:id/:related_name/:related_id", to: "relations#destroy"
29
+ get "/:resource_name/:id/:related_name/new", to: "relations#new", as: "associations_new"
30
+ get "/:resource_name/:id/:related_name/", to: "relations#index", as: "associations_index"
31
+ get "/:resource_name/:id/:related_name/:related_id", to: "relations#show", as: "associations_show"
32
+ post "/:resource_name/:id/:related_name", to: "relations#create", as: "associations_create"
33
+ delete "/:resource_name/:id/:related_name/:related_id", to: "relations#destroy", as: "associations_destroy"
32
34
  end
33
35
  end
data/lib/avo/app.rb CHANGED
@@ -8,6 +8,7 @@ module Avo
8
8
  class_attribute :license, default: nil
9
9
  class_attribute :current_user, default: nil
10
10
  class_attribute :root_path, default: nil
11
+ class_attribute :view_context, default: nil
11
12
 
12
13
  class << self
13
14
  def boot
@@ -22,11 +23,12 @@ module Avo
22
23
  end
23
24
  end
24
25
 
25
- def init(request:, context:, current_user:, root_path:)
26
+ def init(request:, context:, current_user:, root_path:, view_context:)
26
27
  self.request = request
27
28
  self.context = context
28
29
  self.current_user = current_user
29
30
  self.root_path = root_path
31
+ self.view_context = view_context
30
32
 
31
33
  self.license = Licensing::LicenseManager.new(Licensing::HQ.new(request).response).license
32
34
 
@@ -167,13 +169,7 @@ module Avo
167
169
  resource.is_a? Class
168
170
  end
169
171
  .map do |resource|
170
- route_key = if resource.model_class.present?
171
- resource.model_class.model_name.plural.to_sym
172
- else
173
- resource.to_s.underscore.gsub("_resource", "").downcase.pluralize.to_sym
174
- end
175
-
176
- resources route_key
172
+ resources resource.new.model_key
177
173
  end
178
174
  end
179
175
  end