avo 1.20.2.pre.2 → 1.22.0.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 +57 -57
- data/README.md +1 -1
- data/app/assets/builds/avo.css +405 -224
- data/app/assets/builds/avo.js +16 -6
- data/app/assets/builds/avo.js.map +2 -2
- data/app/assets/stylesheets/avo.css +1 -1
- data/app/assets/stylesheets/css/fonts.css +53 -0
- data/app/assets/stylesheets/css/pagination.css +9 -9
- data/app/assets/stylesheets/css/search.css +3 -2
- data/app/assets/svgs/dashboards-icon.svg +6 -0
- data/app/assets/svgs/resources-icon.svg +13 -0
- data/app/assets/svgs/three-dots.svg +5 -0
- data/app/assets/svgs/tools-icon.svg +3 -0
- data/app/components/avo/button_component.html.erb +1 -0
- data/app/components/avo/button_component.rb +111 -0
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +14 -7
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.rb +47 -3
- data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +59 -38
- data/app/components/avo/fields/belongs_to_field/edit_component.rb +37 -0
- data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
- data/app/components/avo/index/field_wrapper_component.html.erb +8 -2
- data/app/components/avo/index/field_wrapper_component.rb +2 -1
- data/app/components/avo/index/resource_table_component.html.erb +3 -3
- data/app/components/avo/index/table_row_component.html.erb +1 -1
- data/app/components/avo/navigation_heading_component.html.erb +2 -2
- data/app/components/avo/navigation_heading_component.rb +5 -1
- data/app/components/avo/navigation_link_component.html.erb +2 -2
- data/app/components/avo/navigation_link_component.rb +5 -0
- data/app/components/avo/panel_component.html.erb +5 -3
- data/app/components/avo/sidebar_profile_component.html.erb +28 -0
- data/app/components/avo/sidebar_profile_component.rb +43 -0
- data/app/components/avo/views/resource_index_component.html.erb +8 -7
- data/app/components/avo/views/resource_show_component.html.erb +10 -0
- data/app/controllers/avo/application_controller.rb +14 -12
- data/app/controllers/avo/base_controller.rb +4 -1
- data/app/helpers/avo/application_helper.rb +7 -5
- data/app/helpers/avo/resources_helper.rb +2 -2
- data/app/javascript/js/controllers/fields/date_field_controller.js +10 -2
- data/app/javascript/js/controllers/item_selector_controller.js +29 -19
- data/app/javascript/js/controllers/search_controller.js +8 -3
- data/app/views/avo/base/_actions.html.erb +4 -3
- data/app/views/avo/base/_filters.html.erb +3 -1
- data/app/views/avo/partials/_global_search.html.erb +2 -2
- data/app/views/avo/partials/_logo.html.erb +3 -1
- data/app/views/avo/partials/_navbar.html.erb +11 -0
- data/app/views/avo/partials/_paginator.html.erb +4 -3
- data/app/views/avo/partials/_resource_search.html.erb +1 -1
- data/app/views/avo/partials/_sidebar.html.erb +35 -0
- data/app/views/avo/partials/_table_header.html.erb +3 -3
- data/app/views/avo/partials/_turbo_frame_wrap.html.erb +7 -1
- data/app/views/avo/partials/_view_toggle_button.html.erb +22 -16
- data/app/views/layouts/avo/application.html.erb +3 -13
- data/db/factories.rb +7 -3
- data/lib/avo/app.rb +4 -3
- data/lib/avo/base_resource.rb +16 -14
- data/lib/avo/version.rb +1 -1
- data/public/avo-assets/avo.css +405 -224
- data/public/avo-assets/avo.js +16 -6
- data/public/avo-assets/avo.js.map +2 -2
- data/public/avo-assets/fonts/inter-v7-latin-500.eot +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-500.svg +351 -0
- data/public/avo-assets/fonts/inter-v7-latin-500.ttf +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-500.woff +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-500.woff2 +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.eot +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.svg +351 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.ttf +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.woff +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.woff2 +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.eot +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.svg +352 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.ttf +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.woff +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.woff2 +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.eot +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.svg +351 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.ttf +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.woff +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.woff2 +0 -0
- data/public/avo-assets/logo.png +0 -0
- metadata +32 -5
- data/app/assets/images/avo/logo.png +0 -0
- data/app/views/avo/partials/_profile_dropdown.html.erb +0 -25
- data/app/views/avo/sidebar/_sidebar.html.erb +0 -33
@@ -1,3 +1,3 @@
|
|
1
|
-
<%= active_link_to @path, class: '
|
2
|
-
|
1
|
+
<%= active_link_to @path, class: 'px-4 block mx-6 leading-none py-3 text-black rounded font-medium hover:bg-blue-50', active: @active do %>
|
2
|
+
<%= @label %>
|
3
3
|
<% end %>
|
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Avo::NavigationLinkComponent < ViewComponent::Base
|
4
|
+
attr_reader :label
|
5
|
+
attr_reader :path
|
6
|
+
attr_reader :active
|
7
|
+
attr_reader :size
|
8
|
+
|
4
9
|
def initialize(label: nil, path: nil, active: :inclusive, size: :md)
|
5
10
|
@label = label
|
6
11
|
@path = path
|
@@ -6,10 +6,12 @@
|
|
6
6
|
<%= helpers.render_breadcrumbs(separator: helpers.svg('chevron-right', class: 'inline-block h-3 stroke-current relative top-[-1px] ml-1' )) if Avo.configuration.display_breadcrumbs %>
|
7
7
|
</div>
|
8
8
|
<% end %>
|
9
|
-
|
9
|
+
|
10
|
+
<div class="text-2xl tracking-normal font-semibold text-gray-800 truncate" data-target="title">
|
10
11
|
<%= @title %>
|
11
12
|
</div>
|
12
|
-
|
13
|
+
|
14
|
+
<div class="text-base tracking-normal font-medium text-gray-600" data-target="description">
|
13
15
|
<%== description %>
|
14
16
|
</div>
|
15
17
|
</div>
|
@@ -21,7 +23,7 @@
|
|
21
23
|
</div>
|
22
24
|
</div>
|
23
25
|
|
24
|
-
<div class="relative bg-white rounded-
|
26
|
+
<div class="relative bg-white rounded-lg shadow mb-8 <%= @body_classes %>">
|
25
27
|
<%= body %>
|
26
28
|
</div>
|
27
29
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<div class="text-black border-t border-gray-200 p-4 flex">
|
2
|
+
<div class="flex-1 flex space-x-4">
|
3
|
+
<div class="relative aspect-square w-10 h-10 overflow-hidden rounded">
|
4
|
+
<%= image_tag avatar, class: "object-cover min-w-full min-h-full h-full" %>
|
5
|
+
</div>
|
6
|
+
<div class="flex flex-col">
|
7
|
+
<div class="font-medium">
|
8
|
+
<%= name %>
|
9
|
+
</div>
|
10
|
+
<div class="text-xs text-gray-500 uppercase">
|
11
|
+
<%= title %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
<div>
|
16
|
+
<% if can_destroy_user? %>
|
17
|
+
<a class="flex items-center h-full cursor-pointer">
|
18
|
+
<%= helpers.svg 'three-dots' %>
|
19
|
+
</a>
|
20
|
+
|
21
|
+
<%# if main_app.respond_to?(destroy_user_session_path)
|
22
|
+
<div class="hidden absolute inset-auto right-0 mr-6 mt-0 py-4 bg-white rounded-xl min-w-[200px] shadow-context" data-toggle-panel-target="panel">
|
23
|
+
<%= button_to t('avo.sign_out'), main_app.send(:destroy_user_session_path), method: :delete, form: { "data-turbo" => "false" }, class: "appearance-none bg-white text-left cursor-pointer text-green-600 font-semibold hover:text-white hover:bg-green-500 block px-4 py-1 w-full"
|
24
|
+
</div>
|
25
|
+
<%# end %>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Avo::SidebarProfileComponent < ViewComponent::Base
|
4
|
+
attr_reader :user
|
5
|
+
|
6
|
+
def initialize(user:)
|
7
|
+
@user = user
|
8
|
+
end
|
9
|
+
|
10
|
+
def avatar
|
11
|
+
if user.respond_to?(:avatar) && user.avatar.present?
|
12
|
+
user.avatar
|
13
|
+
else
|
14
|
+
""
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
if user.respond_to?(:name) && user.name.present?
|
20
|
+
user.name
|
21
|
+
elsif user.respond_to?(:email) && user.email.present?
|
22
|
+
user.email
|
23
|
+
else
|
24
|
+
"Avo user"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def title
|
29
|
+
if user.respond_to?(:avo_title) && user.avo_title.present?
|
30
|
+
user.avo_title
|
31
|
+
else
|
32
|
+
""
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy_user_session_path
|
37
|
+
"destroy_#{Avo.configuration.current_user_resource_name}_session_path".to_sym
|
38
|
+
end
|
39
|
+
|
40
|
+
def can_destroy_user?
|
41
|
+
helpers.main_app.respond_to?(destroy_user_session_path)
|
42
|
+
end
|
43
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
<div>
|
2
|
-
<%= render Avo::PanelComponent.new(title: title, description: description, body_classes: 'py-
|
2
|
+
<%= render Avo::PanelComponent.new(title: title, description: description, body_classes: 'py-2', data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
|
3
3
|
<% c.tools do %>
|
4
4
|
<% if can_see_the_actions_button? %>
|
5
5
|
<%= render 'actions' %>
|
6
6
|
<% end %>
|
7
7
|
|
8
8
|
<% if can_see_the_create_button? %>
|
9
|
-
<%= a_link create_path, 'data-target': 'create' do %>
|
9
|
+
<%= a_link create_path, 'data-target': 'create', color: :primary, variant: :outlined do %>
|
10
10
|
<%= svg 'plus' %> <%= t('avo.create_new_item', item: singular_resource_name.downcase ) %>
|
11
11
|
<% end %>
|
12
12
|
<% end %>
|
@@ -19,7 +19,7 @@
|
|
19
19
|
<% end %>
|
20
20
|
|
21
21
|
<% c.body do %>
|
22
|
-
<div class="flex justify-between
|
22
|
+
<div class="flex justify-between min-h-16"
|
23
23
|
data-selected-resources-name="<%= @resource.model_key %>"
|
24
24
|
data-selected-resources="[]"
|
25
25
|
>
|
@@ -27,11 +27,14 @@
|
|
27
27
|
<%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.model_key} if @resource.search_query.present? %>
|
28
28
|
</div>
|
29
29
|
<div class="flex justify-end items-center px-6 space-x-3">
|
30
|
-
<%= render partial: 'avo/partials/view_toggle_button', locals: { available_view_types: available_view_types, view_type: view_type, turbo_frame: @turbo_frame } if @models.present? %>
|
31
30
|
<%= render 'filters' if @filters.present? %>
|
31
|
+
|
32
|
+
<%= render partial: 'avo/partials/view_toggle_button', locals: { available_view_types: available_view_types, view_type: view_type, turbo_frame: @turbo_frame } if @models.present? %>
|
32
33
|
</div>
|
33
34
|
</div>
|
35
|
+
<% end %>
|
34
36
|
|
37
|
+
<% c.bare_content do %>
|
35
38
|
<% if view_type.to_sym == :table %>
|
36
39
|
<div class="w-full overflow-auto flex flex-col mt-4">
|
37
40
|
<div class="relative flex-1 flex">
|
@@ -45,13 +48,11 @@
|
|
45
48
|
<% end %>
|
46
49
|
</div>
|
47
50
|
<% end %>
|
48
|
-
<% end %>
|
49
51
|
|
50
|
-
<% c.bare_content do %>
|
51
52
|
<% if view_type.to_sym == :grid %>
|
52
53
|
<%= render Avo::Index::ResourceGridComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model) %>
|
53
54
|
|
54
|
-
<div class="
|
55
|
+
<div class="mt-8 py-6">
|
55
56
|
<%= render partial: 'avo/partials/paginator', locals: { pagy: @pagy, turbo_frame: @turbo_frame || 'none' } %>
|
56
57
|
</div>
|
57
58
|
<% end %>
|
@@ -24,6 +24,16 @@
|
|
24
24
|
<%= svg 'arrow-left' %> <%= t('avo.go_back') %>
|
25
25
|
<% end %>
|
26
26
|
|
27
|
+
<%# -- %>
|
28
|
+
|
29
|
+
<%#= render Avo::ButtonComponent.new(icon: svg('arrow-left'), is_link: true, href: back_path) do %>
|
30
|
+
<%#= t('avo.go_back') %>
|
31
|
+
<%# end %>
|
32
|
+
|
33
|
+
<%#= render Avo::ButtonComponent.new(color: 'green', spinner: true, type: :submit, icon: svg('save')) do %>
|
34
|
+
<%#= t('avo.save').capitalize %>
|
35
|
+
<%# end %>
|
36
|
+
|
27
37
|
<% if can_see_the_destroy_button? %>
|
28
38
|
<%= form_with url: helpers.resource_path(model: @resource.model, resource: @resource), method: :delete, html: { 'data-turbo-frame': params[:turbo_frame] } do |form| %>
|
29
39
|
<%= a_button title: t('avo.delete_item', item: @resource.model.model_name.name.downcase).capitalize,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Avo
|
2
2
|
class ApplicationController < ::ActionController::Base
|
3
|
-
include Pundit
|
3
|
+
include Pundit::Authorization
|
4
4
|
include Pagy::Backend
|
5
5
|
include Avo::ApplicationHelper
|
6
6
|
include Avo::UrlHelpers
|
@@ -111,13 +111,7 @@ module Avo
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def fill_model
|
114
|
-
|
115
|
-
is_attach_action = params[model_param_key].blank? && params[:related_name].present? && params[:fields].present?
|
116
|
-
# puts ['fill_model->', is_attach_action, model_param_key].inspect
|
117
|
-
|
118
|
-
unless is_attach_action
|
119
|
-
@model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
|
120
|
-
end
|
114
|
+
@model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
|
121
115
|
end
|
122
116
|
|
123
117
|
def hydrate_resource
|
@@ -193,6 +187,18 @@ module Avo
|
|
193
187
|
query
|
194
188
|
end
|
195
189
|
|
190
|
+
# def authorize_user
|
191
|
+
# return if params[:controller] == 'avo/search'
|
192
|
+
|
193
|
+
# model = record = resource.model
|
194
|
+
|
195
|
+
# if ['show', 'edit', 'update'].include?(params[:action]) && params[:controller] == 'avo/resources'
|
196
|
+
# record = resource
|
197
|
+
# end
|
198
|
+
|
199
|
+
# # AuthorizationService::authorize_action _current_user, record, params[:action] return render_unauthorized unless
|
200
|
+
# end
|
201
|
+
|
196
202
|
def _authenticate!
|
197
203
|
instance_eval(&Avo.configuration.authenticate)
|
198
204
|
end
|
@@ -237,9 +243,5 @@ module Avo
|
|
237
243
|
def on_api_path
|
238
244
|
request.original_url.match?(/.*#{Avo::App.root_path}\/avo_api\/.*/)
|
239
245
|
end
|
240
|
-
|
241
|
-
def model_param_key
|
242
|
-
@resource.form_scope
|
243
|
-
end
|
244
246
|
end
|
245
247
|
end
|
@@ -7,7 +7,6 @@ module Avo
|
|
7
7
|
before_action :hydrate_resource
|
8
8
|
before_action :set_model, only: [:show, :edit, :destroy, :update]
|
9
9
|
before_action :set_model_to_fill
|
10
|
-
before_action :fill_model, only: [:create, :update]
|
11
10
|
before_action :authorize_action
|
12
11
|
before_action :reset_pagination_if_filters_changed, only: :index
|
13
12
|
before_action :cache_applied_filters, only: :index
|
@@ -113,6 +112,7 @@ module Avo
|
|
113
112
|
|
114
113
|
def create
|
115
114
|
# model gets instantiated and filled in the fill_model method
|
115
|
+
fill_model
|
116
116
|
saved = @model.save
|
117
117
|
@resource.hydrate(model: @model, view: :new, user: _current_user)
|
118
118
|
|
@@ -166,6 +166,7 @@ module Avo
|
|
166
166
|
|
167
167
|
def update
|
168
168
|
# model gets instantiated and filled in the fill_model method
|
169
|
+
fill_model
|
169
170
|
saved = @model.save
|
170
171
|
@resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
|
171
172
|
|
@@ -193,6 +194,8 @@ module Avo
|
|
193
194
|
private
|
194
195
|
|
195
196
|
def model_params
|
197
|
+
model_param_key = @resource.form_scope
|
198
|
+
|
196
199
|
request_params = params.require(model_param_key).permit(permitted_params)
|
197
200
|
|
198
201
|
if @resource.devise_password_optional && request_params[:password].blank? && request_params[:password_confirmation].blank?
|
@@ -68,14 +68,14 @@ module Avo
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
def button_classes(extra_classes = nil, color: nil, variant: nil, size: :md)
|
72
|
-
classes = "inline-flex flex-grow-0 items-center text-sm font-
|
71
|
+
def button_classes(extra_classes = nil, color: nil, variant: nil, size: :md, active: false)
|
72
|
+
classes = "inline-flex flex-grow-0 items-center text-sm font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 rounded transform transition duration-100 active:translate-x-px active:translate-y-px cursor-pointer disabled:cursor-not-allowed #{extra_classes}"
|
73
73
|
|
74
74
|
if color.present?
|
75
75
|
if variant.present? && (variant.to_sym == :outlined)
|
76
76
|
classes += " bg-white border"
|
77
77
|
|
78
|
-
classes += " hover:border-#{color}-700 border-#{color}-
|
78
|
+
classes += " hover:border-#{color}-700 border-#{color}-500 text-#{color}-600 hover:text-#{color}-700 disabled:border-gray-300 disabled:text-gray-600"
|
79
79
|
else
|
80
80
|
classes += " text-white bg-#{color}-500 hover:bg-#{color}-600 disabled:bg-#{color}-300"
|
81
81
|
end
|
@@ -88,9 +88,11 @@ module Avo
|
|
88
88
|
when :xs
|
89
89
|
" p-2 py-1"
|
90
90
|
when :sm
|
91
|
-
"
|
91
|
+
" py-1 px-4"
|
92
92
|
when :md
|
93
|
-
"
|
93
|
+
" py-2 px-4"
|
94
|
+
when :xl
|
95
|
+
" py-3 px-4"
|
94
96
|
else
|
95
97
|
" p-4"
|
96
98
|
end
|
@@ -47,7 +47,7 @@ module Avo
|
|
47
47
|
|
48
48
|
def item_selector_input(floating: false, size: :md)
|
49
49
|
"<input type='checkbox'
|
50
|
-
class='mx-3 #{"absolute inset-auto left-0 mt-2 z-10 hidden group-hover:block checked:block" if floating} #{size.to_sym == :lg ? "w-5 h-5" : "w-4 h-4"}'
|
50
|
+
class='mx-3 rounded #{"absolute inset-auto left-0 mt-2 z-10 hidden group-hover:block checked:block" if floating} #{size.to_sym == :lg ? "w-5 h-5" : "w-4 h-4"}'
|
51
51
|
data-action='input->item-selector#toggle input->item-select-all#update'
|
52
52
|
data-item-select-all-target='itemCheckbox'
|
53
53
|
name='#{t "avo.select_item"}'
|
@@ -58,7 +58,7 @@ module Avo
|
|
58
58
|
|
59
59
|
def item_select_all_input
|
60
60
|
"<input type='checkbox'
|
61
|
-
class='mx-3 w-4 h-4'
|
61
|
+
class='mx-3 rounded w-4 h-4'
|
62
62
|
data-action='input->item-select-all#toggle'
|
63
63
|
data-item-select-all-target='checkbox'
|
64
64
|
name='#{t "avo.select_all"}'
|
@@ -1,8 +1,14 @@
|
|
1
1
|
import { Controller } from 'stimulus'
|
2
2
|
import { DateTime } from 'luxon'
|
3
|
-
import { castBoolean } from '../../helpers/cast_boolean'
|
4
3
|
import flatpickr from 'flatpickr'
|
5
4
|
|
5
|
+
import { castBoolean } from '../../helpers/cast_boolean'
|
6
|
+
|
7
|
+
// Get the DateTime with the TZ offset applied.
|
8
|
+
function universalTimestamp(timestampStr) {
|
9
|
+
return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000))
|
10
|
+
}
|
11
|
+
|
6
12
|
export default class extends Controller {
|
7
13
|
static targets = ['input']
|
8
14
|
|
@@ -43,7 +49,9 @@ export default class extends Controller {
|
|
43
49
|
// this.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
44
50
|
options.appTimezone = this.inputTarget.dataset.timezone
|
45
51
|
} else {
|
46
|
-
|
52
|
+
// Because the browser treats the date like a timestamp and updates it ot 00:00 hour, when on a western timezone the date will be converted with one day offset.
|
53
|
+
// Ex: 2022-01-30 will render as 2022-01-29 on an American timezone
|
54
|
+
currentValue = universalTimestamp(this.inputTarget.value)
|
47
55
|
}
|
48
56
|
|
49
57
|
options.defaultDate = currentValue
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import { Controller } from 'stimulus'
|
2
2
|
|
3
3
|
export default class extends Controller {
|
4
|
-
static targets = ['panel']
|
4
|
+
static targets = ['panel'];
|
5
5
|
|
6
|
-
checkbox = {}
|
6
|
+
checkbox = {};
|
7
7
|
|
8
8
|
get actionsPanelPresent() {
|
9
9
|
return this.actionsButtonElement !== null
|
@@ -17,6 +17,12 @@ export default class extends Controller {
|
|
17
17
|
}
|
18
18
|
}
|
19
19
|
|
20
|
+
get actionLinks() {
|
21
|
+
return document.querySelectorAll(
|
22
|
+
'.js-actions-dropdown a[data-actions-picker-target="resourceAction"]',
|
23
|
+
)
|
24
|
+
}
|
25
|
+
|
20
26
|
set currentIds(value) {
|
21
27
|
this.stateHolderElement.dataset.selectedResources = JSON.stringify(value)
|
22
28
|
|
@@ -32,8 +38,12 @@ export default class extends Controller {
|
|
32
38
|
connect() {
|
33
39
|
this.resourceName = this.element.dataset.resourceName
|
34
40
|
this.resourceId = this.element.dataset.resourceId
|
35
|
-
this.actionsButtonElement = document.querySelector(
|
36
|
-
|
41
|
+
this.actionsButtonElement = document.querySelector(
|
42
|
+
`[data-actions-dropdown-button="${this.resourceName}"]`,
|
43
|
+
)
|
44
|
+
this.stateHolderElement = document.querySelector(
|
45
|
+
`[data-selected-resources-name="${this.resourceName}"]`,
|
46
|
+
)
|
37
47
|
}
|
38
48
|
|
39
49
|
addToSelected() {
|
@@ -45,7 +55,9 @@ export default class extends Controller {
|
|
45
55
|
}
|
46
56
|
|
47
57
|
removeFromSelected() {
|
48
|
-
this.currentIds = this.currentIds.filter(
|
58
|
+
this.currentIds = this.currentIds.filter(
|
59
|
+
(item) => item.toString() !== this.resourceId,
|
60
|
+
)
|
49
61
|
}
|
50
62
|
|
51
63
|
toggle(event) {
|
@@ -59,22 +71,20 @@ export default class extends Controller {
|
|
59
71
|
}
|
60
72
|
|
61
73
|
enableResourceActions() {
|
62
|
-
|
63
|
-
.
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
})
|
74
|
+
this.actionLinks.forEach((link) => {
|
75
|
+
link.classList.add('text-gray-700')
|
76
|
+
link.classList.remove('text-gray-500')
|
77
|
+
link.setAttribute('data-href', link.getAttribute('href'))
|
78
|
+
link.dataset.disabled = false
|
79
|
+
})
|
69
80
|
}
|
70
81
|
|
71
82
|
disableResourceActions() {
|
72
|
-
|
73
|
-
.
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
})
|
83
|
+
this.actionLinks.forEach((link) => {
|
84
|
+
link.classList.remove('text-gray-700')
|
85
|
+
link.classList.add('text-gray-500')
|
86
|
+
link.setAttribute('href', link.getAttribute('data-href'))
|
87
|
+
link.dataset.disabled = true
|
88
|
+
})
|
79
89
|
}
|
80
90
|
}
|
@@ -82,7 +82,9 @@ export default class extends Controller {
|
|
82
82
|
|
83
83
|
document.querySelector('.aa-DetachedOverlay').remove()
|
84
84
|
|
85
|
-
this.
|
85
|
+
if (this.hasClearButtonTarget) {
|
86
|
+
this.clearButtonTarget.classList.remove('hidden')
|
87
|
+
}
|
86
88
|
} else {
|
87
89
|
Turbo.visit(item._url, { action: 'advance' })
|
88
90
|
}
|
@@ -157,7 +159,7 @@ export default class extends Controller {
|
|
157
159
|
this.buttonTarget.onclick = () => this.showSearchPanel()
|
158
160
|
|
159
161
|
this.clearValueTargets.forEach((target) => {
|
160
|
-
if (target.getAttribute('value')) {
|
162
|
+
if (target.getAttribute('value') && this.hasClearButtonTarget) {
|
161
163
|
this.clearButtonTarget.classList.remove('hidden')
|
162
164
|
}
|
163
165
|
})
|
@@ -184,6 +186,9 @@ export default class extends Controller {
|
|
184
186
|
},
|
185
187
|
})
|
186
188
|
|
187
|
-
|
189
|
+
// When using search for belongs-to
|
190
|
+
if (this.buttonTarget.dataset.shouldBeDisabled !== 'true') {
|
191
|
+
this.buttonTarget.removeAttribute('disabled')
|
192
|
+
}
|
188
193
|
}
|
189
194
|
}
|
@@ -1,11 +1,12 @@
|
|
1
1
|
<% if @actions.count > 0 %>
|
2
2
|
<div class="relative z-40 js-actions-dropdown" data-controller="toggle-panel actions-picker">
|
3
3
|
<%= a_button class: "focus:outline-none",
|
4
|
-
color: '
|
4
|
+
color: 'primary',
|
5
|
+
size: :xl,
|
5
6
|
'data-action': "click->toggle-panel#togglePanel",
|
6
7
|
'data-actions-dropdown-button': @resource.model_key do
|
7
8
|
%>
|
8
|
-
<%= svg 'arrow-
|
9
|
+
<%= svg 'arrow-circle-right', class: 'h-4 mr-1 transform rotate-90' %> <%= t 'avo.actions' %>
|
9
10
|
<% end %>
|
10
11
|
<div
|
11
12
|
class="absolute block inset-auto right-0 top-full bg-white min-w-300px mt-2 py-4 z-20 shadow-context rounded-xl overflow-hidden hidden"
|
@@ -26,7 +27,7 @@
|
|
26
27
|
'data-turbo-frame': 'actions_show',
|
27
28
|
'data-action': 'click->actions-picker#visitAction',
|
28
29
|
'data-actions-picker-target': action.standalone ? 'standaloneAction' : 'resourceAction',
|
29
|
-
class: "flex items-center w-full py-2 px-4 font-
|
30
|
+
class: "flex items-center w-full py-2 px-4 font-semibold text-gray-700 hover:text-white hover:bg-blue-500 #{disabled ? 'text-gray-500' : 'text-gray-700'}",
|
30
31
|
'data-disabled': disabled do %>
|
31
32
|
<%= svg 'play', class: 'h-5 mr-1 inline' %> <%= action.action_name %>
|
32
33
|
<% end %>
|
@@ -1,7 +1,9 @@
|
|
1
1
|
<div data-controller="toggle-panel">
|
2
2
|
<div class="relative w-full flex justify-between z-30">
|
3
3
|
<%= a_button class: 'focus:outline-none',
|
4
|
-
color: '
|
4
|
+
color: 'secondary',
|
5
|
+
variant: 'outlined',
|
6
|
+
size: :sm,
|
5
7
|
title: t('avo.click_to_reveal_filters'),
|
6
8
|
'data-button': 'resource-filters',
|
7
9
|
'data-action': 'click->toggle-panel#togglePanel',
|
@@ -1,5 +1,5 @@
|
|
1
|
-
<div data-controller="search" class="global-search" data-turbo-remove-before-cache>
|
2
|
-
<div class="inline-block"
|
1
|
+
<div data-controller="search" class="global-search rounded border border-gray-200 pr-4 min-w-[16rem]" data-turbo-remove-before-cache>
|
2
|
+
<div class="inline-block text-gray-500"
|
3
3
|
data-search-target="autocomplete"
|
4
4
|
data-search-resource="global"
|
5
5
|
data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>", "placeholder": "<%= I18n.translate 'avo.search.placeholder' %>", "cancel_button": "<%= I18n.translate 'avo.search.cancel_button' %>"}'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="relative bg-white p-2 h-20 w-full flex flex-shrink-0 items-center z-50 px-6 border-b" v-if="layout !== 'blank'">
|
2
|
+
<div>
|
3
|
+
<%= render partial: "avo/partials/header" %>
|
4
|
+
</div>
|
5
|
+
<div class="flex-1 flex ml-4 pl-4">
|
6
|
+
<%= render partial: "avo/partials/global_search" if ::Avo::App.license.has_with_trial(:global_search) && ::Avo.configuration.feature_enabled?(:global_search) %>
|
7
|
+
</div>
|
8
|
+
<div class="align-end">
|
9
|
+
<% # Notifications %>
|
10
|
+
</div>
|
11
|
+
</div>
|
@@ -8,12 +8,10 @@
|
|
8
8
|
per_page_options = per_page_options.sort.uniq
|
9
9
|
%>
|
10
10
|
|
11
|
-
<div class="
|
11
|
+
<div class="flex items-center justify-between aborder-t aborder-slate-200 rounded-xl">
|
12
12
|
<div class="hidden sm:flex-2 sm:flex sm:items-center sm:justify-between">
|
13
13
|
<div>
|
14
14
|
<div class="text-sm leading-5 text-slate-600 flex items-center">
|
15
|
-
<div class="mr-2"><%== pagy_info @pagy %></div>
|
16
|
-
|
17
15
|
<div data-controller="per-page">
|
18
16
|
<div class="flex items-center">
|
19
17
|
<%= select_tag 'per_page',
|
@@ -39,7 +37,10 @@
|
|
39
37
|
</div>
|
40
38
|
</div>
|
41
39
|
<div class="flex">
|
40
|
+
|
42
41
|
<div class="flex-2 sm:flex sm:items-center sm:justify-between">
|
42
|
+
<div class="mr-4"><%== pagy_info @pagy %></div>
|
43
|
+
|
43
44
|
<%# @todo: add first & last page. make the first and last buttons rounded %>
|
44
45
|
<% if @pagy.pages > 1 %>
|
45
46
|
<%== pagy_nav @pagy %>
|
@@ -5,5 +5,5 @@
|
|
5
5
|
data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>"}'
|
6
6
|
>
|
7
7
|
</div>
|
8
|
-
<div class="hidden relative inline-flex text-gray-400 text-sm border border-gray-300 rounded
|
8
|
+
<div class="hidden relative inline-flex text-gray-400 text-sm border border-gray-300 rounded cursor-pointer" data-search-target="button"></div>
|
9
9
|
</div>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<div class="application-sidebar flex h-full bg-white text-white w-72 z-50 border-r">
|
2
|
+
<div class="flex flex-col w-full">
|
3
|
+
<%= render partial: "avo/partials/logo" %>
|
4
|
+
|
5
|
+
<div class="flex-1 flex flex-col justify-between">
|
6
|
+
<div class="space-y-2">
|
7
|
+
<div class="w-full">
|
8
|
+
<%= render Avo::NavigationLinkComponent.new label: 'Get started', path: avo.root_path, active: :exclusive if Rails.env.development? && Avo.configuration.home_path.nil? %>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="w-full">
|
12
|
+
<%= render Avo::NavigationHeadingComponent.new label: t('avo.resources'), icon: svg('resources-icon') %>
|
13
|
+
<% Avo::App.resources_navigation(_current_user).sort_by { |r| r.navigation_label }.each do |resource| %>
|
14
|
+
<%= render Avo::NavigationLinkComponent.new label: resource.navigation_label, path: resources_path(resource: resource) %>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="w-full">
|
19
|
+
<% tools = Avo::App.get_sidebar_partials %>
|
20
|
+
|
21
|
+
<% if tools.present? %>
|
22
|
+
<%= render Avo::NavigationHeadingComponent.new label: t('avo.tools'), icon: svg('tools-icon') %>
|
23
|
+
|
24
|
+
<% tools.each do |partial| %>
|
25
|
+
<%= render partial: "/avo/sidebar/items/#{partial}" %>
|
26
|
+
<% end %>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<%= render_license_warnings %>
|
32
|
+
<%= render Avo::SidebarProfileComponent.new user: _current_user %>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
<thead class="bg-
|
2
|
+
<thead class="bg-white border-b border-gray-200 pb-1">
|
3
3
|
<th>
|
4
4
|
<%== item_select_all_input %>
|
5
5
|
</th>
|
@@ -28,9 +28,9 @@
|
|
28
28
|
sort_by = field.id
|
29
29
|
sort_direction = 'desc'
|
30
30
|
end
|
31
|
-
classes = "text-
|
31
|
+
classes = "text-gray-500 tracking-tight leading-tight text-sm font-semibold"
|
32
32
|
%>
|
33
|
-
<th class="text-left uppercase px-3 py-
|
33
|
+
<th class="text-left uppercase px-3 py-6 whitespace-nowrap">
|
34
34
|
<% if field.sortable %>
|
35
35
|
<%= link_to params.permit!.merge(sort_by: sort_by, sort_direction: sort_direction), class: "flex items-center #{classes} #{'cursor-pointer' if field.sortable}", 'data-turbo-frame': params[:turbo_frame] do %>
|
36
36
|
<%= field.name %>
|
@@ -1,3 +1,9 @@
|
|
1
1
|
<% if name.present? %><turbo-frame id="<%= name %>"><% end %>
|
2
|
-
|
2
|
+
<%
|
3
|
+
# When rendering the frames the flashed content gets lost.
|
4
|
+
# By including the alerts partial, the stimulus will pick them up and display them to the user.
|
5
|
+
%>
|
6
|
+
<%= render partial: 'avo/partials/alerts'if flash.present? if flash.present? %>
|
7
|
+
|
8
|
+
<%= yield %>
|
3
9
|
<% if name.present? %></turbo-frame><% end %>
|