avo 2.8.0 → 2.9.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.

Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +2 -2
  3. data/README.md +11 -0
  4. data/app/assets/stylesheets/avo.css +4 -4
  5. data/app/assets/stylesheets/css/{components → fields}/code.css +0 -0
  6. data/app/assets/stylesheets/css/{components → fields}/progress.css +0 -0
  7. data/app/assets/stylesheets/css/{components → fields}/status.css +0 -0
  8. data/app/assets/stylesheets/css/fields/trix.css +17 -0
  9. data/app/components/avo/actions_component.html.erb +5 -13
  10. data/app/components/avo/actions_component.rb +39 -1
  11. data/app/components/avo/common_field_wrapper_component.html.erb +1 -1
  12. data/app/components/avo/fields/belongs_to_field/edit_component.rb +1 -1
  13. data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -0
  14. data/app/components/avo/fields/trix_field/show_component.html.erb +1 -1
  15. data/app/components/avo/sidebar/item_switcher_component.html.erb +2 -2
  16. data/app/components/avo/views/resource_edit_component.html.erb +6 -4
  17. data/app/components/avo/views/resource_edit_component.rb +31 -3
  18. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  19. data/app/components/avo/views/resource_show_component.html.erb +2 -2
  20. data/app/controllers/avo/base_controller.rb +11 -6
  21. data/app/helpers/avo/url_helpers.rb +8 -9
  22. data/app/javascript/js/controllers.js +0 -2
  23. data/app/views/avo/base/edit.html.erb +2 -1
  24. data/app/views/avo/base/new.html.erb +1 -1
  25. data/app/views/avo/partials/_custom_tools_alert.html.erb +21 -7
  26. data/app/views/avo/partials/_table_header.html.erb +9 -1
  27. data/bin/test +1 -0
  28. data/lib/avo/app.rb +18 -1
  29. data/lib/avo/base_action.rb +7 -2
  30. data/lib/avo/base_resource.rb +6 -1
  31. data/lib/avo/concerns/fetches_things.rb +19 -12
  32. data/lib/avo/concerns/has_model.rb +11 -0
  33. data/lib/avo/dynamic_router.rb +1 -1
  34. data/lib/avo/engine.rb +1 -3
  35. data/lib/avo/fields/base_field.rb +1 -0
  36. data/lib/avo/fields/concerns/is_required.rb +17 -0
  37. data/lib/avo/hosts/view_record_host.rb +7 -0
  38. data/lib/avo/menu/base_item.rb +4 -0
  39. data/lib/avo/menu/dashboard.rb +5 -0
  40. data/lib/avo/menu/resource.rb +5 -0
  41. data/lib/avo/version.rb +1 -1
  42. data/lib/avo.rb +1 -0
  43. data/lib/generators/avo/install_generator.rb +1 -4
  44. data/public/avo-assets/avo.css +467 -1069
  45. data/public/avo-assets/avo.js +70 -70
  46. data/public/avo-assets/avo.js.map +3 -3
  47. metadata +9 -8
  48. data/app/components/avo/views/resource_new_component.html.erb +0 -60
  49. data/app/components/avo/views/resource_new_component.rb +0 -39
  50. data/app/javascript/js/controllers/custom/course_resource_controller.js +0 -102
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '083a689ccb76278fae699dd89eb21f5cf1dc86a306e2d25c1015a9297eb6f171'
4
- data.tar.gz: ba27d9448b88241853e50a260764f70e232b566352b959c1b8265c5d547d3fea
3
+ metadata.gz: 7a1d99b5007b69c25b8eb7b426fd8881d9baacc700d4f14c0ed8dfeac81c7004
4
+ data.tar.gz: bb18e3d2d52f500c3d73ae8f29bf0165ea560905c848388bfeef127850cc5e79
5
5
  SHA512:
6
- metadata.gz: 8cd68a6ab28242fcd7db9706e58e0ce49575acd055bdddf4be7231092301b46a218ac492b6634b041b8e181ddfc3871e968b280188025d1d05ba715e5c8d94bb
7
- data.tar.gz: 5aff67676e320a05f76c5403124470f6c2ab027dc16853c74eeb991a26d22f8259014e26aae581e9ac6b39f7ed8837379359ae525560632039728148e7a14857
6
+ metadata.gz: 516b6413536158cd998e3addbb3b327f3fbdbdec504a9f1502aaadc5f52925490966c22a5d67f161933e08e5c5c0f4c0a0bf1e3a207216297dd65e50966569b9
7
+ data.tar.gz: 5474fa61b0268b691865abc602271828c49500612bbe22617a2bf117fc1c2cc05ab0568ba78ea917ff021bf63bfd63ad9d728868d723e9684dc9c7c38e636615
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (2.8.0)
4
+ avo (2.9.0)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
@@ -136,7 +136,7 @@ GEM
136
136
  rack-test (>= 0.6.3)
137
137
  regexp_parser (>= 1.5, < 3.0)
138
138
  xpath (~> 3.2)
139
- chartkick (4.1.3)
139
+ chartkick (4.2.0)
140
140
  childprocess (4.1.0)
141
141
  concurrent-ruby (1.1.9)
142
142
  countries (4.2.1)
data/README.md CHANGED
@@ -76,6 +76,17 @@ Please read the [UPGRADE_GUIDE.MD](https://docs.avohq.io/2.0/upgrade.html)
76
76
  </a>
77
77
  <!-- https://contrib.rocks -->
78
78
 
79
+ # 🥇 Business & Agency Sponsors
80
+
81
+ <a href="https://equipetechnique.com/?utm_source=github&utm_medium=link&utm_campaign=avo_code_without_borders" target="_blank">
82
+ <picture>
83
+ <source media="(prefers-color-scheme: dark)" srcset="https://avohq.io/img/sponsors/ET-dark.jpeg">
84
+ <img alt="Equipe Technique Busines Sponsor" src="https://avohq.io/img/sponsors/ET-light.jpeg" width="240px">
85
+ </picture>
86
+ </a>
87
+
88
+ [Become a sponsor ](https://github.com/sponsors/adrianthedev)
89
+
79
90
 
80
91
  ![Alt](https://repobeats.axiom.co/api/embed/1481a6a259064f02a7936470d12a50802a9c98a4.svg "Repobeats analytics image")
81
92
 
@@ -1,7 +1,6 @@
1
1
  @import './../../../node_modules/simplemde/dist/simplemde.min.css';
2
2
  @import './../../../node_modules/tippy.js/dist/tippy.css';
3
3
  @import './../../../node_modules/tippy.js/themes/light.css';
4
- @import './../../../node_modules/trix/dist/trix.css';
5
4
  @import './../../../node_modules/flatpickr/dist/flatpickr.css';
6
5
  @import './../../../node_modules/@algolia/autocomplete-theme-classic/dist/theme.css';
7
6
  @import './../../../node_modules/@yaireo/tagify/dist/tagify.css';
@@ -20,9 +19,10 @@
20
19
  @import './css/spinner.css';
21
20
  @import './css/tags.css';
22
21
 
23
- @import './css/components/status.css';
24
- @import './css/components/code.css';
25
- @import './css/components/progress.css';
22
+ @import './css/fields/status.css';
23
+ @import './css/fields/code.css';
24
+ @import './css/fields/progress.css';
25
+ @import './css/fields/trix.css';
26
26
 
27
27
  @import 'tailwindcss/components';
28
28
 
@@ -0,0 +1,17 @@
1
+ @import './../../../../../node_modules/trix/dist/trix.css';
2
+
3
+ .trix-content h1 {
4
+ @apply text-xl font-bold mb-2;
5
+ }
6
+
7
+ .trix-content ul {
8
+ @apply list-disc;
9
+ }
10
+
11
+ .trix-content pre {
12
+ @apply text-base;
13
+ }
14
+
15
+ .trix-content ol {
16
+ @apply list-decimal;
17
+ }
@@ -4,6 +4,7 @@
4
4
  data-actions-picker-disabled-class="cursor-wait text-gray-500"
5
5
  >
6
6
  <%= a_button style: :outline,
7
+ type: :button,
7
8
  color: :primary,
8
9
  class: "focus:outline-none",
9
10
  icon: 'arrow-circle-right',
@@ -18,24 +19,15 @@
18
19
  >
19
20
  <div class="w-full space divide-y">
20
21
  <% @actions.each_with_index do |action, index| %>
21
- <%
22
- path = action_name == 'show' ?
23
- "#{@resource.record_path}/actions/#{action.param_id}" :
24
- "#{@resource.records_path}/actions/#{action.param_id}"
25
- if action_name == 'show' || action.standalone
26
- disabled = false
27
- else
28
- disabled = true
29
- end
30
- %>
31
- <%= link_to path,
22
+ <%= link_to action_path(action.param_id),
32
23
  data: {
33
24
  'turbo-frame': 'actions_show',
34
25
  'action': 'click->actions-picker#visitAction',
35
26
  'actions-picker-target': action.standalone ? 'standaloneAction' : 'resourceAction',
36
- 'disabled': disabled,
27
+ 'disabled': is_disabled?(action),
37
28
  },
38
- class: "flex items-center px-4 py-3 w-full font-semibold text-sm #{disabled ? 'text-gray-500' : 'text-black hover:bg-blue-500 hover:text-white'}" do %>
29
+ title: action.action_name,
30
+ class: "flex items-center px-4 py-3 w-full font-semibold text-sm #{is_disabled?(action) ? 'text-gray-500' : 'text-black hover:bg-blue-500 hover:text-white'}" do %>
39
31
  <%= svg 'play', class: 'h-5 mr-1 inline' %> <%= action.action_name %>
40
32
  <% end %>
41
33
  <% end %>
@@ -3,12 +3,50 @@
3
3
  class Avo::ActionsComponent < ViewComponent::Base
4
4
  include Avo::ApplicationHelper
5
5
 
6
- def initialize(actions: [], resource: nil)
6
+ def initialize(actions: [], resource: nil, view: nil)
7
7
  @actions = actions
8
8
  @resource = resource
9
+ @view = view
9
10
  end
10
11
 
11
12
  def render?
12
13
  @actions.present?
13
14
  end
15
+
16
+ # When running an action for one record we should do it on a special path.
17
+ # We do that so we get the `model` param inside the action so we can prefill fields.
18
+ def action_path(id)
19
+ return many_records_path(id) unless @resource.has_model_id?
20
+
21
+ if on_record_page?
22
+ single_record_path id
23
+ else
24
+ many_records_path id
25
+ end
26
+ end
27
+
28
+ # How should the action be displayed by default
29
+ def is_disabled?(action)
30
+ return false if action.standalone
31
+
32
+ on_index_page?
33
+ end
34
+
35
+ private
36
+
37
+ def on_record_page?
38
+ @view.in?([:show, :edit, :new])
39
+ end
40
+
41
+ def on_index_page?
42
+ !on_record_page?
43
+ end
44
+
45
+ def single_record_path(id)
46
+ "#{@resource.record_path}/actions/#{id}"
47
+ end
48
+
49
+ def many_records_path(id)
50
+ "#{@resource.records_path}/actions/#{id}"
51
+ end
14
52
  end
@@ -13,7 +13,7 @@
13
13
  <% else %>
14
14
  <%= @field.name %>
15
15
  <% end %>
16
- <% if @field.required %> <span class="text-red-600 ml-1">*</span> <% end %>
16
+ <% if @field.is_required? %> <span class="text-red-600 ml-1">*</span> <% end %>
17
17
  </div>
18
18
  </div>
19
19
  <div class="flex-1 flex flex-row md:min-h-inherit py-2 <% unless @displayed_in_modal %> px-6 <% end %>">
@@ -50,6 +50,6 @@ class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
50
50
  end
51
51
 
52
52
  def field_html_action
53
- @field.get_html(:data, view: @resource.view, element: :input)
53
+ @field.get_html(:data, view: @resource.view, element: :input).fetch(:action, nil)
54
54
  end
55
55
  end
@@ -12,6 +12,7 @@
12
12
  class="relative block overflow-x-auto max-w-full"
13
13
  >
14
14
  <%= content_tag 'trix-editor',
15
+ class: 'trix-content',
15
16
  data: {
16
17
  "trix-field-target": "editor",
17
18
  **@field.get_html(:data, view: view, element: :input)
@@ -1,6 +1,6 @@
1
1
  <%= show_field_wrapper field: @field, resource: @resource, index: @index do %>
2
2
  <%
3
- content_classes = 'prose prose-sm'
3
+ content_classes = 'trix-content py-2'
4
4
  content_classes << ' hidden' unless @field.always_show
5
5
  %>
6
6
  <div data-controller="hidden-input">
@@ -2,10 +2,10 @@
2
2
  <%= render Avo::Sidebar::LinkComponent.new label: item.name, path: item.path, target: item.target %>
3
3
  <% end %>
4
4
  <% if item.is_a? Avo::Menu::Resource %>
5
- <%= render Avo::Sidebar::LinkComponent.new label: resource.navigation_label, path: helpers.resources_path(resource: resource) %>
5
+ <%= render Avo::Sidebar::LinkComponent.new label: item.navigation_label, path: helpers.resources_path(resource: resource) %>
6
6
  <% end %>
7
7
  <% if item.is_a? Avo::Menu::Dashboard %>
8
- <%= render Avo::Sidebar::LinkComponent.new label: dashboard.navigation_label, path: dashboard.navigation_path %>
8
+ <%= render Avo::Sidebar::LinkComponent.new label: item.navigation_label, path: dashboard.navigation_path %>
9
9
  <% end %>
10
10
  <% if item.is_a? Avo::Menu::Section %>
11
11
  <%= render Avo::Sidebar::SectionComponent.new item: item %>
@@ -2,23 +2,25 @@
2
2
  class: "space-y-12",
3
3
  data: {
4
4
  'model-id': @resource.model.id,
5
+ selected_resources_name: @resource.model_key,
6
+ selected_resources: [@resource.model.id],
5
7
  **@resource.stimulus_data_attributes
6
8
  } do %>
7
- <% @resource.panels.each do |resource_panel| %>
9
+ <% @resource.panels.each_with_index do |resource_panel, index| %>
8
10
  <%= form_with model: @resource.model,
9
11
  scope: @resource.form_scope,
10
- url: helpers.resource_path(model: @resource.model, resource: @resource),
11
- method: :put,
12
+ url: form_url,
12
13
  local: true,
13
14
  multipart: true do |form| %>
14
15
  <%= render Avo::ReferrerParamsComponent.new back_path: back_path %>
15
- <%= render Avo::PanelComponent.new(title: resource_panel[:name], description: @resource.resource_description, display_breadcrumbs: true) do |c| %>
16
+ <%= render Avo::PanelComponent.new(title: resource_panel[:name], description: @resource.resource_description, display_breadcrumbs: true, index: index) do |c| %>
16
17
  <% c.tools do %>
17
18
  <%= a_link back_path,
18
19
  style: :text,
19
20
  icon: 'arrow-left' do %>
20
21
  <%= t('avo.cancel').capitalize %>
21
22
  <% end %>
23
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
22
24
  <% if can_see_the_save_button? %>
23
25
  <%= a_button color: :primary,
24
26
  style: :primary,
@@ -4,9 +4,13 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
4
4
  include Avo::ResourcesHelper
5
5
  include Avo::ApplicationHelper
6
6
 
7
- def initialize(resource: nil)
7
+ attr_reader :view
8
+
9
+ def initialize(resource: nil, model: nil, actions: [], view: :edit)
8
10
  @resource = resource
9
- @view = :edit
11
+ @model = model
12
+ @actions = actions
13
+ @view = view
10
14
 
11
15
  split_panel_fields
12
16
  end
@@ -17,7 +21,11 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
17
21
  elsif via_index?
18
22
  helpers.resources_path(resource: @resource)
19
23
  else # via resource show page
20
- helpers.resource_path(model: @resource.model, resource: @resource)
24
+ if is_edit?
25
+ helpers.resource_path(model: @resource.model, resource: @resource)
26
+ else
27
+ helpers.resources_path(resource: @resource)
28
+ end
21
29
  end
22
30
  end
23
31
 
@@ -32,4 +40,24 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
32
40
  def via_index?
33
41
  params[:via_view] == "index"
34
42
  end
43
+
44
+ def is_edit?
45
+ view == :edit
46
+ end
47
+
48
+ def form_url
49
+ if is_edit?
50
+ helpers.resource_path(
51
+ model: @resource.model,
52
+ resource: @resource
53
+ )
54
+ else
55
+ helpers.resources_path(
56
+ resource: @resource,
57
+ via_relation_class: params[:via_relation_class],
58
+ via_relation: params[:via_relation],
59
+ via_resource_id: params[:via_resource_id]
60
+ )
61
+ end
62
+ end
35
63
  end
@@ -15,7 +15,7 @@
15
15
  <% end %>
16
16
  <% end %>
17
17
  <% if can_see_the_actions_button? %>
18
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource %>
18
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
19
19
  <% end %>
20
20
  <% if can_see_the_create_button? %>
21
21
  <%= a_link create_path,
@@ -22,7 +22,7 @@
22
22
  } do %>
23
23
  <%= t('avo.detach_item', item: title).capitalize %>
24
24
  <% end %>
25
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource %>
25
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
26
26
  <% end %>
27
27
  <% if can_see_the_edit_button? %>
28
28
  <%= a_link edit_path,
@@ -57,7 +57,7 @@
57
57
  <%= t('avo.delete').capitalize %>
58
58
  <% end %>
59
59
  <% end %>
60
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource %>
60
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
61
61
  <% if @resource.authorization.authorize_action(:edit, raise_exception: false) %>
62
62
  <%= a_link edit_path,
63
63
  color: :primary,
@@ -76,7 +76,7 @@ module Avo
76
76
 
77
77
  def show
78
78
  @resource.hydrate(model: @model, view: :show, user: _current_user, params: params)
79
-
79
+
80
80
  set_actions
81
81
 
82
82
  @page_title = @resource.default_panel_name.to_s
@@ -101,6 +101,8 @@ module Avo
101
101
  @model = @resource.model_class.new
102
102
  @resource = @resource.hydrate(model: @model, view: :new, user: _current_user)
103
103
 
104
+ set_actions
105
+
104
106
  @page_title = @resource.default_panel_name.to_s
105
107
 
106
108
  if params[:via_relation_class].present? && params[:via_resource_id].present?
@@ -161,6 +163,7 @@ module Avo
161
163
  end
162
164
 
163
165
  def edit
166
+ set_actions
164
167
  end
165
168
 
166
169
  def update
@@ -391,20 +394,22 @@ module Avo
391
394
  return resource_path(model: params[:via_relation_class].safe_constantize, resource: parent_resource, resource_id: params[:via_resource_id])
392
395
  end
393
396
 
394
- redirect_path_from_resource_option || resource_path(model: @model, resource: @resource)
397
+ redirect_path_from_resource_option(:after_create_path) || resource_path(model: @model, resource: @resource)
395
398
  end
396
399
 
397
400
  def after_update_path
398
401
  return params[:referrer] if params[:referrer].present?
399
402
 
400
- redirect_path_from_resource_option || resource_path(model: @model, resource: @resource)
403
+ redirect_path_from_resource_option(:after_update_path) || resource_path(model: @model, resource: @resource)
401
404
  end
402
405
 
403
- def redirect_path_from_resource_option
404
- return nil if @resource.class.after_update_path.blank?
406
+ def redirect_path_from_resource_option(action = :after_update_path)
407
+ return nil if @resource.class.send(action).blank?
405
408
 
406
- if @resource.class.after_create_path == :index
409
+ if @resource.class.send(action) == :index
407
410
  resources_path(resource: @resource)
411
+ elsif @resource.class.send(action) == :edit
412
+ edit_resource_path(resource: @resource, model: @resource.model)
408
413
  else
409
414
  resource_path(model: @model, resource: @resource)
410
415
  end
@@ -12,10 +12,9 @@ module Avo
12
12
  end
13
13
  end
14
14
 
15
- # Create the `route_key` from the model key so the namespaced models get the proper path (SomeModule::Post -> some_module_post).
16
- # Add the `_index` suffix for the uncountable models so they get the correct path (`fish_index`)
17
- route_key = resource.model_key
18
- route_key << "_index" if resource.model_name.singular == resource.model_name.plural
15
+ route_key = resource.route_key
16
+ # Add the `_index` suffix for the uncountable names so they get the correct path (`fish_index`)
17
+ route_key << "_index" if resource.route_key == resource.singular_route_key
19
18
 
20
19
  avo.send :"resources_#{route_key}_path", **existing_params, **args
21
20
  end
@@ -33,19 +32,19 @@ module Avo
33
32
  id = resource_id
34
33
  end
35
34
 
36
- avo.send :"resources_#{resource.singular_model_key}_path", id, **args
35
+ avo.send :"resources_#{resource.singular_route_key}_path", id, **args
37
36
  end
38
37
 
39
38
  def new_resource_path(model:, resource:, **args)
40
- avo.send :"new_resources_#{resource.singular_model_key}_path", **args
39
+ avo.send :"new_resources_#{resource.singular_route_key}_path", **args
41
40
  end
42
41
 
43
42
  def edit_resource_path(model:, resource:, **args)
44
- avo.send :"edit_resources_#{resource.singular_model_key}_path", model, **args
43
+ avo.send :"edit_resources_#{resource.singular_route_key}_path", model, **args
45
44
  end
46
45
 
47
46
  def resource_attach_path(resource, model_id, related_name, related_id = nil)
48
- helpers.avo.resources_associations_new_path(resource.singular_model_key, model_id, related_name)
47
+ helpers.avo.resources_associations_new_path(resource.singular_route_key, model_id, related_name)
49
48
  end
50
49
 
51
50
  def resource_detach_path(
@@ -79,7 +78,7 @@ module Avo
79
78
  end
80
79
 
81
80
  def order_up_resource_path(model:, resource:, **args)
82
- avo.send :"order_up_resources_#{resource.singular_model_key}_path", model, **args
81
+ avo.send :"order_up_resources_#{resource.singular_route_key}_path", model, **args
83
82
  end
84
83
  end
85
84
  end
@@ -7,7 +7,6 @@ import BelongsToFieldController from './controllers/fields/belongs_to_field_cont
7
7
  import BooleanFilterController from './controllers/boolean_filter_controller'
8
8
  import CodeFieldController from './controllers/fields/code_field_controller'
9
9
  import CopyToClipboardController from './controllers/copy_to_clipboard_controller'
10
- import CourseResourceController from './controllers/custom/course_resource_controller'
11
10
  import DashboardCardController from './controllers/dashboard_card_controller'
12
11
  import DateFieldController from './controllers/fields/date_field_controller'
13
12
  import FilterController from './controllers/filter_controller'
@@ -70,4 +69,3 @@ application.register('simple-mde', SimpleMdeController)
70
69
  application.register('trix-field', TrixFieldController)
71
70
 
72
71
  // Custom controllers
73
- application.register('course-resource', CourseResourceController)
@@ -1 +1,2 @@
1
- <%= render Avo::Views::ResourceEditComponent.new(resource: @resource) %>
1
+ <%= render Avo::Views::ResourceEditComponent.new(resource: @resource, view: @view, actions: @actions) %>
2
+
@@ -1 +1 @@
1
- <%= render Avo::Views::ResourceNewComponent.new(resource: @resource, model: @model) %>
1
+ <%= render Avo::Views::ResourceEditComponent.new(resource: @resource, model: @model, view: @view, actions: @actions) %>
@@ -5,13 +5,27 @@
5
5
  </a>
6
6
  </div>
7
7
  <% end %>
8
-
9
8
  <% if Avo::App.error_messages.present? %>
10
- <% Avo::App.error_messages.each do |message| %>
11
- <div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
12
- <a href="https://avohq.io/pricing" target="_blank" class="rounded bg-orange-700 text-white py-2 px-4 text-sm block items-center flex leading-tight">
13
- <%= svg "exclamation", class: "h-6 inline mr-2 text-bold flex-shrink-0 mr-1" %> <%= message %>
14
- </a>
15
- </div>
9
+ <% Avo::App.error_messages.each do |error| %>
10
+ <% if error.is_a? Hash %>
11
+ <%
12
+ url, message, target = error.values_at :url, :message, :target
13
+ %>
14
+ <div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
15
+ <a href="<%= url %>" target="<%= target %>" class="rounded bg-orange-700 text-white py-2 px-4 text-sm items-center flex leading-tight space-x-2">
16
+ <%= svg "exclamation", class: "h-6 inline mr-2 text-bold flex-shrink-0 mr-1" %>
17
+ <div>
18
+ <%= simple_format message %>
19
+ </div>
20
+ </a>
21
+ </div>
22
+ <% elsif error.is_a? String %>
23
+ <div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
24
+ <div class="rounded bg-orange-700 text-white py-2 px-4 text-sm items-center flex leading-tight space-x-2">
25
+ <%= svg "exclamation", class: "h-6 inline mr-2 text-bold flex-shrink-0 mr-1" %>
26
+ <div><%= simple_format error %></div>
27
+ </div>
28
+ </div>
29
+ <% end %>
16
30
  <% end %>
17
31
  <% end %>
@@ -30,6 +30,14 @@
30
30
  sort_direction = 'desc'
31
31
  end
32
32
  classes = "text-gray-500 tracking-tight leading-tight text-xs font-semibold"
33
+ classes += case field.index_text_align.to_sym
34
+ when :right
35
+ " text-right"
36
+ when :center
37
+ " text-center"
38
+ else
39
+ ""
40
+ end
33
41
  %>
34
42
  <th class="text-left uppercase px-3 py-2 whitespace-nowrap rounded-l">
35
43
  <% if field.sortable %>
@@ -38,7 +46,7 @@
38
46
  <%= render partial: 'avo/partials/sortable_component', locals: {field: field} %>
39
47
  <% end %>
40
48
  <% else %>
41
- <div class="flex items-center <%= classes %>">
49
+ <div class="block w-full <%= classes %>">
42
50
  <%= field.name %>
43
51
  </div>
44
52
  <% end %>
data/bin/test CHANGED
@@ -12,6 +12,7 @@ fi;
12
12
  # Run system tests
13
13
  if [[ -z "$1" ]] || [[ "$1" == "system" ]]; then
14
14
  yarn build:js
15
+ yarn build:custom-js
15
16
  yarn build:css
16
17
  bundle exec rspec ./spec --tag type:system
17
18
  fi;
data/lib/avo/app.rb CHANGED
@@ -14,7 +14,7 @@ module Avo
14
14
  class_attribute :view_context, default: nil
15
15
  class_attribute :params, default: {}
16
16
  class_attribute :translation_enabled, default: false
17
- class_attribute :error_messages, default: []
17
+ class_attribute :error_messages
18
18
 
19
19
  class << self
20
20
  def boot
@@ -52,6 +52,7 @@ module Avo
52
52
  Rails.logger.debug "[Avo] Failed to set ActiveStorage::Current.url_options, #{exception.inspect}"
53
53
  end
54
54
 
55
+ check_bad_resources
55
56
  init_resources
56
57
  init_dashboards if license.has_with_trial(:dashboards)
57
58
  end
@@ -80,6 +81,22 @@ module Avo
80
81
  )
81
82
  end
82
83
 
84
+ def check_bad_resources
85
+ resources.each do |resource|
86
+ has_model = resource.model_class.present?
87
+
88
+ unless has_model
89
+ possible_model = resource.class.to_s.gsub 'Resource', ''
90
+
91
+ Avo::App.error_messages.push({
92
+ url: "https://docs.avohq.io/2.0/resources.html#custom-model-class",
93
+ target: "_blank",
94
+ message: "#{resource.class.to_s} does not have a valid model assigned. It failed to find the #{possible_model} model. \n\r Please create that model or assign one using self.model_class = YOUR_MODEL"
95
+ })
96
+ end
97
+ end
98
+ end
99
+
83
100
  def init_resources
84
101
  self.resources = BaseResource.descendants
85
102
  .select do |resource|
@@ -120,9 +120,14 @@ module Avo
120
120
  end
121
121
 
122
122
  def visible_in_view
123
- return true unless visible.present?
123
+ # Run the visible block if available
124
+ return instance_exec(resource: self.class.resource, view: view, &visible) if visible.present?
124
125
 
125
- instance_exec(resource: self.class.resource, view: view, &visible)
126
+ # Hide on the :new view by default
127
+ return false if view == :new
128
+
129
+ # Show on all other views
130
+ true
126
131
  end
127
132
 
128
133
  def param_id
@@ -5,6 +5,7 @@ module Avo
5
5
 
6
6
  include ActionView::Helpers::UrlHelper
7
7
  include Avo::Concerns::HasTools
8
+ include Avo::Concerns::HasModel
8
9
  include Avo::Concerns::HasFields
9
10
  include Avo::Concerns::HasStimulusControllers
10
11
 
@@ -435,7 +436,11 @@ module Avo
435
436
  end
436
437
 
437
438
  def route_key
438
- model_class.model_name.route_key
439
+ class_name_without_resource.underscore.pluralize
440
+ end
441
+
442
+ def singular_route_key
443
+ route_key.singularize
439
444
  end
440
445
 
441
446
  # This is used as the model class ID