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.

Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +57 -57
  3. data/README.md +1 -1
  4. data/app/assets/builds/avo.css +405 -224
  5. data/app/assets/builds/avo.js +16 -6
  6. data/app/assets/builds/avo.js.map +2 -2
  7. data/app/assets/stylesheets/avo.css +1 -1
  8. data/app/assets/stylesheets/css/fonts.css +53 -0
  9. data/app/assets/stylesheets/css/pagination.css +9 -9
  10. data/app/assets/stylesheets/css/search.css +3 -2
  11. data/app/assets/svgs/dashboards-icon.svg +6 -0
  12. data/app/assets/svgs/resources-icon.svg +13 -0
  13. data/app/assets/svgs/three-dots.svg +5 -0
  14. data/app/assets/svgs/tools-icon.svg +3 -0
  15. data/app/components/avo/button_component.html.erb +1 -0
  16. data/app/components/avo/button_component.rb +111 -0
  17. data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +14 -7
  18. data/app/components/avo/fields/belongs_to_field/autocomplete_component.rb +47 -3
  19. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +59 -38
  20. data/app/components/avo/fields/belongs_to_field/edit_component.rb +37 -0
  21. data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
  22. data/app/components/avo/index/field_wrapper_component.html.erb +8 -2
  23. data/app/components/avo/index/field_wrapper_component.rb +2 -1
  24. data/app/components/avo/index/resource_table_component.html.erb +3 -3
  25. data/app/components/avo/index/table_row_component.html.erb +1 -1
  26. data/app/components/avo/navigation_heading_component.html.erb +2 -2
  27. data/app/components/avo/navigation_heading_component.rb +5 -1
  28. data/app/components/avo/navigation_link_component.html.erb +2 -2
  29. data/app/components/avo/navigation_link_component.rb +5 -0
  30. data/app/components/avo/panel_component.html.erb +5 -3
  31. data/app/components/avo/sidebar_profile_component.html.erb +28 -0
  32. data/app/components/avo/sidebar_profile_component.rb +43 -0
  33. data/app/components/avo/views/resource_index_component.html.erb +8 -7
  34. data/app/components/avo/views/resource_show_component.html.erb +10 -0
  35. data/app/controllers/avo/application_controller.rb +14 -12
  36. data/app/controllers/avo/base_controller.rb +4 -1
  37. data/app/helpers/avo/application_helper.rb +7 -5
  38. data/app/helpers/avo/resources_helper.rb +2 -2
  39. data/app/javascript/js/controllers/fields/date_field_controller.js +10 -2
  40. data/app/javascript/js/controllers/item_selector_controller.js +29 -19
  41. data/app/javascript/js/controllers/search_controller.js +8 -3
  42. data/app/views/avo/base/_actions.html.erb +4 -3
  43. data/app/views/avo/base/_filters.html.erb +3 -1
  44. data/app/views/avo/partials/_global_search.html.erb +2 -2
  45. data/app/views/avo/partials/_logo.html.erb +3 -1
  46. data/app/views/avo/partials/_navbar.html.erb +11 -0
  47. data/app/views/avo/partials/_paginator.html.erb +4 -3
  48. data/app/views/avo/partials/_resource_search.html.erb +1 -1
  49. data/app/views/avo/partials/_sidebar.html.erb +35 -0
  50. data/app/views/avo/partials/_table_header.html.erb +3 -3
  51. data/app/views/avo/partials/_turbo_frame_wrap.html.erb +7 -1
  52. data/app/views/avo/partials/_view_toggle_button.html.erb +22 -16
  53. data/app/views/layouts/avo/application.html.erb +3 -13
  54. data/db/factories.rb +7 -3
  55. data/lib/avo/app.rb +4 -3
  56. data/lib/avo/base_resource.rb +16 -14
  57. data/lib/avo/version.rb +1 -1
  58. data/public/avo-assets/avo.css +405 -224
  59. data/public/avo-assets/avo.js +16 -6
  60. data/public/avo-assets/avo.js.map +2 -2
  61. data/public/avo-assets/fonts/inter-v7-latin-500.eot +0 -0
  62. data/public/avo-assets/fonts/inter-v7-latin-500.svg +351 -0
  63. data/public/avo-assets/fonts/inter-v7-latin-500.ttf +0 -0
  64. data/public/avo-assets/fonts/inter-v7-latin-500.woff +0 -0
  65. data/public/avo-assets/fonts/inter-v7-latin-500.woff2 +0 -0
  66. data/public/avo-assets/fonts/inter-v7-latin-600.eot +0 -0
  67. data/public/avo-assets/fonts/inter-v7-latin-600.svg +351 -0
  68. data/public/avo-assets/fonts/inter-v7-latin-600.ttf +0 -0
  69. data/public/avo-assets/fonts/inter-v7-latin-600.woff +0 -0
  70. data/public/avo-assets/fonts/inter-v7-latin-600.woff2 +0 -0
  71. data/public/avo-assets/fonts/inter-v7-latin-700.eot +0 -0
  72. data/public/avo-assets/fonts/inter-v7-latin-700.svg +352 -0
  73. data/public/avo-assets/fonts/inter-v7-latin-700.ttf +0 -0
  74. data/public/avo-assets/fonts/inter-v7-latin-700.woff +0 -0
  75. data/public/avo-assets/fonts/inter-v7-latin-700.woff2 +0 -0
  76. data/public/avo-assets/fonts/inter-v7-latin-regular.eot +0 -0
  77. data/public/avo-assets/fonts/inter-v7-latin-regular.svg +351 -0
  78. data/public/avo-assets/fonts/inter-v7-latin-regular.ttf +0 -0
  79. data/public/avo-assets/fonts/inter-v7-latin-regular.woff +0 -0
  80. data/public/avo-assets/fonts/inter-v7-latin-regular.woff2 +0 -0
  81. data/public/avo-assets/logo.png +0 -0
  82. metadata +32 -5
  83. data/app/assets/images/avo/logo.png +0 -0
  84. data/app/views/avo/partials/_profile_dropdown.html.erb +0 -25
  85. data/app/views/avo/sidebar/_sidebar.html.erb +0 -33
@@ -70,7 +70,7 @@ body {
70
70
 
71
71
  .application-sidebar .active:hover,
72
72
  .application-sidebar .active {
73
- @apply bg-gray-200;
73
+ @apply bg-blue-100 text-blue-500;
74
74
  }
75
75
 
76
76
  .turbo-progress-bar {
@@ -24,3 +24,56 @@
24
24
  url('/avo-assets/fonts/nunito-v16-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
25
25
  url('/avo-assets/fonts/nunito-v16-latin-700.svg#Nunito') format('svg'); /* Legacy iOS */
26
26
  }
27
+
28
+ /* inter-regular - latin */
29
+ @font-face {
30
+ font-family: 'Inter';
31
+ font-style: normal;
32
+ font-weight: 400;
33
+ src: url('/avo-assets/fonts/inter-v7-latin-regular.eot'); /* IE9 Compat Modes */
34
+ src: local(''),
35
+ url('/avo-assets/fonts/inter-v7-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
36
+ url('/avo-assets/fonts/inter-v7-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
37
+ url('/avo-assets/fonts/inter-v7-latin-regular.woff') format('woff'), /* Modern Browsers */
38
+ url('/avo-assets/fonts/inter-v7-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
39
+ url('/avo-assets/fonts/inter-v7-latin-regular.svg#Inter') format('svg'); /* Legacy iOS */
40
+ }
41
+ /* inter-500 - latin */
42
+ @font-face {
43
+ font-family: 'Inter';
44
+ font-style: normal;
45
+ font-weight: 500;
46
+ src: url('/avo-assets/fonts/inter-v7-latin-500.eot'); /* IE9 Compat Modes */
47
+ src: local(''),
48
+ url('/avo-assets/fonts/inter-v7-latin-500.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
49
+ url('/avo-assets/fonts/inter-v7-latin-500.woff2') format('woff2'), /* Super Modern Browsers */
50
+ url('/avo-assets/fonts/inter-v7-latin-500.woff') format('woff'), /* Modern Browsers */
51
+ url('/avo-assets/fonts/inter-v7-latin-500.ttf') format('truetype'), /* Safari, Android, iOS */
52
+ url('/avo-assets/fonts/inter-v7-latin-500.svg#Inter') format('svg'); /* Legacy iOS */
53
+ }
54
+ /* inter-600 - latin */
55
+ @font-face {
56
+ font-family: 'Inter';
57
+ font-style: normal;
58
+ font-weight: 600;
59
+ src: url('/avo-assets/fonts/inter-v7-latin-600.eot'); /* IE9 Compat Modes */
60
+ src: local(''),
61
+ url('/avo-assets/fonts/inter-v7-latin-600.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
62
+ url('/avo-assets/fonts/inter-v7-latin-600.woff2') format('woff2'), /* Super Modern Browsers */
63
+ url('/avo-assets/fonts/inter-v7-latin-600.woff') format('woff'), /* Modern Browsers */
64
+ url('/avo-assets/fonts/inter-v7-latin-600.ttf') format('truetype'), /* Safari, Android, iOS */
65
+ url('/avo-assets/fonts/inter-v7-latin-600.svg#Inter') format('svg'); /* Legacy iOS */
66
+ }
67
+ /* inter-700 - latin */
68
+ @font-face {
69
+ font-family: 'Inter';
70
+ font-style: normal;
71
+ font-weight: 700;
72
+ src: url('/avo-assets/fonts/inter-v7-latin-700.eot'); /* IE9 Compat Modes */
73
+ src: local(''),
74
+ url('/avo-assets/fonts/inter-v7-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
75
+ url('/avo-assets/fonts/inter-v7-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
76
+ url('/avo-assets/fonts/inter-v7-latin-700.woff') format('woff'), /* Modern Browsers */
77
+ url('/avo-assets/fonts/inter-v7-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
78
+ url('/avo-assets/fonts/inter-v7-latin-700.svg#Inter') format('svg'); /* Legacy iOS */
79
+ }
@@ -21,18 +21,18 @@
21
21
  .pagy-nav-js .page a,
22
22
  .pagy-combo-nav-js .page a,
23
23
  .pagy-combo-nav-js .pagy-combo-input a {
24
- @apply rounded-lg px-3 py-1 text-sm text-gray-500 font-semibold bg-gray-200 shadow-md;
24
+ @apply rounded-lg px-3 py-1 text-sm text-gray-500 font-semibold bg-white shadow-md;
25
25
 
26
26
  &:focus{
27
27
  @apply border-blue-300;
28
28
  }
29
29
 
30
30
  &:hover{
31
- @apply bg-gray-300;
31
+ @apply bg-gray-200;
32
32
  }
33
33
 
34
34
  &:active{
35
- @apply bg-gray-400 text-white;
35
+ @apply bg-gray-200;
36
36
  }
37
37
  }
38
38
 
@@ -42,26 +42,26 @@
42
42
  .pagy-nav .page.next.disabled,
43
43
  .pagy-nav-js .page.next.disabled,
44
44
  .pagy-combo-nav-js .page.next.disabled {
45
- @apply text-gray-400 cursor-default;
45
+ @apply text-gray-300 cursor-default;
46
46
 
47
47
  &:hover {
48
- @apply text-gray-400 bg-gray-200;
48
+ @apply text-gray-300 bg-white;
49
49
  }
50
50
 
51
51
  &:active {
52
- @apply text-gray-400 bg-gray-200;
52
+ @apply text-gray-300 bg-white;
53
53
  }
54
54
  }
55
55
 
56
56
  .pagy-nav .page.active,
57
57
  .pagy-nav-js .page.active {
58
- @apply text-white cursor-default bg-gray-400;
58
+ @apply text-white cursor-default bg-gray-500;
59
59
 
60
60
  &:hover {
61
- @apply text-white bg-gray-400;
61
+ @apply text-white bg-gray-500;
62
62
  }
63
63
 
64
64
  &:active {
65
- @apply bg-gray-400 text-white;
65
+ @apply bg-gray-500 text-white;
66
66
  }
67
67
  }
@@ -1,13 +1,14 @@
1
1
  :root {
2
2
  --aa-primary-color: --tw-ring-color;
3
3
  --aa-selected-color: --tw-ring-color;
4
- --aa-primary-color-rgb: 5, 150, 105;
4
+ --aa-primary-color-rgb: 117, 125, 138;
5
5
  }
6
6
 
7
7
  .global-search {
8
8
  .aa-DetachedSearchButton:focus,
9
9
  .aa-DetachedSearchButton {
10
10
  border: none !important;
11
+ @apply text-gray-500;
11
12
  }
12
13
  }
13
14
 
@@ -17,7 +18,7 @@
17
18
  }
18
19
 
19
20
  .aa-DetachedSearchButton {
20
- @apply rounded-full border-gray-300;
21
+ @apply rounded border-gray-300 ;
21
22
  }
22
23
  }
23
24
 
@@ -0,0 +1,6 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M8.4 2H2V8.4H8.4V2Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M18 2H11.6V8.4H18V2Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M8.4 11.6001H2V18.0001H8.4V11.6001Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M18 11.6001H11.6V18.0001H18V11.6001Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
6
+ </svg>
@@ -0,0 +1,13 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_47_764)">
3
+ <path d="M1.52386 18V11.1428H6.8572" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M19.0476 18H0" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M6.85718 18.0001V6.57153H12.1905" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
6
+ <path d="M17.5238 2H12.1905V18H17.5238V2Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
7
+ </g>
8
+ <defs>
9
+ <clipPath id="clip0_47_764">
10
+ <rect width="20" height="20" fill="white"/>
11
+ </clipPath>
12
+ </defs>
13
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg width="18" height="4" viewBox="0 0 18 4" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="2" cy="2" r="2" fill="#757D8A"/>
3
+ <circle cx="9" cy="2" r="2" fill="#757D8A"/>
4
+ <circle cx="16" cy="2" r="2" fill="#757D8A"/>
5
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M17.5702 5.2533C18.0073 6.27384 18.1143 7.4055 17.8762 8.48986C17.6381 9.57423 17.0669 10.557 16.2425 11.3006C15.4182 12.0442 14.3819 12.5114 13.2788 12.6367C12.1757 12.7621 11.061 12.5394 10.0908 11.9997V11.9997L5.41832 17.4135C5.04279 17.789 4.53348 18 4.0024 18C3.47133 18 2.96201 17.789 2.58649 17.4135C2.21097 17.038 2 16.5287 2 15.9976C2 15.4665 2.21097 14.9572 2.58649 14.5817L8.00028 9.90916C7.46064 8.93895 7.23791 7.82427 7.36328 6.72119C7.48864 5.6181 7.95582 4.58183 8.69941 3.75746C9.443 2.93308 10.4258 2.36188 11.5101 2.12381C12.5945 1.88574 13.7262 1.99273 14.7467 2.4298L11.2486 5.91962L11.7233 8.2767L14.0804 8.75144L17.5702 5.2533Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1 @@
1
+ <%= output %>
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::ButtonComponent < ViewComponent::Base
4
+ def initialize(size: :md, type: :secondary, icon: nil, is_link: false, href: nil, **args)
5
+ @size = size
6
+ @type = type
7
+ @icon = icon
8
+ @href = href
9
+ @is_link = is_link
10
+ @args = args || {}
11
+ end
12
+
13
+ def args
14
+ if @args[:spinner]
15
+ @args[:"data-controller"] = "loading-button"
16
+ end
17
+
18
+ @args
19
+ end
20
+
21
+ def label
22
+ 'lalb'
23
+ end
24
+
25
+ def is_link?
26
+ @is_link
27
+ end
28
+
29
+ def the_content
30
+ result = ""
31
+
32
+ result += @icon if @icon.present?
33
+ result += content if content.present?
34
+
35
+ result.html_safe
36
+ end
37
+
38
+ def output
39
+ if is_link?
40
+ output_link
41
+ else
42
+ output_button
43
+ end
44
+ end
45
+
46
+ def output_link
47
+ if content.empty?
48
+ link_to label, @href, **args
49
+ else
50
+ link_to @href, **args do
51
+ content
52
+ end
53
+ end
54
+ end
55
+
56
+ def output_button
57
+ if content.empty?
58
+ button_tag label, **args
59
+ else
60
+ button_tag **args do
61
+ the_content
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+ # args[:class] = button_classes(args[:class], color: args[:color], variant: args[:variant], size: args[:size])
74
+ # if args[:spinner]
75
+ # args[:"data-controller"] = "loading-button"
76
+ # end
77
+
78
+ # locals = {
79
+ # label: label,
80
+ # args: args
81
+ # }
82
+
83
+ # if block
84
+ # render layout: "avo/partials/a_button", locals: locals do
85
+ # capture(&block)
86
+ # end
87
+ # else
88
+ # render partial: "avo/partials/a_button", locals: locals
89
+ # end
90
+
91
+
92
+
93
+
94
+ # <% if is_link? %>
95
+ # <% if content.empty? %>
96
+ # <%= link_to label, @href, **args %>
97
+ # <% else %>
98
+ # <%= link_to @href, **args do %>
99
+ # <% end %>
100
+ # <% end %>
101
+ # <% else %>
102
+ # <% if content.empty? %>
103
+ # <%= button_tag label, **args %>
104
+ # <% else %>
105
+ # <%= button_tag **args do %>
106
+ # <%= the_content %>
107
+ # <% end %>
108
+ # <% end %>
109
+ # <% end %>
110
+
111
+ end
@@ -8,15 +8,22 @@
8
8
  <div class="relative w-full" autocomplete="off">
9
9
  <%= @form.text_field @field.foreign_key,
10
10
  value: field_label,
11
- class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)), 'data-search-target': 'button clearValue',
11
+ class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
12
12
  placeholder: @field.placeholder,
13
+ 'data-search-target': 'button clearValue',
14
+ # This instructs the search_controller if it should enable/disabled this field when the user switches polymorphic associations
15
+ # It should not enable the field if the record is being created through an association
16
+ 'data-should-be-disabled': @disabled,
13
17
  disabled: true %>
14
- <div class="absolute top-1/2 left-auto right-3 mr-px -mt-2 cursor-pointer hidden text-gray-500"
15
- data-tippy="tooltip"
16
- data-search-target="clearButton"
17
- title="<%= I18n.translate 'avo.clear_value' %>"
18
- data-action="click->search#clearValue"
19
- ><%= helpers.svg 'x', class: 'h-4' %></div>
18
+ <% unless @disabled %>
19
+ <div class="absolute top-1/2 left-auto right-3 mr-px -mt-2 cursor-pointer hidden text-gray-500"
20
+ data-tippy="tooltip"
21
+ data-search-target="clearButton"
22
+ title="<%= I18n.translate 'avo.clear_value' %>"
23
+ data-action="click->search#clearValue"
24
+ ><%= helpers.svg 'x', class: 'h-4' %>
25
+ </div>
26
+ <% end %>
20
27
  </div>
21
28
  <%= @form.hidden_field @foreign_key, value: field_value, 'data-search-target': 'hiddenId clearValue' %>
22
29
  </div>
@@ -1,17 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Fields::BelongsToField::AutocompleteComponent < ViewComponent::Base
4
- def initialize(form:, field:, type: nil, model_key:, foreign_key:)
4
+ def initialize(form:, field:, model_key:, foreign_key:, disabled:, type: nil, resource: nil, polymorphic_record: nil)
5
5
  @form = form
6
6
  @field = field
7
7
  @type = type
8
8
  @model_key = model_key
9
9
  @foreign_key = foreign_key
10
+ @resource = resource
11
+ @disabled = disabled
12
+ @polymorphic_record = polymorphic_record
10
13
  end
11
14
 
12
15
  def field_label
13
16
  if searchable?
14
- @field.value&.class == @type ? @field.field_label : nil
17
+ # New records won't have the value (instantiated model) present but the polymorphic_type and polymorphic_id prefilled
18
+ if new_record? && has_polymorphic_association?
19
+ @polymorphic_record.send(polymorphic_fields[:label])
20
+ else
21
+ @field.value&.class == @type ? @field.field_label : nil
22
+ end
15
23
  else
16
24
  @field.field_label
17
25
  end
@@ -19,7 +27,12 @@ class Avo::Fields::BelongsToField::AutocompleteComponent < ViewComponent::Base
19
27
 
20
28
  def field_value
21
29
  if searchable?
22
- @field.value&.class == @type ? @field.field_value : nil
30
+ # New records won't have the value (instantiated model) present but the polymorphic_type and polymorphic_id prefilled
31
+ if new_record? && has_polymorphic_association?
32
+ @polymorphic_record.send(polymorphic_fields[:id])
33
+ else
34
+ @field.value&.class == @type ? @field.field_value : nil
35
+ end
23
36
  else
24
37
  @field.field_value
25
38
  end
@@ -30,4 +43,35 @@ class Avo::Fields::BelongsToField::AutocompleteComponent < ViewComponent::Base
30
43
  def searchable?
31
44
  @type.present?
32
45
  end
46
+
47
+ def new_record?
48
+ @resource.model.new_record?
49
+ end
50
+
51
+ def has_polymorphic_association?
52
+ polymorphic_class.present? && polymorphic_id.present?
53
+ end
54
+
55
+ # Get the polymorphic class
56
+ def polymorphic_class
57
+ @resource.model["#{@field.foreign_key}_type"]
58
+ end
59
+
60
+ # Get the polymorphic id
61
+ def polymorphic_id
62
+ @resource.model["#{@field.foreign_key}_id"]
63
+ end
64
+
65
+ # Get the resource for that polymorphic class
66
+ def polymorphic_resource
67
+ ::Avo::App.get_resource_by_model_name polymorphic_class
68
+ end
69
+
70
+ # Extract the needed fields to identify the record for polymorphic associations
71
+ def polymorphic_fields
72
+ {
73
+ id: polymorphic_resource.id,
74
+ label: polymorphic_resource.title
75
+ }
76
+ end
33
77
  end
@@ -1,5 +1,5 @@
1
1
  <%
2
- if @field.types.present? # It's a polymorphic association
2
+ if is_polymorphic?
3
3
 
4
4
  # Set the model keys so we can pass them over
5
5
  model_keys = @field.types.map do |type|
@@ -7,58 +7,73 @@
7
7
  [type.to_s, resource.model_key]
8
8
  end.to_h
9
9
  %>
10
- <div data-controller="belongs-to-field"
10
+ <div data-controller="belongs-to-field"
11
11
  data-searchable="<%= @field.searchable %>"
12
12
  data-association="<%= @field.id %>"
13
13
  data-association-class="<%= @field&.target_resource&.model_class || nil %>"
14
14
  >
15
- <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
16
- <%= @form.select "#{@field.foreign_key}_type", @field.types.map { |type| [type.to_s.underscore.humanize, type.to_s] },
17
- {
18
- value: @field.value,
19
- include_blank: @field.placeholder,
20
- },
21
- {
22
- class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
23
- disabled: disabled,
24
- 'data-belongs-to-field-target': "select",
25
- 'data-action': 'change->belongs-to-field#changeType'
26
- }
27
- %>
28
- <%
15
+ <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
16
+ <%= @form.select "#{@field.foreign_key}_type", @field.types.map { |type| [type.to_s.underscore.humanize, type.to_s] },
17
+ {
18
+ value: @field.value,
19
+ include_blank: @field.placeholder,
20
+ },
21
+ {
22
+ class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
23
+ disabled: disabled,
24
+ 'data-belongs-to-field-target': "select",
25
+ 'data-action': 'change->belongs-to-field#changeType'
26
+ }
27
+ %>
28
+ <%
29
29
  # If the select field is disabled, no value will be sent. It's how HTML works.
30
30
  # Thus the extra hidden field to actually send the related id to the server.
31
31
  if disabled %>
32
- <%= @form.hidden_field "#{@field.foreign_key}_type" %>
33
- <% end %>
32
+ <%= @form.hidden_field "#{@field.foreign_key}_type" %>
34
33
  <% end %>
35
- <% @field.types.each do |type| %>
36
- <div class="hidden"
34
+ <% end %>
35
+ <% @field.types.each do |type| %>
36
+ <div class="hidden"
37
37
  data-belongs-to-field-target="type"
38
38
  data-type="<%= type %>"
39
39
  >
40
- <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, label: type.to_s.underscore.humanize do %>
41
- <% if @field.searchable %>
42
- <%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form, field: @field, type: type, model_key: model_keys[type.to_s], foreign_key: "#{@field.foreign_key}_id" %>
43
- <% else %>
44
- <%= @form.select "#{@field.foreign_key}_id", options_for_select(@field.values_for_type(type), @field.value&.class == type ? @field.field_value : nil),
45
- {
46
- include_blank: @field.placeholder,
47
- },
48
- {
49
- class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
50
- disabled: disabled
51
- }
52
- %>
53
- <% end %>
40
+ <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, label: type.to_s.underscore.humanize do %>
41
+ <% if @field.searchable %>
42
+ <%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form,
43
+ field: @field,
44
+ type: type,
45
+ model_key: model_keys[type.to_s],
46
+ foreign_key: "#{@field.foreign_key}_id",
47
+ resource: @resource,
48
+ disabled: disabled,
49
+ polymorphic_record: polymorphic_record
50
+ %>
51
+ <% else %>
52
+ <%= @form.select "#{@field.foreign_key}_id", options_for_select(@field.values_for_type(type), @resource.present? && @resource.model.present? ? @resource.model["#{@field.foreign_key}_id"] : nil),
53
+ {
54
+ value: @resource.model["#{@field.foreign_key}_id"].to_s,
55
+ include_blank: @field.placeholder,
56
+ },
57
+ {
58
+ class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
59
+ disabled: disabled
60
+ }
61
+ %>
54
62
  <% end %>
55
- </div>
56
- <% end %>
57
- </div>
63
+ <% end %>
64
+ </div>
65
+ <% end %>
66
+ </div>
58
67
  <% else %>
59
68
  <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
60
69
  <% if @field.searchable %>
61
- <%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form, field: @field, model_key: @field.target_resource&.model_key, foreign_key: @field.foreign_key %>
70
+ <%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form,
71
+ field: @field,
72
+ model_key: @field.target_resource&.model_key,
73
+ foreign_key: @field.foreign_key,
74
+ resource: @resource,
75
+ disabled: disabled
76
+ %>
62
77
  <% else %>
63
78
  <%= @form.select @field.foreign_key, @field.options.map { |o| [o[:label], o[:value]] },
64
79
  {
@@ -69,6 +84,12 @@
69
84
  disabled: disabled
70
85
  }
71
86
  %>
87
+ <%
88
+ # If the select field is disabled, no value will be sent. It's how HTML works.
89
+ # Thus the extra hidden field to actually send the related id to the server.
90
+ if disabled %>
91
+ <%= @form.hidden_field @field.foreign_key %>
92
+ <% end %>
72
93
  <% end %>
73
94
  <% end %>
74
95
  <% end %>
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
4
+ def initialize(field: nil, resource: nil, index: 0, form: nil, displayed_in_modal: false)
5
+ super field: field, resource: resource, index: index, form: form, displayed_in_modal: displayed_in_modal
6
+
7
+ @polymorphic_record = nil
8
+ end
9
+
4
10
  def disabled
5
11
  return true if @field.readonly
6
12
  return true if @field.target_resource.present? && @field.target_resource.model_class.name == params[:via_resource_class]
@@ -8,4 +14,35 @@ class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
8
14
 
9
15
  false
10
16
  end
17
+
18
+ def is_polymorphic?
19
+ @field.types.present?
20
+ end
21
+
22
+ def has_polymorphic_association?
23
+ polymorphic_class.present? && polymorphic_id.present?
24
+ end
25
+
26
+ # Get the polymorphic class
27
+ def polymorphic_class
28
+ @resource.model["#{@field.foreign_key}_type"]
29
+ end
30
+
31
+ # Get the polymorphic id
32
+ def polymorphic_id
33
+ @resource.model["#{@field.foreign_key}_id"]
34
+ end
35
+
36
+ # Get the actual resource
37
+ def polymorphic_record
38
+ return unless has_polymorphic_association?
39
+
40
+ return unless is_polymorphic?
41
+
42
+ return @polymorphic_record if @polymorphic_record.present?
43
+
44
+ @polymorphic_record = polymorphic_class.safe_constantize.find polymorphic_id
45
+
46
+ @polymorphic_record
47
+ end
11
48
  end
@@ -1,3 +1,3 @@
1
- <%= index_field_wrapper field: @field, dash_if_blank: false do %>
1
+ <%= index_field_wrapper field: @field, dash_if_blank: false, center_content: true do %>
2
2
  <%= render Avo::Fields::Common::BooleanCheckComponent.new checked: @field.value %>
3
3
  <% end %>
@@ -1,7 +1,13 @@
1
- <td class="px-4 py-2 leading-tight whitespace-nowrap h-12 text-slate-800 <%= @classes %>" data-field-id="<%= @field.id %>" data-field-type="<%= @field.type %>">
1
+ <td class="px-4 py-3 leading-tight whitespace-nowrap h-full text-slate-800 <%= @classes %>" data-field-id="<%= @field.id %>" data-field-type="<%= @field.type %>">
2
2
  <% if @field.value.blank? && @dash_if_blank %>
3
3
 
4
4
  <% else %>
5
- <%= content %>
5
+ <% if @center_content %>
6
+ <div class="flex items-center justify-center">
7
+ <%= content %>
8
+ </div>
9
+ <% else %>
10
+ <%= content %>
11
+ <% end %>
6
12
  <% end %>
7
13
  </td>
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Index::FieldWrapperComponent < ViewComponent::Base
4
- def initialize(field: nil, dash_if_blank: true, **args)
4
+ def initialize(field: nil, dash_if_blank: true, center_content: false, **args)
5
5
  @field = field
6
6
  @dash_if_blank = dash_if_blank
7
+ @center_content = center_content
7
8
  @classes = args[:class].present? ? args[:class] : ""
8
9
  @args = args
9
10
  end
@@ -1,8 +1,8 @@
1
- <div class="w-full">
1
+ <div class="w-full ">
2
2
  <% if @resources.present?%>
3
- <table class="w-full px-4 overflow-hidden" data-resource-name='<%= @resource.model_key %>' data-controller='item-select-all'>
3
+ <table class="w-full px-4 overflow-hidden bg-white rounded shadow " data-resource-name='<%= @resource.model_key %>' data-controller='item-select-all'>
4
4
  <%= render partial: 'avo/partials/table_header', locals: {fields: @resource.get_fields(reflection: @reflection)} %>
5
- <tbody>
5
+ <tbody class="divide-y">
6
6
  <% @resources.each_with_index do |resource, index| %>
7
7
  <% cache_if Avo.configuration.cache_resources_on_index_view, resource.cache_hash(@parent_model) do %>
8
8
  <%= render Avo::Index::TableRowComponent.new(resource: resource, reflection: @reflection, parent_model: @parent_model) %>
@@ -1,5 +1,5 @@
1
1
  <tr
2
- class="hover:bg-sky-50 hover:shadow-row relative z-20 border-b"
2
+ class="bg-white hover:bg-sky-50 hover:shadow-row relative z-20 border-b"
3
3
  <%== item_selector_init @resource %>
4
4
  >
5
5
  <td class="w-10">
@@ -1,3 +1,3 @@
1
- <div class="flex items-center my-2 p-3 text-gray-500 font-bold text-sm -mb-1 leading-none">
2
- <div class="w-4"></div> <%= @label %>
1
+ <div class="flex items-center p-4 text-gray-500 text-sm uppercase font-semibold leading-none">
2
+ <%= icon %> <%= label %>
3
3
  </div>