avo 2.1.2.pre2 → 2.2.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bde8997aaf953104f34a221e6be3c93b6c7f20bc1b9f125615c0ff994cc6791f
4
- data.tar.gz: ca3a39976e8046d165cdb66a8aad6260e98d869981e73eba1d6d121bb57a7580
3
+ metadata.gz: 6ddda419ee56fc6958c6014c1c87c4fc42d33564b4c3c6642b369729f2948b48
4
+ data.tar.gz: 8b76cd83e1c6fd3a4ea564ee09d4507b2de1034c390e3cf5d7ae4ec175a569e0
5
5
  SHA512:
6
- metadata.gz: 50c30c580b89ad850a34c0125ad0079d34a1af72eefbb99193644f605692ea5f3e2bb54e1a0032ef04e219ba84888171cfcf6b69c6da045475f21afc50747680
7
- data.tar.gz: 0ce0f8be40b683c2c1ef5b8e963bbbb0d8282e4e0b7d193f4acedee530d9618481a036a0c3955214d4c024876a374009c82df85a788708aebf603943595218c1
6
+ metadata.gz: 52494cb524f70561a7ab099f44c1df29faf56897d7f487337a1d1831964e3732d31d69be7644b4adeb4a60b79e3199b2d931f5c2a726d8e2f136d6d40256b719
7
+ data.tar.gz: 2ce7d38f174655a677de092a486357fef13deca755839bd305eaf65ca81f5fd33eea543b69939ce396f65f752673b1131910044bb5ac923b71c0178ff7b8736c
data/Gemfile CHANGED
@@ -34,7 +34,7 @@ gem "puma", "~> 5.6.4"
34
34
  # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
35
35
  # gem "jbuilder", "~> 2.7"
36
36
  # Use Redis adapter to run Action Cable in production
37
- # gem 'redis', '~> 4.0'
37
+ gem 'redis', '~> 4.0'
38
38
  # Use Active Model has_secure_password
39
39
  # gem 'bcrypt', '~> 3.1.7'
40
40
 
@@ -79,6 +79,8 @@ group :development do
79
79
  # gem 'pry-rails'
80
80
 
81
81
  gem 'htmlbeautifier'
82
+
83
+ gem "hotwire-livereload", "~> 1.1"
82
84
  end
83
85
 
84
86
  group :development, :test do
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (2.1.2.pre2)
4
+ avo (2.2.0)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
@@ -183,6 +183,9 @@ GEM
183
183
  hashdiff (1.0.1)
184
184
  hightop (0.3.0)
185
185
  activesupport (>= 5.2)
186
+ hotwire-livereload (1.1.0)
187
+ listen (>= 3.0.0)
188
+ rails (>= 6.0.0)
186
189
  hotwire-rails (0.1.3)
187
190
  rails (>= 6.0.0)
188
191
  stimulus-rails
@@ -297,6 +300,7 @@ GEM
297
300
  rb-fsevent (0.11.0)
298
301
  rb-inotify (0.10.1)
299
302
  ffi (~> 1.0)
303
+ redis (4.6.0)
300
304
  regexp_parser (2.2.0)
301
305
  responders (3.0.1)
302
306
  actionpack (>= 5.0)
@@ -435,6 +439,7 @@ DEPENDENCIES
435
439
  gem-release
436
440
  groupdate
437
441
  hightop
442
+ hotwire-livereload (~> 1.1)
438
443
  hotwire-rails
439
444
  htmlbeautifier
440
445
  httparty
@@ -452,6 +457,7 @@ DEPENDENCIES
452
457
  rails (~> 6.1.0)
453
458
  rails-controller-testing
454
459
  ransack
460
+ redis (~> 4.0)
455
461
  rspec-rails (~> 4.0.0)
456
462
  rubocop
457
463
  rubocop-shopify
@@ -24,8 +24,12 @@ class Avo::ButtonComponent < ViewComponent::Base
24
24
  end
25
25
 
26
26
  def args
27
- if @args[:spinner]
27
+ if @args[:loading]
28
28
  @args[:"data-controller"] = "loading-button"
29
+
30
+ if @args[:confirm]
31
+ @args[:"data-avo-confirm"] = @args.delete(:confirm)
32
+ end
29
33
  end
30
34
 
31
35
  @args[:class] = button_classes
@@ -34,7 +38,7 @@ class Avo::ButtonComponent < ViewComponent::Base
34
38
  end
35
39
 
36
40
  def button_classes
37
- classes = "button-component inline-flex flex-grow-0 items-center text-sm font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 transform transition duration-100 cursor-pointer disabled:cursor-not-allowed disabled:opacity-30 border justify-center active:outline active:outline-1 #{@class}"
41
+ classes = "button-component inline-flex flex-grow-0 items-center text-sm font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 transform transition duration-100 cursor-pointer disabled:cursor-not-allowed disabled:opacity-70 border justify-center active:outline active:outline-1 #{@class}"
38
42
 
39
43
  classes += " rounded" if @rounded.present?
40
44
 
@@ -7,6 +7,7 @@
7
7
  ></div>
8
8
  <div class="relative w-full" autocomplete="off">
9
9
  <%= @form.text_field @foreign_key,
10
+ type: :text,
10
11
  value: field_label,
11
12
  class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
12
13
  placeholder: @field.placeholder,
@@ -9,8 +9,11 @@ class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
9
9
 
10
10
  def disabled
11
11
  return true if @field.readonly
12
- return true if @field.target_resource.present? && @field.target_resource.model_class.name == params[:via_resource_class]
13
- return true if @field.id.to_s == params[:via_relation].to_s
12
+
13
+ # When visiting the record through it's association we keep the field disabled by default
14
+ # We make an exception when the user deliberately instructs Avo to allow detaching in this scenario
15
+ return !@field.allow_via_detaching if @field.target_resource.present? && @field.target_resource.model_class.name == params[:via_resource_class]
16
+ return !@field.allow_via_detaching if @field.id.to_s == params[:via_relation].to_s
14
17
 
15
18
  false
16
19
  end
@@ -3,7 +3,9 @@
3
3
  <%== item_selector_init @resource %>
4
4
  >
5
5
  <div class="relative w-full pb-3/4 rounded-t overflow-hidden">
6
- <%== item_selector_input floating: true, size: :lg %>
6
+ <% if @resource.record_selector %>
7
+ <%== item_selector_input floating: true, size: :lg %>
8
+ <% end %>
7
9
  <% if cover.blank? %>
8
10
  <%= link_to cover.link_to_resource do %>
9
11
  <%= render Avo::Index::GridCoverEmptyStateComponent.new %>
@@ -2,11 +2,13 @@
2
2
  class="bg-white hover:bg-blue-50 hover:shadow-row hover:z-[21] relative z-20 border-b"
3
3
  <%== item_selector_init @resource %>
4
4
  >
5
- <td class="w-10">
6
- <div class="flex justify-center h-full">
7
- <%== item_selector_input floating: false %>
8
- </div>
9
- </td>
5
+ <% if @resource.record_selector %>
6
+ <td class="w-10">
7
+ <div class="flex justify-center h-full">
8
+ <%== item_selector_input floating: false %>
9
+ </div>
10
+ </td>
11
+ <% end %>
10
12
  <% @resource.get_fields(reflection: @reflection).each_with_index do |field, index| %>
11
13
  <%= render field.component_for_view(:index).new(field: field, resource: @resource, index: index, parent_model: @parent_model) %>
12
14
  <% end %>
@@ -1,19 +1,19 @@
1
1
  <div <%== data_attributes %>>
2
2
  <% if render_header? %>
3
3
  <div class="<%= white_panel_classes %> p-4 flex-1 flex flex-col xl:flex-row justify-between mb-6">
4
- <div class="overflow-hidden mr-4">
4
+ <div class="overflow-hidden mr-4 flex flex-col">
5
5
  <% if display_breadcrumbs? %>
6
6
  <div class="breadcrumbs truncate mb-2">
7
7
  <%= 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 %>
8
8
  </div>
9
9
  <% end %>
10
10
 
11
- <div class="text-2xl tracking-normal font-semibold text-gray-800 truncate" data-target="title">
12
- <%= @title %>
11
+ <div class="text-2xl tracking-normal font-semibold text-gray-800 truncate items-center flex flex-1" data-target="title">
12
+ <span><%= @title %></span>
13
13
  </div>
14
14
 
15
15
  <% if description.present? %>
16
- <div class="text-base tracking-normal font-medium text-gray-600 truncate" data-target="description">
16
+ <div class="text-base tracking-normal font-medium text-gray-600" data-target="description">
17
17
  <%== description %>
18
18
  </div>
19
19
  <% end %>
@@ -13,7 +13,7 @@
13
13
  <%= t('avo.cancel').capitalize %>
14
14
  <% end %>
15
15
  <% if can_see_the_save_button? %>
16
- <%= a_button color: :green, spinner: true, type: :submit, icon: 'save' do %>
16
+ <%= a_button color: :green, loading: true, type: :submit, icon: 'save' do %>
17
17
  <%= t('avo.save').capitalize %>
18
18
  <% end %>
19
19
  <% end %>
@@ -24,7 +24,7 @@
24
24
  <%= t('avo.cancel').capitalize %>
25
25
  <% end %>
26
26
  <% if can_see_the_save_button? %>
27
- <%= a_button color: :green, spinner: true, type: :submit, icon: 'save' do %>
27
+ <%= a_button color: :green, loading: true, type: :submit, icon: 'save' do %>
28
28
  <%= t('avo.save').capitalize %>
29
29
  <% end %>
30
30
  <% end %>
@@ -18,7 +18,7 @@
18
18
  <%= t('avo.cancel').capitalize %>
19
19
  <% end %>
20
20
  <% if can_see_the_save_button? %>
21
- <%= a_button color: 'green', spinner: true, type: :submit, icon: 'save' do %>
21
+ <%= a_button color: 'green', loading: true, type: :submit, icon: 'save' do %>
22
22
  <%= t('avo.save').capitalize %>
23
23
  <% end %>
24
24
  <% end %>
@@ -30,7 +30,7 @@
30
30
  <%= t('avo.cancel').capitalize %>
31
31
  <% end %>
32
32
  <% if can_see_the_save_button? %>
33
- <%= a_button color: :green, spinner: true, type: :submit, icon: 'save' do %>
33
+ <%= a_button color: :green, loading: true, type: :submit, icon: 'save' do %>
34
34
  <%= t('avo.save').capitalize %>
35
35
  <% end %>
36
36
  <% end %>
@@ -35,12 +35,12 @@
35
35
  method: :delete,
36
36
  local: true,
37
37
  title: t('avo.delete_item', item: @resource.model.model_name.name.downcase).capitalize,
38
- spinner: true,
38
+ loading: true,
39
+ confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
39
40
  color: :red,
40
41
  icon: 'trash',
41
42
  form_class: 'flex flex-col sm:flex-row sm:inline-flex',
42
43
  data: {
43
- confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
44
44
  control: :destroy,
45
45
  'resource-id': @resource.model.id,
46
46
  'tippy': 'tooltip',
@@ -44,7 +44,7 @@ module Avo
44
44
  unless @index_params[:sort_by].eql? :created_at
45
45
  @query = @query.unscope(:order)
46
46
  end
47
- @query = @query.order(Arel.sql("#{@resource.model_class.table_name}.#{@index_params[:sort_by]} #{@index_params[:sort_direction]}"))
47
+ @query = @query.order("#{@resource.model_class.table_name}.#{@index_params[:sort_by]} #{@index_params[:sort_direction]}")
48
48
  end
49
49
 
50
50
  # Apply filters
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-alert */
1
2
  import { Controller } from '@hotwired/stimulus'
2
3
 
3
4
  export default class extends Controller {
@@ -6,17 +7,53 @@ export default class extends Controller {
6
7
  <div class="double-bounce2"></div>
7
8
  </div>`;
8
9
 
10
+ confirmed = false
11
+
9
12
  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)
13
+ this.context.scope.element.addEventListener('click', (e) => {
14
+ // If the user has to confirm the action
15
+ if (this.confirmationMessage) {
16
+ // Intervene only if not confirmed
17
+ if (!this.confirmed) {
18
+ e.preventDefault()
19
+ if (window.confirm(this.confirmationMessage)) {
20
+ this.applyLoader()
21
+ }
22
+ }
23
+ } else {
24
+ this.applyLoader()
25
+ }
20
26
  })
21
27
  }
28
+
29
+ get button() {
30
+ return this.context.scope.element
31
+ }
32
+
33
+ get confirmationMessage() {
34
+ return this.context.scope.element.getAttribute('data-avo-confirm')
35
+ }
36
+
37
+ applyLoader() {
38
+ const { button } = this
39
+
40
+ button.style.width = `${button.getBoundingClientRect().width}px`
41
+ button.style.height = `${button.getBoundingClientRect().height}px`
42
+ button.innerHTML = this.spinnerMarkup
43
+ button.classList.add('justify-center')
44
+
45
+ setTimeout(() => {
46
+ this.markConfirmed()
47
+ button.click()
48
+ button.setAttribute('disabled', 'disabled')
49
+ }, 1)
50
+ }
51
+
52
+ markConfirmed() {
53
+ this.confirmed = true
54
+ }
55
+
56
+ markUnconfirmed() {
57
+ this.confirmed = false
58
+ }
22
59
  }
@@ -88,6 +88,9 @@ export default class extends Controller {
88
88
  } else {
89
89
  Turbo.visit(item._url, { action: 'advance' })
90
90
  }
91
+
92
+ // On searchable belongs to the class `aa-Detached` remains on the body making it unscrollable
93
+ document.body.classList.remove('aa-Detached')
91
94
  }
92
95
 
93
96
  addSource(resourceName, data) {
@@ -1,10 +1,11 @@
1
-
2
1
  <thead class="bg-white border-b border-gray-200 pb-1">
3
- <th class="rounded-lg">
4
- <%== item_select_all_input %>
5
- </th>
6
- <%
7
- fields.each_with_index do |field, index|
2
+ <% if @resource.record_selector %>
3
+ <th class="rounded-lg">
4
+ <%== item_select_all_input %>
5
+ </th>
6
+ <% end %>
7
+ <% fields.each_with_index do |field, index| %>
8
+ <%
8
9
  if params[:sort_by] == field.id.to_s
9
10
  if params[:sort_direction] == 'asc'
10
11
  sort_by = nil
@@ -28,8 +29,7 @@
28
29
  sort_by = field.id
29
30
  sort_direction = 'desc'
30
31
  end
31
- classes = "text-gray-500 tracking-tight leading-tight text-sm font-semibold"
32
- %>
32
+ classes = "text-gray-500 tracking-tight leading-tight text-sm font-semibold" %>
33
33
  <th class="text-left uppercase px-3 py-4 whitespace-nowrap rounded-l">
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 %>
@@ -37,14 +37,13 @@
37
37
  <%= render partial: 'avo/partials/sortable_component', locals: {field: field} %>
38
38
  <% end %>
39
39
  <% else %>
40
- <div class="flex items-center <%= classes %>">
41
- <%= field.name %>
42
- </div>
40
+ <div class="flex items-center <%= classes %>">
41
+ <%= field.name %>
42
+ </div>
43
43
  <% end %>
44
44
  </th>
45
45
  <% end %>
46
-
47
46
  <th class="w-24">
48
- <!-- Controls cell -->
47
+ <!-- Item controls cell -->
49
48
  </th>
50
49
  </thead>
@@ -16,6 +16,9 @@
16
16
  <% else %>
17
17
  <%= javascript_include_tag "avo", "data-turbo-track": "reload", defer: true %>
18
18
  <%= stylesheet_link_tag "avo", "data-turbo-track": "reload", defer: true %>
19
+ <% if Rails.env.development? %>
20
+ <%= javascript_include_tag "hotwire-livereload", defer: true %>
21
+ <% end %>
19
22
  <% end %>
20
23
  </head>
21
24
  <body class="bg-application os-mac">
data/bin/helpers.rb CHANGED
@@ -22,8 +22,14 @@ end
22
22
 
23
23
  def ask(question:, valid_answers: [])
24
24
  puts "\n#{question} (#{valid_answers.join('/')})"
25
+ # An uppercase option is treated as a default answer. Otherwise, we disregard case, and always
26
+ # return the answer in lowercase.
27
+ default_answer = valid_answers.select { |val| val == val.upcase }.first&.downcase
25
28
 
26
- input = gets.chomp
29
+ valid_answers.map!(&:downcase)
30
+
31
+ input = gets.downcase.chomp
32
+ input = default_answer if input == ''
27
33
 
28
34
  while !valid_answers.include?(input)
29
35
  puts 'Invalid input, please try again.'
data/bin/init CHANGED
@@ -17,7 +17,7 @@ app_root do
17
17
  header 'Installing Yarn packages'
18
18
  run! 'yarn'
19
19
 
20
- if use_docker
20
+ if use_docker == 'y'
21
21
  header 'Creating the Docker volume'
22
22
  run! 'docker volume create --name=avo-db-data'
23
23
 
@@ -28,7 +28,7 @@ app_root do
28
28
  header 'Preparing the database'
29
29
  run! 'bin/rails db:setup'
30
30
 
31
- if use_docker
31
+ if use_docker == 'y'
32
32
  header 'Stopping the Docker image'
33
33
  run! 'docker-compose stop'
34
34
  end
@@ -43,6 +43,7 @@ module Avo
43
43
  class_attribute :after_create_path, default: :show
44
44
  class_attribute :after_update_path, default: :show
45
45
  class_attribute :invalid_fields
46
+ class_attribute :record_selector, default: true
46
47
 
47
48
  class << self
48
49
  delegate :t, to: ::I18n
@@ -285,9 +286,6 @@ module Avo
285
286
  end
286
287
 
287
288
  def name
288
- # return 'hwhwhw'
289
-
290
-
291
289
  default = class_name_without_resource.titlecase
292
290
 
293
291
  return @name if @name.present?
@@ -1,7 +1,7 @@
1
1
  module Avo
2
2
  module DynamicRouter
3
3
  def self.routes(router)
4
- Rails.application.eager_load! if Rails.env.development?
4
+ Rails.application.eager_load! unless Rails.env.production?
5
5
 
6
6
  BaseResource.descendants
7
7
  .select do |resource|
data/lib/avo/engine.rb CHANGED
@@ -19,7 +19,6 @@ module Avo
19
19
 
20
20
  config.i18n.load_path += Dir[Avo::Engine.root.join('lib', 'generators', 'avo', 'templates', 'locales', '*.{rb,yml}')]
21
21
 
22
- # initializer "avo.autoload", before: :set_autoload_paths do |app|
23
22
  initializer "avo.autoload" do |app|
24
23
  [
25
24
  ["app", "avo", "fields"],
@@ -61,6 +61,7 @@ module Avo
61
61
  attr_reader :polymorphic_as
62
62
  attr_reader :relation_method
63
63
  attr_reader :types # for Polymorphic associations
64
+ attr_reader :allow_via_detaching
64
65
 
65
66
  def initialize(id, **args, &block)
66
67
  args[:placeholder] ||= I18n.t("avo.choose_an_option")
@@ -71,6 +72,7 @@ module Avo
71
72
  @polymorphic_as = args[:polymorphic_as]
72
73
  @types = args[:types]
73
74
  @relation_method = id.to_s.parameterize.underscore
75
+ @allow_via_detaching = args[:allow_via_detaching] == true
74
76
  end
75
77
 
76
78
  def searchable
data/lib/avo/reloader.rb CHANGED
@@ -3,31 +3,31 @@ class Avo::Reloader
3
3
 
4
4
  def reload!
5
5
  # reload all files declared in paths
6
- paths.each { |path| load path }
6
+ files.each { |file| load file }
7
7
 
8
8
  # reload all files declared in each directory
9
9
  directories.keys.each do |dir|
10
- Dir.glob("#{dir}/**/*.rb".to_s).each { |c| load c }
10
+ Dir.glob("#{dir}/**/*.rb".to_s).each { |file| load file }
11
11
  end
12
12
  end
13
13
 
14
14
  private
15
15
  def updater
16
- @updater ||= config.file_watcher.new(paths, directories) { reload! }
16
+ @updater ||= config.file_watcher.new(files, directories) { reload! }
17
17
  end
18
18
 
19
- def paths
19
+ def files
20
20
  # we want to watch some files no matter what
21
- files = [
21
+ paths = [
22
22
  Rails.root.join("config", "initializers", "avo.rb"),
23
23
  ]
24
24
 
25
25
  # we want to watch some files only in Avo development
26
26
  if reload_lib?
27
- files += []
27
+ paths += []
28
28
  end
29
29
 
30
- files
30
+ paths
31
31
  end
32
32
 
33
33
  def directories
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.1.2.pre2"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -8484,8 +8484,8 @@ trix-editor {
8484
8484
  opacity:0.5
8485
8485
  }
8486
8486
 
8487
- .disabled\:opacity-30:disabled{
8488
- opacity:0.3
8487
+ .disabled\:opacity-70:disabled{
8488
+ opacity:0.7
8489
8489
  }
8490
8490
 
8491
8491
  .group:hover .group-hover\:block{