avo 2.5.2.pre.4 → 2.5.2.pre.7

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +4 -1
  4. data/app/assets/builds/avo.css +764 -166
  5. data/app/assets/builds/avo.js +212 -123
  6. data/app/assets/builds/avo.js.map +3 -3
  7. data/app/assets/stylesheets/avo.css +3 -33
  8. data/app/assets/stylesheets/css/alerts.css +35 -0
  9. data/app/assets/stylesheets/css/search.css +1 -1
  10. data/app/assets/stylesheets/css/tags.css +16 -0
  11. data/app/assets/svgs/heroicons/solid/user-remove.svg +1 -1
  12. data/app/components/avo/actions_component.html.erb +1 -2
  13. data/app/components/avo/alert_component.html.erb +1 -1
  14. data/app/components/avo/alert_component.rb +5 -24
  15. data/app/components/avo/button_component.rb +0 -2
  16. data/app/components/avo/fields/tags_field/edit_component.html.erb +27 -0
  17. data/app/components/avo/fields/tags_field/edit_component.rb +4 -0
  18. data/app/components/avo/fields/tags_field/index_component.html.erb +14 -0
  19. data/app/components/avo/fields/tags_field/index_component.rb +7 -0
  20. data/app/components/avo/fields/tags_field/show_component.html.erb +7 -0
  21. data/app/components/avo/fields/tags_field/show_component.rb +11 -0
  22. data/app/components/avo/fields/tags_field/tag_component.html.erb +9 -0
  23. data/app/components/avo/fields/tags_field/tag_component.rb +11 -0
  24. data/app/components/avo/filters_component.html.erb +1 -1
  25. data/app/components/avo/index/field_wrapper_component.html.erb +1 -1
  26. data/app/components/avo/index/grid_cover_empty_state_component.html.erb +1 -1
  27. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  28. data/app/components/avo/panel_component.html.erb +3 -3
  29. data/app/components/avo/panel_component.rb +1 -1
  30. data/app/components/avo/resource_component.rb +0 -50
  31. data/app/components/avo/sidebar/heading_component.html.erb +1 -1
  32. data/app/components/avo/sidebar/link_component.rb +1 -1
  33. data/app/components/avo/sidebar_component.html.erb +13 -5
  34. data/app/components/avo/sidebar_profile_component.html.erb +1 -1
  35. data/app/components/avo/views/resource_edit_component.html.erb +3 -28
  36. data/app/components/avo/views/resource_edit_component.rb +6 -4
  37. data/app/components/avo/views/resource_index_component.html.erb +7 -15
  38. data/app/components/avo/views/resource_new_component.html.erb +2 -8
  39. data/app/components/avo/views/resource_show_component.html.erb +5 -15
  40. data/app/components/avo/views/resource_show_component.rb +45 -0
  41. data/app/controllers/avo/actions_controller.rb +8 -23
  42. data/app/controllers/avo/associations_controller.rb +3 -3
  43. data/app/controllers/avo/base_controller.rb +16 -25
  44. data/app/controllers/avo/search_controller.rb +2 -2
  45. data/app/helpers/avo/application_helper.rb +1 -1
  46. data/app/javascript/js/application.js +1 -1
  47. data/app/javascript/js/controllers/alerts_controller.js +26 -0
  48. data/app/javascript/js/controllers/base_controller.js +22 -0
  49. data/app/javascript/js/controllers/fields/key_value_controller.js +1 -1
  50. data/app/javascript/js/controllers/fields/tags_field_controller.js +86 -0
  51. data/app/javascript/js/controllers/fields/tags_field_helpers.js +47 -0
  52. data/app/javascript/js/controllers/filter_controller.js +1 -4
  53. data/app/javascript/js/controllers.js +4 -0
  54. data/app/views/avo/actions/show.html.erb +2 -5
  55. data/app/views/avo/partials/_logo.html.erb +1 -1
  56. data/app/views/avo/partials/_navbar.html.erb +6 -12
  57. data/app/views/avo/private/_links_and_buttons.html.erb +1 -1
  58. data/app/views/layouts/avo/application.html.erb +53 -50
  59. data/db/factories.rb +2 -0
  60. data/lib/avo/base_action.rb +6 -24
  61. data/lib/avo/base_resource.rb +6 -0
  62. data/lib/avo/concerns/handles_field_args.rb +36 -0
  63. data/lib/avo/engine.rb +1 -1
  64. data/lib/avo/fields/base_field.rb +2 -1
  65. data/lib/avo/fields/belongs_to_field.rb +4 -4
  66. data/lib/avo/fields/has_and_belongs_to_many_field.rb +2 -2
  67. data/lib/avo/fields/has_base_field.rb +0 -2
  68. data/lib/avo/fields/has_many_field.rb +2 -2
  69. data/lib/avo/fields/has_one_field.rb +2 -2
  70. data/lib/avo/fields/tags_field.rb +82 -0
  71. data/lib/avo/hosts/record_host.rb +7 -0
  72. data/lib/avo/licensing/pro_license.rb +2 -1
  73. data/lib/avo/version.rb +1 -1
  74. data/lib/generators/avo/templates/locales/avo.en.yml +4 -0
  75. data/public/avo-assets/avo.css +812 -214
  76. data/public/avo-assets/avo.js +212 -123
  77. data/public/avo-assets/avo.js.map +3 -3
  78. metadata +19 -2
@@ -8,38 +8,32 @@
8
8
  <% c.tools do %>
9
9
  <% if resource_panel[:name] == @resource.default_panel_name %>
10
10
  <% if @reflection.present? && @resource.model.present? %>
11
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource %>
11
12
  <% if can_detach? %>
12
13
  <%= a_button url: detach_path,
13
14
  icon: 'detach',
14
15
  method: :delete,
15
16
  form_class: 'flex flex-col sm:flex-row sm:inline-flex',
16
- style: :text,
17
17
  data: {
18
18
  confirm: "Are you sure you want to detach this #{@reflection.name.to_s}."
19
19
  } do %>
20
20
  <%= t('avo.detach_item', item: @reflection.name.to_s).capitalize %>
21
21
  <% end %>
22
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource %>
23
22
  <% end %>
24
23
  <% if can_see_the_edit_button? %>
25
- <%= a_link edit_path,
26
- color: :primary,
27
- style: :primary,
28
- icon: 'edit' do %>
24
+ <%= a_link edit_path, color: :primary, icon: 'edit' do %>
29
25
  <%= t('avo.edit').capitalize %>
30
26
  <% end %>
31
27
  <% end %>
32
28
  <% else %>
33
- <%= a_link back_path,
34
- style: :text,
35
- icon: 'arrow-left' do %>
29
+ <%= a_link back_path, icon: 'arrow-left' do %>
36
30
  <%= t('avo.go_back') %>
37
31
  <% end %>
32
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource %>
38
33
  <% if can_see_the_destroy_button? %>
39
34
  <%= a_button url: helpers.resource_path(model: @resource.model, resource: @resource),
40
35
  method: :delete,
41
36
  local: true,
42
- style: :text,
43
37
  title: t('avo.delete_item', item: @resource.model.model_name.name.downcase).capitalize,
44
38
  loading: true,
45
39
  confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
@@ -54,12 +48,8 @@
54
48
  <%= t('avo.delete').capitalize %>
55
49
  <% end %>
56
50
  <% end %>
57
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource %>
58
51
  <% if @resource.authorization.authorize_action(:edit, raise_exception: false) %>
59
- <%= a_link edit_path,
60
- color: :primary,
61
- style: :primary,
62
- icon: 'edit' do %>
52
+ <%= a_link edit_path, color: :primary, icon: 'edit' do %>
63
53
  <%= t('avo.edit').capitalize %>
64
54
  <% end %>
65
55
  <% end %>
@@ -44,8 +44,53 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
44
44
  helpers.edit_resource_path(model: @resource.model, resource: @resource, **args)
45
45
  end
46
46
 
47
+ def detach_path
48
+ helpers.resource_detach_path(params[:resource_name], params[:id], @reflection.name.to_s, @resource.model.id)
49
+ end
50
+
51
+ def destroy_path
52
+ helpers.resource_path(model: @resource.model, resource: @resource)
53
+ end
54
+
55
+ def can_detach?
56
+ authorize_association_for("detach")
57
+ end
58
+
59
+ def can_see_the_edit_button?
60
+ @resource.authorization.authorize_action(:edit, raise_exception: false)
61
+ end
62
+
63
+ def can_see_the_destroy_button?
64
+ @resource.authorization.authorize_action(:destroy, raise_exception: false)
65
+ end
66
+
47
67
  private
48
68
 
69
+ def via_resource?
70
+ params[:via_resource_class].present? && params[:via_resource_id].present?
71
+ end
72
+
73
+ def split_panel_fields
74
+ @fields_by_panel = {}
75
+ @has_one_panels = []
76
+ @has_many_panels = []
77
+ @has_as_belongs_to_many_panels = []
78
+
79
+ @resource.get_fields.each do |field|
80
+ case field.class.to_s
81
+ when "Avo::Fields::HasOneField"
82
+ @has_one_panels << field
83
+ when "Avo::Fields::HasManyField"
84
+ @has_many_panels << field
85
+ when "Avo::Fields::HasAndBelongsToManyField"
86
+ @has_as_belongs_to_many_panels << field
87
+ else
88
+ @fields_by_panel[field.panel_name] ||= []
89
+ @fields_by_panel[field.panel_name] << field
90
+ end
91
+ end
92
+ end
93
+
49
94
  # In development and test environments we shoudl show the invalid field errors
50
95
  def should_display_invalid_fields_errors?
51
96
  (Rails.env.development? || Rails.env.test?) && @resource.invalid_fields.present?
@@ -14,12 +14,14 @@ module Avo
14
14
  resource_ids = action_params[:fields][:resource_ids].split(",")
15
15
  models = @resource.class.find_scope.find resource_ids
16
16
 
17
- fields = action_params[:fields].except("resource_ids")
17
+ fields = action_params[:fields].select do |key, value|
18
+ key != "resource_ids"
19
+ end
18
20
 
19
21
  args = {
20
22
  fields: fields,
21
23
  current_user: _current_user,
22
- resource: resource
24
+ resource: resource,
23
25
  }
24
26
 
25
27
  args[:models] = models unless @action.standalone
@@ -47,7 +49,8 @@ module Avo
47
49
 
48
50
  def respond(response)
49
51
  response[:type] ||= :reload
50
- messages = get_messages response
52
+ response[:message_type] ||= :notice
53
+ response[:message] ||= I18n.t("avo.action_ran_successfully")
51
54
 
52
55
  if response[:type] == :download
53
56
  return send_data response[:path], filename: response[:filename]
@@ -55,11 +58,6 @@ module Avo
55
58
 
56
59
  respond_to do |format|
57
60
  format.html do
58
- # Flash the messages collected from the action
59
- messages.each do |message|
60
- flash[message[:type]] = message[:body]
61
- end
62
-
63
61
  if response[:type] == :redirect
64
62
  path = response[:path]
65
63
 
@@ -67,25 +65,12 @@ module Avo
67
65
  path = instance_eval(&path)
68
66
  end
69
67
 
70
- redirect_to path
68
+ redirect_to path, "#{response[:message_type]}": response[:message]
71
69
  elsif response[:type] == :reload
72
- redirect_back fallback_location: resources_path(resource: @resource)
70
+ redirect_back fallback_location: resources_path(resource: @resource), "#{response[:message_type]}": response[:message]
73
71
  end
74
72
  end
75
73
  end
76
74
  end
77
-
78
- private
79
-
80
- def get_messages(response)
81
- default_message = {
82
- type: :info,
83
- body: I18n.t("avo.action_ran_successfully")
84
- }
85
-
86
- return [default_message] if response[:messages].blank?
87
-
88
- response[:messages]
89
- end
90
75
  end
91
76
  end
@@ -46,8 +46,8 @@ module Avo
46
46
  query = @authorization.apply_policy @attachment_class
47
47
 
48
48
  # Add the association scope to the query scope
49
- if @field.attach_scope.present?
50
- query = Avo::Hosts::AssociationScopeHost.new(block: @field.attach_scope, query: query, parent: @model).handle
49
+ if @field.scope.present?
50
+ query = Avo::Hosts::AssociationScopeHost.new(block: @field.scope, query: query, parent: @model).handle
51
51
  end
52
52
 
53
53
  @options = query.all.map do |model|
@@ -65,7 +65,7 @@ module Avo
65
65
 
66
66
  respond_to do |format|
67
67
  if @model.save
68
- format.html { redirect_back fallback_location: resource_path(model: @model, resource: @resource), notice: t("avo.attachment_class_attached", attachment_class: @related_resource.name) }
68
+ format.html { redirect_to resource_path(model: @model, resource: @resource), notice: t("avo.attachment_class_attached", attachment_class: @related_resource.name) }
69
69
  else
70
70
  format.html { render :new }
71
71
  end
@@ -11,8 +11,8 @@ module Avo
11
11
  before_action :set_edit_title_and_breadcrumbs, only: [:edit, :update]
12
12
  before_action :fill_model, only: [:create, :update]
13
13
  before_action :authorize_action
14
- # before_action :reset_pagination_if_filters_changed, only: :index
15
- # before_action :cache_applied_filters, only: :index
14
+ before_action :reset_pagination_if_filters_changed, only: :index
15
+ before_action :cache_applied_filters, only: :index
16
16
 
17
17
  def index
18
18
  @page_title = @resource.plural_name.humanize
@@ -49,10 +49,10 @@ module Avo
49
49
  # Check if the sortable field option is actually a proc and we need to do a custom sort
50
50
  field_id = @index_params[:sort_by].to_sym
51
51
  field = @resource.get_field_definitions.find { |field| field.id == field_id }
52
- @query = if field&.sortable.is_a?(Proc)
53
- field.sortable.call(@query, @index_params[:sort_direction])
52
+ if field&.sortable.is_a?(Proc)
53
+ @query = field.sortable.call(@query, @index_params[:sort_direction])
54
54
  else
55
- @query.order("#{@resource.model_class.table_name}.#{@index_params[:sort_by]} #{@index_params[:sort_direction]}")
55
+ @query = @query.order("#{@resource.model_class.table_name}.#{@index_params[:sort_by]} #{@index_params[:sort_direction]}")
56
56
  end
57
57
  end
58
58
 
@@ -61,14 +61,7 @@ module Avo
61
61
  @query = filter_class.safe_constantize.new.apply_query request, @query, filter_value
62
62
  end
63
63
 
64
- extra_pagy_params = {}
65
-
66
- # Reset open filters when a user navigates to a new page
67
- extra_pagy_params[:keep_filters_panel_open] = if params[:keep_filters_panel_open] == "1"
68
- "0"
69
- end
70
-
71
- @pagy, @models = pagy(@query, items: @index_params[:per_page], link_extra: "data-turbo-frame=\"#{params[:turbo_frame]}\"", size: [1, 2, 2, 1], params: extra_pagy_params)
64
+ @pagy, @models = pagy(@query, items: @index_params[:per_page], link_extra: "data-turbo-frame=\"#{params[:turbo_frame]}\"", size: [1, 2, 2, 1])
72
65
 
73
66
  # Create resources for each model
74
67
  @resources = @models.map do |model|
@@ -361,21 +354,19 @@ module Avo
361
354
  end
362
355
 
363
356
  # Caching these so we know when the filters have changed so we reset the pagination
364
- # def cache_applied_filters
365
- # # puts ["Rails.session->", session].inspect
366
- # session[:avo_applied_filters] = params[:filters]
367
- # # ::Avo::App.cache_store.delete(applied_filters_cache_key) if params[:filters].nil?
357
+ def cache_applied_filters
358
+ ::Avo::App.cache_store.delete applied_filters_cache_key if params[:filters].nil?
368
359
 
369
- # # ::Avo::App.cache_store.write(applied_filters_cache_key, params[:filters], expires_in: 1.day)
370
- # end
360
+ ::Avo::App.cache_store.write(applied_filters_cache_key, params[:filters], expires_in: 1.day)
361
+ end
371
362
 
372
- # def reset_pagination_if_filters_changed
373
- # params[:page] = 1 if params[:filters] != session[:avo_applied_filters]
374
- # end
363
+ def reset_pagination_if_filters_changed
364
+ params[:page] = 1 if params[:filters] != ::Avo::App.cache_store.read(applied_filters_cache_key)
365
+ end
375
366
 
376
- # def applied_filters_cache_key
377
- # "avo.base_controller.#{@resource.model_key}.applied_filters"
378
- # end
367
+ def applied_filters_cache_key
368
+ "avo.base_controller.#{@resource.model_key}.applied_filters"
369
+ end
379
370
 
380
371
  def set_edit_title_and_breadcrumbs
381
372
  @resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
@@ -51,12 +51,12 @@ module Avo
51
51
  # Fetch the field
52
52
  field = belongs_to_field
53
53
 
54
- if field.attach_scope.present?
54
+ if field.scope.present?
55
55
  # Fetch the parent
56
56
  parent = params[:via_reflection_class].safe_constantize.find params[:via_reflection_id]
57
57
 
58
58
  # Add to the query
59
- query = Avo::Hosts::AssociationScopeHost.new(block: belongs_to_field.attach_scope, query: query, parent: parent).handle
59
+ query = Avo::Hosts::AssociationScopeHost.new(block: belongs_to_field.scope, query: query, parent: parent).handle
60
60
  end
61
61
  end
62
62
 
@@ -118,7 +118,7 @@ module Avo
118
118
  end
119
119
 
120
120
  def input_classes(extra_classes = "", has_error: false)
121
- classes = "appearance-none inline-flex bg-gray-25 disabled:cursor-not-allowed text-gray-600 disabled:opacity-50 rounded py-2 px-3 leading-tight border focus:border-gray-600 focus-visible:ring-0 focus:text-gray-700"
121
+ classes = "appearance-none inline-flex bg-gray-100 disabled:cursor-not-allowed text-gray-600 disabled:opacity-50 rounded py-2 px-3 leading-tight border focus:border-gray-600 focus-visible:ring-0 focus:text-gray-700"
122
122
 
123
123
  classes += if has_error
124
124
  " border-red-600"
@@ -4,7 +4,7 @@ import { Application } from '@hotwired/stimulus'
4
4
  const application = Application.start()
5
5
 
6
6
  // Configure Stimulus development experience
7
- application.debug = false
7
+ application.debug = window?.localStorage.getItem('avo.debug')
8
8
  window.Stimulus = application
9
9
 
10
10
  // Register stimulus-components controller
@@ -0,0 +1,26 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['container']
5
+
6
+ connect() {
7
+ window.toastr[this.type](this.message)
8
+ }
9
+
10
+ get type() {
11
+ const typeMap = {
12
+ info: 'info',
13
+ warning: 'warning',
14
+ success: 'success',
15
+ error: 'error',
16
+ notice: 'info',
17
+ alert: 'error',
18
+ }
19
+
20
+ return typeMap[this.containerTarget.dataset.alertType]
21
+ }
22
+
23
+ get message() {
24
+ return this.containerTarget.innerHTML
25
+ }
26
+ }
@@ -0,0 +1,22 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ /**
5
+ * Helper that parses the data attribute value to JSON
6
+ */
7
+ getJsonAttribute(target, attribute, defaultValue = []) {
8
+ let result = defaultValue
9
+ try {
10
+ result = JSON.parse(target.getAttribute(attribute))
11
+ } catch (error) {}
12
+
13
+ return result
14
+ }
15
+
16
+ /**
17
+ * Parses the attribute to boolean
18
+ */
19
+ getBooleanAttribute(target, attribute) {
20
+ return target.getAttribute(attribute) === '1'
21
+ }
22
+ }
@@ -110,7 +110,7 @@ export default class extends Controller {
110
110
 
111
111
  inputField(id = 'key', index, key, value) {
112
112
  return `<input
113
- class="${this.options.inputClasses} focus:bg-gray-100 !rounded-none border-gray-600 border-r border-l-0 border-b-0 border-t-0 focus:border-gray-300 w-1/2 focus:outline-none outline-none key-value-input-${id}"
113
+ class="${this.options.inputClasses} !rounded-none border-gray-600 border-r border-l-0 border-b-0 border-t-0 focus:border-gray-300 w-1/2 focus:outline-none outline-none key-value-input-${id}"
114
114
  data-action="input->key-value#${id}FieldUpdated"
115
115
  placeholder="${this.options[`${id}_label`]}"
116
116
  data-index="${index}"
@@ -0,0 +1,86 @@
1
+ import { first, isObject, merge } from 'lodash'
2
+ import Tagify from '@yaireo/tagify'
3
+
4
+ import BaseController from '../base_controller'
5
+
6
+ import { suggestionItemTemplate, tagTemplate } from './tags_field_helpers'
7
+
8
+ export default class extends BaseController {
9
+ static targets = ['input', 'fakeInput'];
10
+
11
+ tagify = null;
12
+
13
+ get whitelistItems() {
14
+ return this.getJsonAttribute(this.inputTarget, 'data-whitelist-items', [])
15
+ }
16
+
17
+ get blacklistItems() {
18
+ return this.getJsonAttribute(this.inputTarget, 'data-blacklist-items', [])
19
+ }
20
+
21
+ get enforceSuggestions() {
22
+ return this.getBooleanAttribute(this.inputTarget, 'data-enforce-suggestions')
23
+ }
24
+
25
+ get closeOnSelect() {
26
+ return this.getBooleanAttribute(this.inputTarget, 'data-close-on-select')
27
+ }
28
+
29
+ get delimiters() {
30
+ return this.getJsonAttribute(this.inputTarget, 'data-delimiters', [])
31
+ }
32
+
33
+ get suggestionsAreObjects() {
34
+ return isObject(first(this.whitelistItems))
35
+ }
36
+
37
+ get tagifyOptions() {
38
+ let options = {
39
+ whitelist: this.whitelistItems,
40
+ blacklist: this.blacklistItems,
41
+ enforceWhitelist: this.enforceSuggestions,
42
+ delimiters: this.delimiters.join('|'),
43
+ maxTags: 10,
44
+ dropdown: {
45
+ maxItems: 20,
46
+ enabled: 0,
47
+ closeOnSelect: this.closeOnSelect,
48
+ },
49
+ }
50
+
51
+ if (this.suggestionsAreObjects) {
52
+ options = merge(options, {
53
+ tagTextProp: 'label',
54
+ dropdown: {
55
+ searchKeys: ['label'],
56
+ },
57
+ templates: {
58
+ tag: tagTemplate,
59
+ dropdownItem: suggestionItemTemplate,
60
+ },
61
+ })
62
+ }
63
+
64
+ return options
65
+ }
66
+
67
+ connect() {
68
+ if (this.hasInputTarget) {
69
+ this.hideFakeInput()
70
+ this.showRealInput()
71
+ this.initTagify()
72
+ }
73
+ }
74
+
75
+ initTagify() {
76
+ this.tagify = new Tagify(this.inputTarget, this.tagifyOptions)
77
+ }
78
+
79
+ hideFakeInput() {
80
+ this.fakeInputTarget.classList.add('hidden')
81
+ }
82
+
83
+ showRealInput() {
84
+ this.inputTarget.classList.remove('hidden')
85
+ }
86
+ }
@@ -0,0 +1,47 @@
1
+ export function tagTemplate(tagData) {
2
+ const suggestions = this.settings.whitelist || []
3
+
4
+ const possibleSuggestion = suggestions.find(
5
+ // eslint-disable-next-line eqeqeq
6
+ (item) => item.value == tagData.value,
7
+ )
8
+ const possibleLabel = possibleSuggestion
9
+ ? possibleSuggestion.label
10
+ : tagData.value
11
+
12
+ return `
13
+ <tag title="${tagData.value}"
14
+ contenteditable='false'
15
+ spellcheck='false'
16
+ tabIndex="-1"
17
+ class="tagify__tag ${tagData.class ? tagData.class : ''}"
18
+ ${this.getAttributes(tagData)}
19
+ >
20
+ <x title='' class='tagify__tag__removeBtn' role='button' aria-label='remove tag'></x>
21
+ <div>
22
+ <span class='tagify__tag-text'>${possibleLabel}</span>
23
+ </div>
24
+ </tag>
25
+ `
26
+ }
27
+
28
+ export function suggestionItemTemplate(tagData) {
29
+ return `
30
+ <div ${this.getAttributes(tagData)}
31
+ class='tagify__dropdown__item flex items-center ${
32
+ tagData.class ? tagData.class : ''
33
+ }'
34
+ tabindex="0"
35
+ role="option">
36
+ ${
37
+ tagData.avatar
38
+ ? `
39
+ <div class='rounded w-8 h-8 block mr-2'>
40
+ <img onerror="this.style.visibility='hidden'" class="w-full" src="${tagData.avatar}">
41
+ </div>`
42
+ : ''
43
+ }
44
+ <span>${tagData.label}</span>
45
+ </div>
46
+ `
47
+ }
@@ -84,12 +84,9 @@ export default class extends Controller {
84
84
 
85
85
  if (this.keepFiltersPanelOpenValue) {
86
86
  // eslint-disable-next-line camelcase
87
- query.keep_filters_panel_open = this.keepFiltersPanelOpenValue ? 1 : null
87
+ query.keep_filters_panel_open = this.keepFiltersPanelOpenValue
88
88
  }
89
89
 
90
- // force to go to the first page if the filters changed
91
- query.page = 1
92
-
93
90
  if (encodedFilters) {
94
91
  query.filters = encodedFilters
95
92
  } else {
@@ -2,6 +2,7 @@ import { application } from './application'
2
2
 
3
3
  import ActionController from './controllers/action_controller'
4
4
  import ActionsPickerController from './controllers/actions_picker_controller'
5
+ import AlertsController from './controllers/alerts_controller'
5
6
  import AttachmentsController from './controllers/attachments_controller'
6
7
  import BelongsToFieldController from './controllers/fields/belongs_to_field_controller'
7
8
  import BooleanFilterController from './controllers/boolean_filter_controller'
@@ -24,6 +25,7 @@ import SearchController from './controllers/search_controller'
24
25
  import SelectController from './controllers/select_controller'
25
26
  import SelectFilterController from './controllers/select_filter_controller'
26
27
  import SimpleMdeController from './controllers/fields/simple_mde_controller'
28
+ import TagsFieldController from './controllers/fields/tags_field_controller'
27
29
  import TextFilterController from './controllers/text_filter_controller'
28
30
  import TippyController from './controllers/tippy_controller'
29
31
  import TogglePanelController from './controllers/toggle_panel_controller'
@@ -31,6 +33,7 @@ import TrixFieldController from './controllers/fields/trix_field_controller'
31
33
 
32
34
  application.register('action', ActionController)
33
35
  application.register('actions-picker', ActionsPickerController)
36
+ application.register('alerts', AlertsController)
34
37
  application.register('attachments', AttachmentsController)
35
38
  application.register('boolean-filter', BooleanFilterController)
36
39
  application.register('copy-to-clipboard', CopyToClipboardController)
@@ -48,6 +51,7 @@ application.register('per-page', PerPageController)
48
51
  application.register('search', SearchController)
49
52
  application.register('select', SelectController)
50
53
  application.register('select-filter', SelectFilterController)
54
+ application.register('tags-field', TagsFieldController)
51
55
  application.register('text-filter', TextFilterController)
52
56
  application.register('tippy', TippyController)
53
57
  application.register('toggle-panel', TogglePanelController)
@@ -29,14 +29,11 @@
29
29
  </div>
30
30
  <% end %>
31
31
  <% c.controls do %>
32
- <%= a_button data: { action: 'click->modal#close' },
33
- size: :sm,
34
- color: :primary do %>
32
+ <%= a_button data: { action: 'click->modal#close' }, size: :sm do %>
35
33
  <%= @action.cancel_button_label %>
36
34
  <% end %>
37
35
  <%= a_button type: :submit,
38
- color: :primary,
39
- style: :primary,
36
+ color: :green,
40
37
  size: :sm,
41
38
  data: @action.class.submit_button_data_attributes do %>
42
39
  <%= @action.confirm_button_label %>
@@ -1,3 +1,3 @@
1
- <%= link_to root_path, class: 'logo-placeholder h-10 bg-white flex justify-start' do %>
1
+ <%= link_to root_path, class: 'logo-placeholder h-20 bg-white p-4 flex justify-start' do %>
2
2
  <%= image_tag '/avo-assets/logo.png', class: 'h-full', title: 'Avo' %>
3
3
  <% end %>
@@ -1,20 +1,14 @@
1
1
  <div
2
- class="fixed bg-white p-2 w-full flex flex-shrink-0 items-center z-50 px-4 lg:px-4 border-b space-x-4 lg:space-x-0 min-h-[4rem] <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
2
+ class="relative bg-white p-2 w-full flex flex-shrink-0 items-center z-50 px-4 lg:px-8 border-b space-x-4 lg:space-x-0 min-h-[4rem] <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
3
3
  v-if="layout !== 'blank'"
4
4
  >
5
- <div class="flex items-center space-x-2 lg:space-x-0 w-64">
6
- <%= a_button class: 'lg:hidden', icon: 'menu', size: :xs, compact: true, style: :text, data: { action: 'click->mobile#toggleSidebar' } %>
7
-
8
- <%= render partial: "avo/partials/logo" %>
9
- </div>
10
-
11
-
12
- <div class="flex-1 flex items-center justify-between lg:justify-start space-x-8 pl-4">
13
- <div class="flex">
14
- <%= render partial: "avo/partials/global_search" if ::Avo::App.license.has_with_trial(:global_search) && ::Avo.configuration.feature_enabled?(:global_search) %>
15
- </div>
5
+ <%= a_button class: 'lg:hidden', icon: 'menu', data: { action: 'click->mobile#toggleSidebar' } %>
6
+ <div class="flex-1 flex items-center justify-between lg:justify-start space-x-8">
16
7
  <div class="m-0">
17
8
  <%= render partial: "avo/partials/header" %>
18
9
  </div>
10
+ <div class="flex">
11
+ <%= render partial: "avo/partials/global_search" if ::Avo::App.license.has_with_trial(:global_search) && ::Avo.configuration.feature_enabled?(:global_search) %>
12
+ </div>
19
13
  </div>
20
14
  </div>
@@ -1,7 +1,7 @@
1
1
  <%
2
2
  entities = [:button, :link]
3
3
  sizes = [:lg, :md, :sm, :xs]
4
- styles = [:primary, :outline, :text]
4
+ styles = [:primary, :outline]
5
5
  colors = [:primary, :gray, :red, :orange, :green]
6
6
  states = [:regular, :hover, :disabled, :active]
7
7
  %>