avo 1.21.1.pre.1 → 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 (84) 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 +19 -4
  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 +1 -1
  36. data/app/helpers/avo/application_helper.rb +7 -5
  37. data/app/helpers/avo/resources_helper.rb +2 -2
  38. data/app/javascript/js/controllers/fields/date_field_controller.js +10 -2
  39. data/app/javascript/js/controllers/item_selector_controller.js +29 -19
  40. data/app/javascript/js/controllers/search_controller.js +8 -3
  41. data/app/views/avo/base/_actions.html.erb +4 -3
  42. data/app/views/avo/base/_filters.html.erb +3 -1
  43. data/app/views/avo/partials/_global_search.html.erb +2 -2
  44. data/app/views/avo/partials/_logo.html.erb +3 -1
  45. data/app/views/avo/partials/_navbar.html.erb +11 -0
  46. data/app/views/avo/partials/_paginator.html.erb +4 -3
  47. data/app/views/avo/partials/_resource_search.html.erb +1 -1
  48. data/app/views/avo/partials/_sidebar.html.erb +35 -0
  49. data/app/views/avo/partials/_table_header.html.erb +3 -3
  50. data/app/views/avo/partials/_turbo_frame_wrap.html.erb +7 -1
  51. data/app/views/avo/partials/_view_toggle_button.html.erb +22 -16
  52. data/app/views/layouts/avo/application.html.erb +3 -13
  53. data/db/factories.rb +7 -3
  54. data/lib/avo/app.rb +4 -3
  55. data/lib/avo/base_resource.rb +16 -14
  56. data/lib/avo/version.rb +1 -1
  57. data/public/avo-assets/avo.css +405 -224
  58. data/public/avo-assets/avo.js +16 -6
  59. data/public/avo-assets/avo.js.map +2 -2
  60. data/public/avo-assets/fonts/inter-v7-latin-500.eot +0 -0
  61. data/public/avo-assets/fonts/inter-v7-latin-500.svg +351 -0
  62. data/public/avo-assets/fonts/inter-v7-latin-500.ttf +0 -0
  63. data/public/avo-assets/fonts/inter-v7-latin-500.woff +0 -0
  64. data/public/avo-assets/fonts/inter-v7-latin-500.woff2 +0 -0
  65. data/public/avo-assets/fonts/inter-v7-latin-600.eot +0 -0
  66. data/public/avo-assets/fonts/inter-v7-latin-600.svg +351 -0
  67. data/public/avo-assets/fonts/inter-v7-latin-600.ttf +0 -0
  68. data/public/avo-assets/fonts/inter-v7-latin-600.woff +0 -0
  69. data/public/avo-assets/fonts/inter-v7-latin-600.woff2 +0 -0
  70. data/public/avo-assets/fonts/inter-v7-latin-700.eot +0 -0
  71. data/public/avo-assets/fonts/inter-v7-latin-700.svg +352 -0
  72. data/public/avo-assets/fonts/inter-v7-latin-700.ttf +0 -0
  73. data/public/avo-assets/fonts/inter-v7-latin-700.woff +0 -0
  74. data/public/avo-assets/fonts/inter-v7-latin-700.woff2 +0 -0
  75. data/public/avo-assets/fonts/inter-v7-latin-regular.eot +0 -0
  76. data/public/avo-assets/fonts/inter-v7-latin-regular.svg +351 -0
  77. data/public/avo-assets/fonts/inter-v7-latin-regular.ttf +0 -0
  78. data/public/avo-assets/fonts/inter-v7-latin-regular.woff +0 -0
  79. data/public/avo-assets/fonts/inter-v7-latin-regular.woff2 +0 -0
  80. data/public/avo-assets/logo.png +0 -0
  81. metadata +32 -5
  82. data/app/assets/images/avo/logo.png +0 -0
  83. data/app/views/avo/partials/_profile_dropdown.html.erb +0 -25
  84. data/app/views/avo/sidebar/_sidebar.html.erb +0 -33
@@ -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-4', data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
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 pt-2 pb-2 min-h-16"
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="bg-white rounded-xl shadow-xl mt-8 py-6">
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
@@ -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-bold leading-none fill-current whitespace-nowrap transition duration-100 rounded-lg shadow-xl transform transition duration-100 active:translate-x-px active:translate-y-px cursor-pointer disabled:cursor-not-allowed #{extra_classes}"
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}-600 text-#{color}-600 hover:text-#{color}-700 disabled:border-gray-300 disabled:text-gray-600"
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
- " p-3"
91
+ " py-1 px-4"
92
92
  when :md
93
- " p-4"
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
- currentValue = new Date(this.inputTarget.value)
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(`[data-actions-dropdown-button="${this.resourceName}"]`)
36
- this.stateHolderElement = document.querySelector(`[data-selected-resources-name="${this.resourceName}"]`)
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((item) => item.toString() !== this.resourceId)
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
- (document.querySelectorAll('.js-actions-dropdown a[data-actions-picker-target="resourceAction"]'))
63
- .forEach((link) => {
64
- link.classList.add('text-gray-700')
65
- link.classList.remove('text-gray-500')
66
- link.setAttribute('data-href', link.getAttribute('href'))
67
- link.dataset.disabled = false
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
- (document.querySelectorAll('.js-actions-dropdown a[data-actions-picker-target="resourceAction"]'))
73
- .forEach((link) => {
74
- link.classList.remove('text-gray-700')
75
- link.classList.add('text-gray-500')
76
- link.setAttribute('href', link.getAttribute('data-href'))
77
- link.dataset.disabled = true
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.clearButtonTarget.classList.remove('hidden')
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
- this.buttonTarget.removeAttribute('disabled')
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: 'sky',
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-left', class: 'h-4 mr-1 transform -rotate-90' %> <%= t 'avo.actions' %>
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-bold text-gray-700 hover:text-white hover:bg-blue-500 #{disabled ? 'text-gray-500' : 'text-gray-700'}",
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: 'slate',
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' %>"}'
@@ -1 +1,3 @@
1
- <%= image_tag '/avo-assets/logo.png', class: 'h-full', title: 'Avo' %>
1
+ <%= link_to root_path, class: 'logo-placeholder h-20 bg-white p-4 flex justify-start' do %>
2
+ <%= image_tag '/avo-assets/logo.png', class: 'h-full', title: 'Avo' %>
3
+ <% end %>
@@ -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="bg-white px-4 flex items-center justify-between aborder-t aborder-slate-200 sm:px-6 rounded-xl">
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-full cursor-pointer" data-search-target="button"></div>
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-slate-100 border-t border-b border-gray-300 pb-1">
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-slate-600 tracking-tight leading-tight text-xs font-semibold"
31
+ classes = "text-gray-500 tracking-tight leading-tight text-sm font-semibold"
32
32
  %>
33
- <th class="text-left uppercase px-3 py-2 whitespace-nowrap">
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
- <%= yield %>
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 %>
@@ -1,17 +1,23 @@
1
- <%
2
- if available_view_types.count > 1
3
- case view_type.to_sym
4
- when :table
5
- new_view_type = 'grid'
6
- new_icon = 'view-grid'
7
- translation_key = 'avo.grid_view'
8
- when :grid
9
- new_view_type = 'table'
10
- new_icon = 'view-list'
11
- translation_key = 'avo.table_view'
12
- end
13
- %>
14
- <%= a_link params.permit!.merge(view_type: new_view_type), color: :blue, 'data-turbo-frame': turbo_frame, title: t('avo.switch_to_view', view_type: new_view_type), 'data-tippy': 'tooltip' do %>
15
- <%= svg new_icon %> <%= t translation_key, item: @resource.model_class.model_name.human.downcase %>
16
- <% end %>
1
+ <% if available_view_types.count > 1 %>
2
+ <%
3
+ info = {
4
+ table: {
5
+ new_view_type: 'grid',
6
+ new_icon: 'view-grid',
7
+ translation_key: 'avo.grid_view',
8
+ },
9
+ grid: {
10
+ new_view_type: 'table',
11
+ new_icon: 'view-list',
12
+ translation_key: 'avo.table_view',
13
+ },
14
+ }
15
+ %>
16
+ <div class="flex">
17
+ <% available_view_types.each do |type| %>
18
+ <%= a_link params.permit!.merge(view_type: type), color: :secondary, variant: :outlined, class: view_type.to_s == type.to_s ? 'bg-gray-200' : '', 'data-turbo-frame': turbo_frame, title: t('avo.switch_to_view', view_type: type), 'data-tippy': 'tooltip' do %>
19
+ <%= svg info[type][:new_icon] %>
20
+ <% end %>
21
+ <% end %>
22
+ </div>
17
23
  <% end %>
@@ -17,24 +17,14 @@
17
17
  <%= stylesheet_link_tag "avo", "data-turbo-track": "reload", defer: true %>
18
18
  <% end %>
19
19
  </head>
20
- <body class="bg-slate-200 os-mac">
20
+ <body class="bg-gray-100 os-mac">
21
21
 
22
22
  <div id="app" class="flex min-h-screen flex-row h-full">
23
23
  <div class="flex flex-1 w-full">
24
- <%= render partial: 'avo/sidebar/sidebar' %>
24
+ <%= render partial: 'avo/partials/sidebar' %>
25
25
 
26
26
  <div class="flex-1 flex flex-col h-full overflow-auto">
27
- <div class="relative bg-white p-2 shadow-md h-16 w-full flex flex-shrink-0 items-center z-50 px-6" v-if="layout !== 'blank'">
28
- <div>
29
- <%= render partial: "avo/partials/header" %>
30
- </div>
31
- <div class="flex-1 flex ml-4 pl-4">
32
- <%= render partial: "avo/partials/global_search" if ::Avo::App.license.has_with_trial(:global_search) && ::Avo.configuration.feature_enabled?(:global_search) %>
33
- </div>
34
- <div class="align-end">
35
- <%= render partial: "avo/partials/profile_dropdown" %>
36
- </div>
37
- </div>
27
+ <%= render partial: "avo/partials/navbar" %>
38
28
 
39
29
  <div class="content p-8 flex-1 flex flex-col justify-between items-stretch <%= @container_classes %>">
40
30
  <%= render partial: "avo/partials/custom_tools_alert" if @custom_tools_alert_visible %>
data/db/factories.rb CHANGED
@@ -58,11 +58,15 @@ FactoryBot.define do
58
58
  type { "Spouse" }
59
59
  end
60
60
 
61
+ factory :fish do
62
+ name { %w[Tilapia Salmon Trout Catfish Pangasius Carp].sample }
63
+ end
64
+
61
65
  factory :course do
62
- name { Faker::Company.name }
66
+ name { Faker::Educator.unique.course_name }
63
67
  end
64
68
 
65
- factory :link do
66
- name { Faker::Internet.url }
69
+ factory :course_link, class: "Course::Link" do
70
+ link { Faker::Internet.url }
67
71
  end
68
72
  end
data/lib/avo/app.rb CHANGED
@@ -133,9 +133,10 @@ module Avo
133
133
  end
134
134
 
135
135
  def resources_navigation(user = nil)
136
- get_available_resources(user).select do |resource|
137
- resource.model_class.present?
138
- end
136
+ get_available_resources(user)
137
+ .select do |resource|
138
+ resource.model_class.present?
139
+ end
139
140
  .select do |resource|
140
141
  resource.visible_on_sidebar
141
142
  end