avo 1.18.2.pre.0 → 1.19.1.pre.2
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 -3
- data/Gemfile.lock +9 -8
- data/app/components/avo/fields/belongs_to_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/belongs_to_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/common/multiple_file_viewer_component.html.erb +1 -1
- data/app/components/avo/fields/common/single_file_viewer_component.html.erb +1 -2
- data/app/components/avo/fields/has_one_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/has_one_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/has_one_field/show_component.rb +6 -1
- data/app/components/avo/fields/index_component.rb +2 -2
- data/app/components/avo/fields/text_field/index_component.html.erb +5 -1
- data/app/components/avo/fields/text_field/show_component.html.erb +5 -1
- data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -1
- data/app/components/avo/index/grid_item_component.html.erb +4 -4
- data/app/components/avo/index/resource_controls_component.html.erb +1 -1
- data/app/components/avo/index/resource_controls_component.rb +22 -6
- data/app/components/avo/index/resource_table_component.html.erb +1 -1
- data/app/components/avo/panel_component.rb +3 -4
- data/app/components/avo/resource_component.rb +5 -1
- data/app/components/avo/views/resource_edit_component.html.erb +2 -2
- data/app/components/avo/views/resource_edit_component.rb +3 -3
- data/app/components/avo/views/resource_index_component.html.erb +2 -8
- data/app/components/avo/views/resource_index_component.rb +7 -15
- data/app/components/avo/views/resource_new_component.html.erb +2 -2
- data/app/components/avo/views/resource_new_component.rb +6 -2
- data/app/components/avo/views/resource_show_component.html.erb +5 -4
- data/app/components/avo/views/resource_show_component.rb +11 -6
- data/app/controllers/avo/actions_controller.rb +1 -1
- data/app/controllers/avo/application_controller.rb +17 -61
- data/app/controllers/avo/attachments_controller.rb +3 -2
- data/app/controllers/avo/base_controller.rb +30 -25
- data/app/controllers/avo/home_controller.rb +5 -1
- data/app/controllers/avo/relations_controller.rb +3 -3
- data/app/controllers/avo/search_controller.rb +1 -1
- data/app/helpers/avo/application_helper.rb +5 -11
- data/app/helpers/avo/resources_helper.rb +1 -1
- data/app/helpers/avo/url_helpers.rb +77 -0
- data/app/packs/entrypoints/application.css +1 -0
- data/app/packs/entrypoints/application.js +7 -0
- data/app/packs/js/controllers/loading_button_controller.js +22 -0
- data/app/packs/stylesheets/components/status.css +22 -15
- data/app/packs/stylesheets/spinner.css +49 -0
- data/app/views/avo/actions/show.html.erb +2 -2
- data/app/views/avo/base/_actions.html.erb +3 -3
- data/app/views/avo/base/_filters.html.erb +1 -1
- data/app/views/avo/home/failed_to_load.html.erb +3 -0
- data/app/views/avo/partials/_failed_state.html.erb +16 -0
- data/app/views/avo/partials/_paginator.html.erb +2 -2
- data/app/views/avo/partials/_profile_dropdown.html.erb +7 -5
- data/app/views/avo/relations/new.html.erb +0 -2
- data/app/views/avo/sidebar/_sidebar.html.erb +1 -1
- data/app/views/kaminari/_page.html.erb +1 -1
- data/config/routes.rb +7 -5
- data/lib/avo/app.rb +12 -12
- data/lib/avo/base_action.rb +9 -1
- data/lib/avo/base_resource.rb +33 -7
- data/lib/avo/configuration.rb +4 -0
- data/lib/avo/fields/base_field.rb +15 -4
- data/lib/avo/fields/has_and_belongs_to_many_field.rb +1 -19
- data/lib/avo/fields/has_base_field.rb +35 -0
- data/lib/avo/fields/has_many_field.rb +1 -19
- data/lib/avo/fields/has_one_field.rb +3 -19
- data/lib/avo/fields/key_value_field.rb +4 -4
- data/lib/avo/fields/text_field.rb +2 -0
- data/lib/avo/services/authorization_service.rb +8 -4
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/templates/locales/avo.en.yml +12 -3
- data/lib/generators/avo/templates/locales/avo.ro.yml +7 -0
- data/public/avo-packs/css/{application-f9191617.css → application-c3b50b28.css} +54 -12
- data/public/avo-packs/css/application-c3b50b28.css.br +0 -0
- data/public/avo-packs/css/{application-f9191617.css.gz → application-c3b50b28.css.gz} +0 -0
- data/public/avo-packs/css/application-c3b50b28.css.map +1 -0
- data/public/avo-packs/css/application-c3b50b28.css.map.br +0 -0
- data/public/avo-packs/css/application-c3b50b28.css.map.gz +0 -0
- data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js +2 -0
- data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.br +0 -0
- data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.gz +0 -0
- data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map +1 -0
- data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map.br +0 -0
- data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map.gz +0 -0
- data/public/avo-packs/js/application-6fc968cfa52976c4582b.js +26 -0
- data/public/avo-packs/js/{application-cc89f096028eb1d4d971.js.LICENSE.txt → application-6fc968cfa52976c4582b.js.LICENSE.txt} +0 -0
- data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.br +0 -0
- data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.gz +0 -0
- data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map +1 -0
- data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map.br +0 -0
- data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map.gz +0 -0
- data/public/avo-packs/manifest.json +21 -21
- metadata +27 -21
- data/public/avo-packs/css/application-f9191617.css.br +0 -0
- data/public/avo-packs/css/application-f9191617.css.map +0 -1
- data/public/avo-packs/css/application-f9191617.css.map.br +0 -0
- data/public/avo-packs/css/application-f9191617.css.map.gz +0 -0
- data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js +0 -2
- data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.br +0 -0
- data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.gz +0 -0
- data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map +0 -1
- data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map.br +0 -0
- data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map.gz +0 -0
- data/public/avo-packs/js/application-cc89f096028eb1d4d971.js +0 -26
- data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.br +0 -0
- data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.gz +0 -0
- data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map +0 -1
- data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map.br +0 -0
- data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map.gz +0 -0
@@ -3,6 +3,7 @@ module Avo
|
|
3
3
|
include Pundit
|
4
4
|
include Pagy::Backend
|
5
5
|
include Avo::ApplicationHelper
|
6
|
+
include Avo::UrlHelpers
|
6
7
|
|
7
8
|
protect_from_forgery with: :exception
|
8
9
|
before_action :init_app
|
@@ -11,6 +12,8 @@ module Avo
|
|
11
12
|
before_action :_authenticate!
|
12
13
|
before_action :set_container_classes
|
13
14
|
before_action :add_initial_breadcrumbs
|
15
|
+
before_action :set_view
|
16
|
+
before_action :set_model_to_fill
|
14
17
|
|
15
18
|
rescue_from Pundit::NotAuthorizedError, with: :render_unauthorized
|
16
19
|
rescue_from ActiveRecord::RecordInvalid, with: :exception_logger
|
@@ -19,7 +22,7 @@ module Avo
|
|
19
22
|
add_flash_types :info, :warning, :success, :error
|
20
23
|
|
21
24
|
def init_app
|
22
|
-
Avo::App.init request: request, context: context, root_path: avo.root_path.delete_suffix("/"), current_user: _current_user
|
25
|
+
Avo::App.init request: request, context: context, root_path: avo.root_path.delete_suffix("/"), current_user: _current_user, view_context: view_context
|
23
26
|
|
24
27
|
@license = Avo::App.license
|
25
28
|
end
|
@@ -70,66 +73,6 @@ module Avo
|
|
70
73
|
instance_eval(&Avo.configuration.context)
|
71
74
|
end
|
72
75
|
|
73
|
-
def resources_path(model, keep_query_params: false, **args)
|
74
|
-
return if model.nil?
|
75
|
-
|
76
|
-
model_class = get_model_class model
|
77
|
-
|
78
|
-
existing_params = {}
|
79
|
-
|
80
|
-
begin
|
81
|
-
if keep_query_params
|
82
|
-
existing_params = Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
|
83
|
-
end
|
84
|
-
rescue; end
|
85
|
-
avo.send :"resources_#{model_class.base_class.model_name.route_key}_path", **existing_params, **args
|
86
|
-
end
|
87
|
-
|
88
|
-
def related_resources_path(parent_model, model, keep_query_params: false, **args)
|
89
|
-
return if model.nil?
|
90
|
-
|
91
|
-
existing_params = {}
|
92
|
-
|
93
|
-
begin
|
94
|
-
if keep_query_params
|
95
|
-
existing_params = Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
|
96
|
-
end
|
97
|
-
rescue; end
|
98
|
-
Addressable::Template.new("#{Avo::App.root_path}/resources/#{@parent_resource.model.model_name.route_key}/#{@parent_resource.model.id}/#{@resource.route_key}{?query*}")
|
99
|
-
.expand({query: {**existing_params, **args}})
|
100
|
-
.to_str
|
101
|
-
end
|
102
|
-
|
103
|
-
def resource_path(model = nil, resource_id: nil, keep_query_params: false, **args)
|
104
|
-
return avo.send :"resources_#{singular_name(model)}_path", resource_id, **args if resource_id.present?
|
105
|
-
|
106
|
-
avo.send :"resources_#{singular_name(model)}_path", model, **args
|
107
|
-
end
|
108
|
-
|
109
|
-
def resource_attach_path(model_name, model_id, related_name, related_id = nil)
|
110
|
-
path = "#{Avo::App.root_path}/resources/#{model_name}/#{model_id}/#{related_name}/new"
|
111
|
-
|
112
|
-
path += "/#{related_id}" if related_id.present?
|
113
|
-
|
114
|
-
path
|
115
|
-
end
|
116
|
-
|
117
|
-
def resource_detach_path(model_name, model_id, related_name, related_id = nil)
|
118
|
-
path = "#{Avo::App.root_path}/resources/#{model_name}/#{model_id}/#{related_name}"
|
119
|
-
|
120
|
-
path += "/#{related_id}" if related_id.present?
|
121
|
-
|
122
|
-
path
|
123
|
-
end
|
124
|
-
|
125
|
-
def new_resource_path(model, **args)
|
126
|
-
avo.send :"new_resources_#{singular_name(model)}_path", **args
|
127
|
-
end
|
128
|
-
|
129
|
-
def edit_resource_path(model, **args)
|
130
|
-
avo.send :"edit_resources_#{singular_name(model)}_path", model.id, **args
|
131
|
-
end
|
132
|
-
|
133
76
|
private
|
134
77
|
|
135
78
|
def set_resource_name
|
@@ -158,6 +101,19 @@ module Avo
|
|
158
101
|
@related_model = eager_load_files(@related_resource, @related_resource.class.find_scope).find params[:related_id]
|
159
102
|
end
|
160
103
|
|
104
|
+
def set_view
|
105
|
+
@view = params[:action].to_sym
|
106
|
+
end
|
107
|
+
|
108
|
+
def set_model_to_fill
|
109
|
+
@model_to_fill = @resource.model_class.new if @view == :create
|
110
|
+
@model_to_fill = @model if @view == :update
|
111
|
+
end
|
112
|
+
|
113
|
+
def fill_model
|
114
|
+
@model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
|
115
|
+
end
|
116
|
+
|
161
117
|
def hydrate_resource
|
162
118
|
@resource.hydrate(view: action_name.to_sym, user: _current_user)
|
163
119
|
end
|
@@ -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] ||
|
31
|
+
redirect_to params[:referrer] || path, notice: t("avo.attachment_destroyed")
|
31
32
|
else
|
32
|
-
redirect_back fallback_location:
|
33
|
+
redirect_back fallback_location: path, notice: t("avo.failed_to_find_attachment")
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
@@ -5,8 +5,10 @@ module Avo
|
|
5
5
|
before_action :set_resource_name
|
6
6
|
before_action :set_resource
|
7
7
|
before_action :hydrate_resource
|
8
|
-
before_action :authorize_action
|
9
8
|
before_action :set_model, only: [:show, :edit, :destroy, :update]
|
9
|
+
before_action :set_model_to_fill
|
10
|
+
before_action :fill_model, only: [:create, :update]
|
11
|
+
before_action :authorize_action
|
10
12
|
before_action :reset_pagination_if_filters_changed, only: :index
|
11
13
|
before_action :cache_applied_filters, only: :index
|
12
14
|
|
@@ -70,10 +72,10 @@ 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
|
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
|
78
|
+
add_breadcrumb resource_name.humanize, resources_path(resource: @resource)
|
77
79
|
end
|
78
80
|
|
79
81
|
add_breadcrumb @resource.model_title
|
@@ -84,7 +86,7 @@ module Avo
|
|
84
86
|
@resource = @resource.hydrate(model: @model, view: :new, user: _current_user)
|
85
87
|
|
86
88
|
@page_title = @resource.default_panel_name
|
87
|
-
add_breadcrumb resource_name.humanize, resources_path(@resource
|
89
|
+
add_breadcrumb resource_name.humanize, resources_path(resource: @resource)
|
88
90
|
add_breadcrumb t("avo.new").humanize
|
89
91
|
end
|
90
92
|
|
@@ -99,30 +101,31 @@ module Avo
|
|
99
101
|
via_model = via_resource.class.find_scope.find params[:via_resource_id]
|
100
102
|
via_resource.hydrate model: via_model
|
101
103
|
|
102
|
-
add_breadcrumb via_resource.plural_name, resources_path(
|
103
|
-
add_breadcrumb via_resource.model_title, resource_path(via_model)
|
104
|
+
add_breadcrumb via_resource.plural_name, resources_path(resource: @resource)
|
105
|
+
add_breadcrumb via_resource.model_title, resource_path(model: via_model, resource: via_resource)
|
104
106
|
else
|
105
|
-
add_breadcrumb resource_name.humanize, resources_path(@resource
|
107
|
+
add_breadcrumb resource_name.humanize, resources_path(resource: @resource)
|
106
108
|
end
|
107
109
|
|
108
|
-
add_breadcrumb @resource.model_title, resource_path(@resource.model)
|
110
|
+
add_breadcrumb @resource.model_title, resource_path(model: @resource.model, resource: @resource)
|
109
111
|
add_breadcrumb t("avo.edit").humanize
|
110
112
|
end
|
111
113
|
|
112
114
|
def create
|
113
|
-
|
115
|
+
# model gets instantiated and filled in the fill_model before_action
|
114
116
|
saved = @model.save
|
115
117
|
@resource.hydrate(model: @model, view: :new, user: _current_user)
|
116
118
|
|
117
119
|
respond_to do |format|
|
118
120
|
if saved
|
119
121
|
redirect_path = if params[:via_relation_class].present? && params[:via_resource_id].present?
|
120
|
-
|
122
|
+
parent_resource = ::Avo::App.get_resource_by_model_name params[:via_relation_class].safe_constantize
|
123
|
+
resource_path(model: params[:via_relation_class].safe_constantize, resource: parent_resource, resource_id: params[:via_resource_id])
|
121
124
|
else
|
122
|
-
resource_path(@model)
|
125
|
+
resource_path(model: @model, resource: @resource)
|
123
126
|
end
|
124
127
|
|
125
|
-
format.html { redirect_to redirect_path, notice: "#{@model.class.name}
|
128
|
+
format.html { redirect_to redirect_path, notice: "#{@model.class.name} #{t("avo.was_successfully_created")}." }
|
126
129
|
format.json { render :show, status: :created, location: @model }
|
127
130
|
else
|
128
131
|
flash[:error] = t "avo.you_missed_something_check_form"
|
@@ -133,13 +136,13 @@ module Avo
|
|
133
136
|
end
|
134
137
|
|
135
138
|
def update
|
136
|
-
|
139
|
+
# model gets instantiated and filled in the fill_model before_action
|
137
140
|
saved = @model.save
|
138
141
|
@resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
|
139
142
|
|
140
143
|
respond_to do |format|
|
141
144
|
if saved
|
142
|
-
format.html { redirect_to params[:referrer] || resource_path(@model), notice: "#{@model.class.name}
|
145
|
+
format.html { redirect_to params[:referrer] || resource_path(model: @model, resource: @resource), notice: "#{@model.class.name} #{t("avo.was_successfully_updated")}." }
|
143
146
|
format.json { render :show, status: :ok, location: @model }
|
144
147
|
else
|
145
148
|
flash[:error] = t "avo.you_missed_something_check_form"
|
@@ -153,19 +156,17 @@ module Avo
|
|
153
156
|
@model.destroy!
|
154
157
|
|
155
158
|
respond_to do |format|
|
156
|
-
format.html { redirect_to params[:referrer] || resources_path(@
|
159
|
+
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
160
|
format.json { head :no_content }
|
158
161
|
end
|
159
162
|
end
|
160
163
|
|
161
164
|
private
|
162
165
|
|
163
|
-
def model_route_key
|
164
|
-
singular_name @resource.model_class
|
165
|
-
end
|
166
|
-
|
167
166
|
def model_params
|
168
|
-
|
167
|
+
model_param_key = @resource.singular_model_key
|
168
|
+
|
169
|
+
request_params = params.require(model_param_key).permit(permitted_params)
|
169
170
|
|
170
171
|
if @resource.devise_password_optional && request_params[:password].blank? && request_params[:password_confirmation].blank?
|
171
172
|
request_params.delete(:password_confirmation)
|
@@ -248,9 +249,13 @@ module Avo
|
|
248
249
|
model = @resource.class.find_scope.find params[:resource_id]
|
249
250
|
end
|
250
251
|
|
251
|
-
@actions =
|
252
|
-
|
253
|
-
|
252
|
+
@actions =
|
253
|
+
@resource
|
254
|
+
.get_actions
|
255
|
+
.map do |action|
|
256
|
+
action.new(model: model, resource: @resource, view: @view)
|
257
|
+
end
|
258
|
+
.select { |action| action.visible_in_view }
|
254
259
|
end
|
255
260
|
|
256
261
|
def applied_filters
|
@@ -282,7 +287,7 @@ module Avo
|
|
282
287
|
end
|
283
288
|
|
284
289
|
def applied_filters_cache_key
|
285
|
-
"avo.base_controller.#{@resource.
|
290
|
+
"avo.base_controller.#{@resource.model_key}.applied_filters"
|
286
291
|
end
|
287
292
|
end
|
288
293
|
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
|
-
|
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
|
|
@@ -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
|
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.
|
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
|
-
.
|
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
|
20
|
-
animation: sk-bounce
|
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: -
|
25
|
-
animation-delay: -
|
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%,
|
30
|
-
|
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%,
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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.
|
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: "#{
|
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.
|
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
|
-
"#{
|
18
|
-
"#{
|
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
|
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,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.
|
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
|
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>
|