avo 2.0.0 → 2.1.2.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/Gemfile +1 -1
 - data/Gemfile.lock +3 -3
 - data/app/assets/builds/avo.css +8810 -0
 - data/app/assets/builds/avo.js +423 -0
 - data/app/assets/builds/avo.js.map +7 -0
 - data/app/assets/stylesheets/avo.css +1 -1
 - data/app/assets/svgs/save.svg +8 -1
 - data/app/components/avo/actions_component.html.erb +1 -1
 - data/app/components/avo/alert_component.html.erb +1 -1
 - data/app/components/avo/alert_component.rb +6 -6
 - data/app/components/avo/blank_field_component.html.erb +0 -0
 - data/app/components/avo/blank_field_component.rb +4 -0
 - data/app/components/avo/card_component.html.erb +7 -2
 - data/app/components/avo/fields/badge_field/index_component.html.erb +1 -1
 - data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
 - data/app/components/avo/fields/external_image_field/index_component.html.erb +1 -2
 - data/app/components/avo/fields/file_field/index_component.html.erb +3 -3
 - data/app/components/avo/fields/file_field/index_component.rb +11 -0
 - data/app/components/avo/fields/gravatar_field/index_component.html.erb +1 -1
 - data/app/components/avo/fields/progress_bar_field/index_component.html.erb +1 -1
 - data/app/components/avo/index/field_wrapper_component.html.erb +1 -1
 - data/app/components/avo/index/field_wrapper_component.rb +12 -1
 - data/app/components/avo/index/resource_controls_component.html.erb +5 -1
 - data/app/components/avo/index/resource_table_component.html.erb +1 -1
 - data/app/components/avo/panel_component.html.erb +15 -5
 - data/app/components/avo/panel_component.rb +9 -0
 - data/app/components/avo/referrer_params_component.html.erb +4 -0
 - data/app/components/avo/referrer_params_component.rb +9 -0
 - data/app/components/avo/sidebar_component.html.erb +2 -0
 - data/app/components/avo/sidebar_component.rb +3 -1
 - data/app/components/avo/views/resource_edit_component.html.erb +14 -1
 - data/app/components/avo/views/resource_new_component.html.erb +13 -0
 - data/app/components/avo/views/resource_show_component.html.erb +11 -0
 - data/app/components/avo/views/resource_show_component.rb +5 -0
 - data/app/controllers/avo/application_controller.rb +1 -1
 - data/app/controllers/avo/base_controller.rb +87 -37
 - data/app/controllers/avo/dashboards_controller.rb +2 -6
 - data/app/controllers/avo/search_controller.rb +5 -1
 - data/app/helpers/avo/url_helpers.rb +6 -2
 - data/app/javascript/avo.js +4 -3
 - data/app/views/avo/actions/show.html.erb +1 -0
 - data/app/views/avo/associations/new.html.erb +1 -0
 - data/app/views/avo/dashboards/show.html.erb +4 -1
 - data/app/views/avo/home/failed_to_load.html.erb +21 -1
 - data/app/views/avo/partials/_sidebar_extra.html.erb +0 -0
 - data/app/views/layouts/avo/application.html.erb +7 -0
 - data/bin/dev +7 -6
 - data/lib/avo/app.rb +8 -1
 - data/lib/avo/base_action.rb +5 -4
 - data/lib/avo/base_card.rb +175 -0
 - data/lib/avo/base_resource.rb +34 -8
 - data/lib/avo/configuration.rb +2 -0
 - data/lib/avo/dashboards/base_dashboard.rb +37 -2
 - data/lib/avo/dashboards/base_divider.rb +3 -1
 - data/lib/avo/dashboards/chartkick_card.rb +1 -1
 - data/lib/avo/dashboards/dashboard_card.rb +6 -0
 - data/lib/avo/dashboards/partial_card.rb +1 -1
 - data/lib/avo/fields/base_field.rb +19 -6
 - data/lib/avo/fields/belongs_to_field.rb +1 -1
 - data/lib/avo/fields/date_field.rb +1 -1
 - data/lib/avo/fields/external_image_field.rb +2 -2
 - data/lib/avo/fields_collector.rb +7 -2
 - data/lib/avo/hosts/dashboard_card.rb +1 -0
 - data/lib/avo/hosts/dashboard_visibility.rb +19 -0
 - data/lib/avo/licensing/pro_license.rb +1 -0
 - data/lib/avo/version.rb +1 -1
 - data/lib/generators/avo/templates/dashboards/dashboard.tt +4 -1
 - data/public/avo-assets/avo.css +55 -34
 - data/public/avo-assets/avo.js +166 -165
 - data/public/avo-assets/avo.js.map +3 -3
 - metadata +15 -6
 - data/app/views/avo/partials/_failed_state.html.erb +0 -16
 - data/lib/avo/dashboards/base_card.rb +0 -151
 
| 
         @@ -10,7 +10,11 @@ module Avo 
     | 
|
| 
       10 
10 
     | 
    
         
             
                def index
         
     | 
| 
       11 
11 
     | 
    
         
             
                  raise ActionController::BadRequest.new("This feature requires the pro license https://avohq.io/purchase/pro") if App.license.lacks_with_trial(:global_search)
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
      
 13 
     | 
    
         
            +
                  resources = Avo::App.resources.reject do |resource|
         
     | 
| 
      
 14 
     | 
    
         
            +
                    resource.class.hide_from_global_search
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  render json: search_resources(resources)
         
     | 
| 
       14 
18 
     | 
    
         
             
                end
         
     | 
| 
       15 
19 
     | 
    
         | 
| 
       16 
20 
     | 
    
         
             
                def show
         
     | 
| 
         @@ -12,8 +12,12 @@ module Avo 
     | 
|
| 
       12 
12 
     | 
    
         
             
                    end
         
     | 
| 
       13 
13 
     | 
    
         
             
                  end
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                  #  
     | 
| 
       16 
     | 
    
         
            -
                   
     | 
| 
      
 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
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  avo.send :"resources_#{route_key}_path", **existing_params, **args
         
     | 
| 
       17 
21 
     | 
    
         
             
                end
         
     | 
| 
       18 
22 
     | 
    
         | 
| 
       19 
23 
     | 
    
         
             
                def resource_path(
         
     | 
    
        data/app/javascript/avo.js
    CHANGED
    
    | 
         @@ -71,12 +71,13 @@ document.addEventListener('turbo:frame-load', () => { 
     | 
|
| 
       71 
71 
     | 
    
         
             
              initTippy()
         
     | 
| 
       72 
72 
     | 
    
         
             
            })
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
            document.addEventListener('turbo:before-fetch-response', (e) => {
         
     | 
| 
      
 74 
     | 
    
         
            +
            document.addEventListener('turbo:before-fetch-response', async (e) => {
         
     | 
| 
       75 
75 
     | 
    
         
             
              if (e.detail.fetchResponse.response.status === 500) {
         
     | 
| 
       76 
     | 
    
         
            -
                const id = e. 
     | 
| 
       77 
     | 
    
         
            -
                e. 
     | 
| 
      
 76 
     | 
    
         
            +
                const { id, src } = e.target
         
     | 
| 
      
 77 
     | 
    
         
            +
                e.target.src = `${window.Avo.configuration.root_path}/failed_to_load?turbo_frame=${id}&src=${src}`
         
     | 
| 
       78 
78 
     | 
    
         
             
              }
         
     | 
| 
       79 
79 
     | 
    
         
             
            })
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
       80 
81 
     | 
    
         
             
            document.addEventListener('turbo:visit', () => document.body.classList.add('turbo-loading'))
         
     | 
| 
       81 
82 
     | 
    
         
             
            document.addEventListener('turbo:submit-start', () => document.body.classList.add('turbo-loading'))
         
     | 
| 
       82 
83 
     | 
    
         
             
            document.addEventListener('turbo:submit-end', () => document.body.classList.remove('turbo-loading'))
         
     | 
| 
         @@ -8,7 +8,10 @@ 
     | 
|
| 
       8 
8 
     | 
    
         
             
                          <%= render Avo::Dashboards::DividerComponent.new(divider: item) %>
         
     | 
| 
       9 
9 
     | 
    
         
             
                        <% elsif item.is_card? %>
         
     | 
| 
       10 
10 
     | 
    
         
             
                          <%= content_tag(:div, class: "relative bg-white rounded shadow-panel space-y-2 #{item.card_classes} overflow-hidden") do %>
         
     | 
| 
       11 
     | 
    
         
            -
                            <turbo-frame id="<%= item.turbo_frame %>" 
     | 
| 
      
 11 
     | 
    
         
            +
                            <turbo-frame id="<%= item.turbo_frame %>"
         
     | 
| 
      
 12 
     | 
    
         
            +
                              src="<%= item.frame_url(enforced_range: @range, params: params) %>"
         
     | 
| 
      
 13 
     | 
    
         
            +
                              data-card-index="<%= item.index %>"
         
     | 
| 
      
 14 
     | 
    
         
            +
                            >
         
     | 
| 
       12 
15 
     | 
    
         
             
                              <%= render(Avo::LoadingComponent.new(title: item.label)) %>
         
     | 
| 
       13 
16 
     | 
    
         
             
                            </turbo-frame>
         
     | 
| 
       14 
17 
     | 
    
         
             
                          <% end %>
         
     | 
| 
         @@ -1,3 +1,23 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            <%= turbo_frame_wrap(params[:turbo_frame]) do %>
         
     | 
| 
       2 
     | 
    
         
            -
               
     | 
| 
      
 2 
     | 
    
         
            +
              <%
         
     | 
| 
      
 3 
     | 
    
         
            +
              classes = 'absolute inset-auto left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2'
         
     | 
| 
      
 4 
     | 
    
         
            +
              label = t 'avo.failed_to_load'
         
     | 
| 
      
 5 
     | 
    
         
            +
            %>
         
     | 
| 
      
 6 
     | 
    
         
            +
              <div class="relative flex-1 py-12">
         
     | 
| 
      
 7 
     | 
    
         
            +
                <div class="relative block text-gray-300 h-40 w-full">
         
     | 
| 
      
 8 
     | 
    
         
            +
                  <%= svg 'avocado', class: "#{classes} h-20 text-gray-400" %>
         
     | 
| 
      
 9 
     | 
    
         
            +
                  <%= svg 'code', class: "#{classes} h-8 -ml-20 -mt-12" %>
         
     | 
| 
      
 10 
     | 
    
         
            +
                  <%= svg 'fire', class: "#{classes} h-8 -ml-10 -mt-24" %>
         
     | 
| 
      
 11 
     | 
    
         
            +
                  <%= svg 'color-swatch', class: "#{classes} h-8 ml-8 -mt-24" %>
         
     | 
| 
      
 12 
     | 
    
         
            +
                  <%= svg 'globe', class: "#{classes} h-8 ml-20 -mt-12" %>
         
     | 
| 
      
 13 
     | 
    
         
            +
                  <%= svg 'library', class: "#{classes} h-8 -ml-20 mt-4" %>
         
     | 
| 
      
 14 
     | 
    
         
            +
                  <%= svg 'photograph', class: "#{classes} h-8 ml-20 mt-4" %>
         
     | 
| 
      
 15 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 16 
     | 
    
         
            +
                <div class="relative block text-center text-lg text-gray-400 font-semibold -mt-10"><%= label %> <span class="border-b-2 border-dashed"><%= params[:turbo_frame].to_s.humanize.downcase if params[:turbo_frame].present? %></span> frame</div>
         
     | 
| 
      
 17 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 18 
     | 
    
         
            +
              <% if Rails.env.development? %>
         
     | 
| 
      
 19 
     | 
    
         
            +
                <div class="text-center text-sm w-full">
         
     | 
| 
      
 20 
     | 
    
         
            +
                  This is not an issue with Avo. Click <%= link_to 'this link', params[:src], target: :_blank %> to see why this frame failed to load.
         
     | 
| 
      
 21 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 22 
     | 
    
         
            +
              <% end %>
         
     | 
| 
       3 
23 
     | 
    
         
             
            <% end %>
         
     | 
| 
         
            File without changes
         
     | 
| 
         @@ -42,6 +42,13 @@ 
     | 
|
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
            <%= turbo_frame_tag 'alerts', class: "fixed inset-0 bottom-0 flex flex-col space-y-4 items-end justify-right px-4 py-6 sm:p-6 justify-end z-50 pointer-events-none" do %>
         
     | 
| 
       44 
44 
     | 
    
         
             
              <%= render Avo::FlashAlertsComponent.new flashes: flash %>
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              <% # In case we have other general error messages %>
         
     | 
| 
      
 47 
     | 
    
         
            +
              <% if @errors.present? %>
         
     | 
| 
      
 48 
     | 
    
         
            +
                <% @errors.each do |message| %>
         
     | 
| 
      
 49 
     | 
    
         
            +
                  <%= render Avo::AlertComponent.new :error, message %>
         
     | 
| 
      
 50 
     | 
    
         
            +
                <% end %>
         
     | 
| 
      
 51 
     | 
    
         
            +
              <% end %>
         
     | 
| 
       45 
52 
     | 
    
         
             
            <% end %>
         
     | 
| 
       46 
53 
     | 
    
         | 
| 
       47 
54 
     | 
    
         
             
            <%= render partial: "avo/partials/scripts" %>
         
     | 
    
        data/bin/dev
    CHANGED
    
    | 
         @@ -1,9 +1,10 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            #!/usr/bin/env bash
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
              echo "Installing foreman..."
         
     | 
| 
       6 
     | 
    
         
            -
              gem install foreman
         
     | 
| 
       7 
     | 
    
         
            -
            fi
         
     | 
| 
      
 3 
     | 
    
         
            +
            PORT="${PORT:-3030}"
         
     | 
| 
      
 4 
     | 
    
         
            +
            export PORT
         
     | 
| 
       8 
5 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
            if command -v overmind &> /dev/null; then
         
     | 
| 
      
 7 
     | 
    
         
            +
              overmind start -f Procfile.dev "$@"
         
     | 
| 
      
 8 
     | 
    
         
            +
            else
         
     | 
| 
      
 9 
     | 
    
         
            +
              foreman start -f Procfile.dev "$@"
         
     | 
| 
      
 10 
     | 
    
         
            +
            fi
         
     | 
    
        data/lib/avo/app.rb
    CHANGED
    
    | 
         @@ -10,6 +10,8 @@ module Avo 
     | 
|
| 
       10 
10 
     | 
    
         
             
                class_attribute :current_user, default: nil
         
     | 
| 
       11 
11 
     | 
    
         
             
                class_attribute :root_path, default: nil
         
     | 
| 
       12 
12 
     | 
    
         
             
                class_attribute :view_context, default: nil
         
     | 
| 
      
 13 
     | 
    
         
            +
                class_attribute :params, default: {}
         
     | 
| 
      
 14 
     | 
    
         
            +
                class_attribute :translation_enabled, default: false
         
     | 
| 
       13 
15 
     | 
    
         | 
| 
       14 
16 
     | 
    
         
             
                class << self
         
     | 
| 
       15 
17 
     | 
    
         
             
                  def boot
         
     | 
| 
         @@ -24,14 +26,16 @@ module Avo 
     | 
|
| 
       24 
26 
     | 
    
         
             
                    end
         
     | 
| 
       25 
27 
     | 
    
         
             
                  end
         
     | 
| 
       26 
28 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                  def init(request:, context:, current_user:, root_path:, view_context:)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  def init(request:, context:, current_user:, root_path:, view_context:, params:)
         
     | 
| 
       28 
30 
     | 
    
         
             
                    self.request = request
         
     | 
| 
       29 
31 
     | 
    
         
             
                    self.context = context
         
     | 
| 
       30 
32 
     | 
    
         
             
                    self.current_user = current_user
         
     | 
| 
       31 
33 
     | 
    
         
             
                    self.root_path = root_path
         
     | 
| 
       32 
34 
     | 
    
         
             
                    self.view_context = view_context
         
     | 
| 
      
 35 
     | 
    
         
            +
                    self.params = params
         
     | 
| 
       33 
36 
     | 
    
         | 
| 
       34 
37 
     | 
    
         
             
                    self.license = Licensing::LicenseManager.new(Licensing::HQ.new(request).response).license
         
     | 
| 
      
 38 
     | 
    
         
            +
                    self.translation_enabled = license.has(:localization)
         
     | 
| 
       35 
39 
     | 
    
         | 
| 
       36 
40 
     | 
    
         
             
                    # Set the current host for ActiveStorage
         
     | 
| 
       37 
41 
     | 
    
         
             
                    begin
         
     | 
| 
         @@ -89,6 +93,9 @@ module Avo 
     | 
|
| 
       89 
93 
     | 
    
         
             
                      .select do |dashboard|
         
     | 
| 
       90 
94 
     | 
    
         
             
                        dashboard != Dashboards::BaseDashboard
         
     | 
| 
       91 
95 
     | 
    
         
             
                      end
         
     | 
| 
      
 96 
     | 
    
         
            +
                      .uniq do |dashboard|
         
     | 
| 
      
 97 
     | 
    
         
            +
                        dashboard.id
         
     | 
| 
      
 98 
     | 
    
         
            +
                      end
         
     | 
| 
       92 
99 
     | 
    
         
             
                  end
         
     | 
| 
       93 
100 
     | 
    
         | 
| 
       94 
101 
     | 
    
         
             
                  # Returns the Avo dashboard by id
         
     | 
    
        data/lib/avo/base_action.rb
    CHANGED
    
    | 
         @@ -13,6 +13,7 @@ module Avo 
     | 
|
| 
       13 
13 
     | 
    
         
             
                class_attribute :user
         
     | 
| 
       14 
14 
     | 
    
         
             
                class_attribute :resource
         
     | 
| 
       15 
15 
     | 
    
         
             
                class_attribute :fields
         
     | 
| 
      
 16 
     | 
    
         
            +
                class_attribute :invalid_fields
         
     | 
| 
       16 
17 
     | 
    
         
             
                class_attribute :standalone, default: false
         
     | 
| 
       17 
18 
     | 
    
         
             
                class_attribute :visible
         
     | 
| 
       18 
19 
     | 
    
         
             
                class_attribute :may_download_file, default: false
         
     | 
| 
         @@ -27,16 +28,16 @@ module Avo 
     | 
|
| 
       27 
28 
     | 
    
         
             
                  def form_data_attributes
         
     | 
| 
       28 
29 
     | 
    
         
             
                    # We can't respond with a file download from Turbo se we disable it on the form
         
     | 
| 
       29 
30 
     | 
    
         
             
                    if may_download_file
         
     | 
| 
       30 
     | 
    
         
            -
                      { 
     | 
| 
      
 31 
     | 
    
         
            +
                      {turbo: false, remote: false}
         
     | 
| 
       31 
32 
     | 
    
         
             
                    else
         
     | 
| 
       32 
     | 
    
         
            -
                      { 
     | 
| 
      
 33 
     | 
    
         
            +
                      {"turbo-frame": "_top", "action-target": "form"}
         
     | 
| 
       33 
34 
     | 
    
         
             
                    end
         
     | 
| 
       34 
35 
     | 
    
         
             
                  end
         
     | 
| 
       35 
36 
     | 
    
         | 
| 
       36 
37 
     | 
    
         
             
                  # We can't respond with a file download from Turbo se we disable close the modal manually after a while (it's a hack, we know)
         
     | 
| 
       37 
38 
     | 
    
         
             
                  def submit_button_data_attributes
         
     | 
| 
       38 
39 
     | 
    
         
             
                    if may_download_file
         
     | 
| 
       39 
     | 
    
         
            -
                      { 
     | 
| 
      
 40 
     | 
    
         
            +
                      {action: "click->modal#delayedClose"}
         
     | 
| 
       40 
41 
     | 
    
         
             
                    else
         
     | 
| 
       41 
42 
     | 
    
         
             
                      {}
         
     | 
| 
       42 
43 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -109,7 +110,7 @@ module Avo 
     | 
|
| 
       109 
110 
     | 
    
         
             
                  args = {
         
     | 
| 
       110 
111 
     | 
    
         
             
                    fields: processed_fields,
         
     | 
| 
       111 
112 
     | 
    
         
             
                    current_user: current_user,
         
     | 
| 
       112 
     | 
    
         
            -
                    resource: resource 
     | 
| 
      
 113 
     | 
    
         
            +
                    resource: resource
         
     | 
| 
       113 
114 
     | 
    
         
             
                  }
         
     | 
| 
       114 
115 
     | 
    
         | 
| 
       115 
116 
     | 
    
         
             
                  args[:models] = models unless standalone
         
     | 
| 
         @@ -0,0 +1,175 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Avo
         
     | 
| 
      
 2 
     | 
    
         
            +
              class BaseCard
         
     | 
| 
      
 3 
     | 
    
         
            +
                class_attribute :id
         
     | 
| 
      
 4 
     | 
    
         
            +
                class_attribute :label
         
     | 
| 
      
 5 
     | 
    
         
            +
                class_attribute :description
         
     | 
| 
      
 6 
     | 
    
         
            +
                class_attribute :cols, default: 1
         
     | 
| 
      
 7 
     | 
    
         
            +
                class_attribute :rows, default: 1
         
     | 
| 
      
 8 
     | 
    
         
            +
                class_attribute :initial_range
         
     | 
| 
      
 9 
     | 
    
         
            +
                class_attribute :ranges, default: []
         
     | 
| 
      
 10 
     | 
    
         
            +
                class_attribute :refresh_every
         
     | 
| 
      
 11 
     | 
    
         
            +
                class_attribute :display_header, default: true
         
     | 
| 
      
 12 
     | 
    
         
            +
                # private
         
     | 
| 
      
 13 
     | 
    
         
            +
                class_attribute :result_data
         
     | 
| 
      
 14 
     | 
    
         
            +
                class_attribute :query_block
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                attr_accessor :dashboard
         
     | 
| 
      
 17 
     | 
    
         
            +
                attr_accessor :options
         
     | 
| 
      
 18 
     | 
    
         
            +
                attr_accessor :index
         
     | 
| 
      
 19 
     | 
    
         
            +
                attr_accessor :params
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                delegate :context, to: ::Avo::App
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def query(&block)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    self.query_block = block
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def initialize(dashboard:, options: {}, index: 0, cols: nil, rows: nil, label: nil, description: nil, refresh_every: nil)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @dashboard = dashboard
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @index = index
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @cols = cols
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @rows = rows
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @label = label
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @refresh_every = refresh_every
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @description = description
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def label
         
     | 
| 
      
 41 
     | 
    
         
            +
                  return @label.to_s if @label.present?
         
     | 
| 
      
 42 
     | 
    
         
            +
                  return self.class.label.to_s if self.class.label.present?
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  self.class.id.to_s.humanize
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def description
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @description || self.class.description
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def refresh_every
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @refresh_every || self.class.refresh_every
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                def translated_range(range)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  return "#{range} days" if range.is_a? Integer
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  case range
         
     | 
| 
      
 59 
     | 
    
         
            +
                  when "MTD"
         
     | 
| 
      
 60 
     | 
    
         
            +
                    "Month to date"
         
     | 
| 
      
 61 
     | 
    
         
            +
                  when "QTD"
         
     | 
| 
      
 62 
     | 
    
         
            +
                    "Quarter to date"
         
     | 
| 
      
 63 
     | 
    
         
            +
                  when "YTD"
         
     | 
| 
      
 64 
     | 
    
         
            +
                    "Year to date"
         
     | 
| 
      
 65 
     | 
    
         
            +
                  when "TODAY"
         
     | 
| 
      
 66 
     | 
    
         
            +
                    "Today"
         
     | 
| 
      
 67 
     | 
    
         
            +
                  else
         
     | 
| 
      
 68 
     | 
    
         
            +
                    range
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def parsed_ranges
         
     | 
| 
      
 73 
     | 
    
         
            +
                  return unless ranges.present?
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  ranges.map { |range| [translated_range(range), range] }
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def turbo_frame
         
     | 
| 
      
 79 
     | 
    
         
            +
                  "#{dashboard.id}_#{id}"
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                def frame_url(enforced_range: nil, params: {})
         
     | 
| 
      
 83 
     | 
    
         
            +
                  enforced_range ||= initial_range || ranges.first
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  # append the parent params to the card request
         
     | 
| 
      
 86 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 87 
     | 
    
         
            +
                    other_params = "&#{params.permit!.to_h.map { |k, v| "#{k}=#{v}" }.join("&")}"
         
     | 
| 
      
 88 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  "#{Avo::App.root_path}/dashboards/#{dashboard.id}/cards/#{id}?turbo_frame=#{turbo_frame}&index=#{index}&range=#{enforced_range}#{other_params}"
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                def card_classes
         
     | 
| 
      
 95 
     | 
    
         
            +
                  result = ""
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  # Writing down the classes so TailwindCSS knows not to purge them
         
     | 
| 
      
 98 
     | 
    
         
            +
                  classes_for_cols = {
         
     | 
| 
      
 99 
     | 
    
         
            +
                    1 => " sm:col-span-1",
         
     | 
| 
      
 100 
     | 
    
         
            +
                    2 => " sm:col-span-2",
         
     | 
| 
      
 101 
     | 
    
         
            +
                    3 => " sm:col-span-3",
         
     | 
| 
      
 102 
     | 
    
         
            +
                    4 => " sm:col-span-4",
         
     | 
| 
      
 103 
     | 
    
         
            +
                    5 => " sm:col-span-5",
         
     | 
| 
      
 104 
     | 
    
         
            +
                    6 => " sm:col-span-6"
         
     | 
| 
      
 105 
     | 
    
         
            +
                  }
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                  classes_for_rows = {
         
     | 
| 
      
 108 
     | 
    
         
            +
                    1 => " h-36",
         
     | 
| 
      
 109 
     | 
    
         
            +
                    2 => " h-72",
         
     | 
| 
      
 110 
     | 
    
         
            +
                    3 => " h-[27rem]",
         
     | 
| 
      
 111 
     | 
    
         
            +
                    4 => " h-[36rem]",
         
     | 
| 
      
 112 
     | 
    
         
            +
                    5 => " h-[45rem]",
         
     | 
| 
      
 113 
     | 
    
         
            +
                    6 => " h-[54rem]"
         
     | 
| 
      
 114 
     | 
    
         
            +
                  }
         
     | 
| 
      
 115 
     | 
    
         
            +
                  # puts ["cols->", cols, classes_for_cols, classes_for_rows, classes_for_cols[cols.to_i]].inspect
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                  result += classes_for_cols[cols.to_i] if classes_for_cols[cols.to_i].present?
         
     | 
| 
      
 118 
     | 
    
         
            +
                  result += classes_for_rows[rows.to_i] if classes_for_rows[rows.to_i].present?
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  result
         
     | 
| 
      
 121 
     | 
    
         
            +
                end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                def type
         
     | 
| 
      
 124 
     | 
    
         
            +
                  return :metric if self.class.superclass == ::Avo::Dashboards::MetricCard
         
     | 
| 
      
 125 
     | 
    
         
            +
                  return :chartkick if self.class.superclass == ::Avo::Dashboards::ChartkickCard
         
     | 
| 
      
 126 
     | 
    
         
            +
                  return :partial if self.class.superclass == ::Avo::Dashboards::PartialCard
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                def compute_result
         
     | 
| 
      
 130 
     | 
    
         
            +
                  Avo::Hosts::DashboardCard.new(card: self, dashboard: dashboard, params: params, context: context, range: range, options: options)
         
     | 
| 
      
 131 
     | 
    
         
            +
                    .compute_result
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  self
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                def hydrate(dashboard: nil, params: nil)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  @dashboard = dashboard if dashboard.present?
         
     | 
| 
      
 138 
     | 
    
         
            +
                  @params = params if params.present?
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                  self
         
     | 
| 
      
 141 
     | 
    
         
            +
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                def range
         
     | 
| 
      
 144 
     | 
    
         
            +
                  return params[:range] if params.dig(:range).present?
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                  return initial_range if initial_range.present?
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                  ranges.first
         
     | 
| 
      
 149 
     | 
    
         
            +
                end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                def result(data)
         
     | 
| 
      
 152 
     | 
    
         
            +
                  self.result_data = data
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                  self
         
     | 
| 
      
 155 
     | 
    
         
            +
                end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                def is_card?
         
     | 
| 
      
 158 
     | 
    
         
            +
                  true
         
     | 
| 
      
 159 
     | 
    
         
            +
                end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                def is_divider?
         
     | 
| 
      
 162 
     | 
    
         
            +
                  false
         
     | 
| 
      
 163 
     | 
    
         
            +
                end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                private
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                def cols
         
     | 
| 
      
 168 
     | 
    
         
            +
                  @cols || self.class.cols
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                def rows
         
     | 
| 
      
 172 
     | 
    
         
            +
                  @rows || self.class.rows
         
     | 
| 
      
 173 
     | 
    
         
            +
                end
         
     | 
| 
      
 174 
     | 
    
         
            +
              end
         
     | 
| 
      
 175 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/avo/base_resource.rb
    CHANGED
    
    | 
         @@ -11,6 +11,7 @@ module Avo 
     | 
|
| 
       11 
11 
     | 
    
         
             
                delegate :avo, to: :view_context
         
     | 
| 
       12 
12 
     | 
    
         
             
                delegate :resource_path, to: :view_context
         
     | 
| 
       13 
13 
     | 
    
         
             
                delegate :resources_path, to: :view_context
         
     | 
| 
      
 14 
     | 
    
         
            +
                delegate :t, to: ::I18n
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
16 
     | 
    
         
             
                attr_accessor :view
         
     | 
| 
       16 
17 
     | 
    
         
             
                attr_accessor :model
         
     | 
| 
         @@ -26,7 +27,7 @@ module Avo 
     | 
|
| 
       26 
27 
     | 
    
         
             
                class_attribute :includes, default: []
         
     | 
| 
       27 
28 
     | 
    
         
             
                class_attribute :model_class
         
     | 
| 
       28 
29 
     | 
    
         
             
                class_attribute :translation_key
         
     | 
| 
       29 
     | 
    
         
            -
                class_attribute :translation_enabled
         
     | 
| 
      
 30 
     | 
    
         
            +
                class_attribute :translation_enabled, default: false
         
     | 
| 
       30 
31 
     | 
    
         
             
                class_attribute :default_view_type, default: :table
         
     | 
| 
       31 
32 
     | 
    
         
             
                class_attribute :devise_password_optional, default: false
         
     | 
| 
       32 
33 
     | 
    
         
             
                class_attribute :actions_loader
         
     | 
| 
         @@ -38,8 +39,14 @@ module Avo 
     | 
|
| 
       38 
39 
     | 
    
         
             
                class_attribute :resolve_query_scope
         
     | 
| 
       39 
40 
     | 
    
         
             
                class_attribute :resolve_find_scope
         
     | 
| 
       40 
41 
     | 
    
         
             
                class_attribute :ordering
         
     | 
| 
      
 42 
     | 
    
         
            +
                class_attribute :hide_from_global_search, default: false
         
     | 
| 
      
 43 
     | 
    
         
            +
                class_attribute :after_create_path, default: :show
         
     | 
| 
      
 44 
     | 
    
         
            +
                class_attribute :after_update_path, default: :show
         
     | 
| 
      
 45 
     | 
    
         
            +
                class_attribute :invalid_fields
         
     | 
| 
       41 
46 
     | 
    
         | 
| 
       42 
47 
     | 
    
         
             
                class << self
         
     | 
| 
      
 48 
     | 
    
         
            +
                  delegate :t, to: ::I18n
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
       43 
50 
     | 
    
         
             
                  def grid(&block)
         
     | 
| 
       44 
51 
     | 
    
         
             
                    grid_collector = GridCollector.new
         
     | 
| 
       45 
52 
     | 
    
         
             
                    grid_collector.instance_eval(&block)
         
     | 
| 
         @@ -96,6 +103,8 @@ module Avo 
     | 
|
| 
       96 
103 
     | 
    
         
             
                      self.class.model_class = model_class.base_class
         
     | 
| 
       97 
104 
     | 
    
         
             
                    end
         
     | 
| 
       98 
105 
     | 
    
         
             
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                  self.class.translation_enabled = ::Avo::App.translation_enabled
         
     | 
| 
       99 
108 
     | 
    
         
             
                end
         
     | 
| 
       100 
109 
     | 
    
         | 
| 
       101 
110 
     | 
    
         
             
                def hydrate(model: nil, view: nil, user: nil, params: nil)
         
     | 
| 
         @@ -215,7 +224,7 @@ module Avo 
     | 
|
| 
       215 
224 
     | 
    
         
             
                  when :edit
         
     | 
| 
       216 
225 
     | 
    
         
             
                    model_title
         
     | 
| 
       217 
226 
     | 
    
         
             
                  when :new
         
     | 
| 
       218 
     | 
    
         
            -
                     
     | 
| 
      
 227 
     | 
    
         
            +
                    t("avo.create_new_item", item: name.downcase).upcase_first
         
     | 
| 
       219 
228 
     | 
    
         
             
                  end
         
     | 
| 
       220 
229 
     | 
    
         
             
                end
         
     | 
| 
       221 
230 
     | 
    
         | 
| 
         @@ -244,9 +253,18 @@ module Avo 
     | 
|
| 
       244 
253 
     | 
    
         
             
                  class_name_without_resource.safe_constantize
         
     | 
| 
       245 
254 
     | 
    
         
             
                end
         
     | 
| 
       246 
255 
     | 
    
         | 
| 
      
 256 
     | 
    
         
            +
                def model_id
         
     | 
| 
      
 257 
     | 
    
         
            +
                  @model.send id
         
     | 
| 
      
 258 
     | 
    
         
            +
                end
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
       247 
260 
     | 
    
         
             
                def model_title
         
     | 
| 
       248 
     | 
    
         
            -
                  return  
     | 
| 
      
 261 
     | 
    
         
            +
                  return name if @model.nil?
         
     | 
| 
      
 262 
     | 
    
         
            +
             
     | 
| 
      
 263 
     | 
    
         
            +
                  the_title = @model.send title
         
     | 
| 
      
 264 
     | 
    
         
            +
                  return the_title if the_title.present?
         
     | 
| 
       249 
265 
     | 
    
         | 
| 
      
 266 
     | 
    
         
            +
                  model_id
         
     | 
| 
      
 267 
     | 
    
         
            +
                rescue
         
     | 
| 
       250 
268 
     | 
    
         
             
                  name
         
     | 
| 
       251 
269 
     | 
    
         
             
                end
         
     | 
| 
       252 
270 
     | 
    
         | 
| 
         @@ -267,11 +285,13 @@ module Avo 
     | 
|
| 
       267 
285 
     | 
    
         
             
                end
         
     | 
| 
       268 
286 
     | 
    
         | 
| 
       269 
287 
     | 
    
         
             
                def name
         
     | 
| 
      
 288 
     | 
    
         
            +
                  default = class_name_without_resource.titlecase
         
     | 
| 
      
 289 
     | 
    
         
            +
             
     | 
| 
       270 
290 
     | 
    
         
             
                  return @name if @name.present?
         
     | 
| 
       271 
291 
     | 
    
         | 
| 
       272 
     | 
    
         
            -
                  return  
     | 
| 
      
 292 
     | 
    
         
            +
                  return t(translation_key, count: 1, default: default).capitalize if translation_key
         
     | 
| 
       273 
293 
     | 
    
         | 
| 
       274 
     | 
    
         
            -
                   
     | 
| 
      
 294 
     | 
    
         
            +
                  default
         
     | 
| 
       275 
295 
     | 
    
         
             
                end
         
     | 
| 
       276 
296 
     | 
    
         | 
| 
       277 
297 
     | 
    
         
             
                def singular_name
         
     | 
| 
         @@ -279,9 +299,11 @@ module Avo 
     | 
|
| 
       279 
299 
     | 
    
         
             
                end
         
     | 
| 
       280 
300 
     | 
    
         | 
| 
       281 
301 
     | 
    
         
             
                def plural_name
         
     | 
| 
       282 
     | 
    
         
            -
                   
     | 
| 
      
 302 
     | 
    
         
            +
                  default = name.pluralize
         
     | 
| 
      
 303 
     | 
    
         
            +
             
     | 
| 
      
 304 
     | 
    
         
            +
                  return t(translation_key, count: 2, default: default).capitalize if translation_key
         
     | 
| 
       283 
305 
     | 
    
         | 
| 
       284 
     | 
    
         
            -
                   
     | 
| 
      
 306 
     | 
    
         
            +
                  default
         
     | 
| 
       285 
307 
     | 
    
         
             
                end
         
     | 
| 
       286 
308 
     | 
    
         | 
| 
       287 
309 
     | 
    
         
             
                def underscore_name
         
     | 
| 
         @@ -408,11 +430,15 @@ module Avo 
     | 
|
| 
       408 
430 
     | 
    
         
             
                # This is used as the model class ID
         
     | 
| 
       409 
431 
     | 
    
         
             
                # We use this instead of the route_key to maintain compatibility with uncountable models
         
     | 
| 
       410 
432 
     | 
    
         
             
                # With uncountable models route key appends an _index suffix (Fish->fish_index)
         
     | 
| 
       411 
     | 
    
         
            -
                # Example: User->users, MediaItem-> 
     | 
| 
      
 433 
     | 
    
         
            +
                # Example: User->users, MediaItem->media_items, Fish->fish
         
     | 
| 
       412 
434 
     | 
    
         
             
                def model_key
         
     | 
| 
       413 
435 
     | 
    
         
             
                  model_class.model_name.plural
         
     | 
| 
       414 
436 
     | 
    
         
             
                end
         
     | 
| 
       415 
437 
     | 
    
         | 
| 
      
 438 
     | 
    
         
            +
                def model_name
         
     | 
| 
      
 439 
     | 
    
         
            +
                  model_class.model_name
         
     | 
| 
      
 440 
     | 
    
         
            +
                end
         
     | 
| 
      
 441 
     | 
    
         
            +
             
     | 
| 
       416 
442 
     | 
    
         
             
                def singular_model_key
         
     | 
| 
       417 
443 
     | 
    
         
             
                  model_class.model_name.singular
         
     | 
| 
       418 
444 
     | 
    
         
             
                end
         
     | 
    
        data/lib/avo/configuration.rb
    CHANGED
    
    | 
         @@ -28,6 +28,7 @@ module Avo 
     | 
|
| 
       28 
28 
     | 
    
         
             
                attr_accessor :current_user_resource_name
         
     | 
| 
       29 
29 
     | 
    
         
             
                attr_accessor :raise_error_on_missing_policy
         
     | 
| 
       30 
30 
     | 
    
         
             
                attr_accessor :disabled_features
         
     | 
| 
      
 31 
     | 
    
         
            +
                attr_accessor :buttons_on_form_footers
         
     | 
| 
       31 
32 
     | 
    
         | 
| 
       32 
33 
     | 
    
         
             
                def initialize
         
     | 
| 
       33 
34 
     | 
    
         
             
                  @root_path = "/avo"
         
     | 
| 
         @@ -68,6 +69,7 @@ module Avo 
     | 
|
| 
       68 
69 
     | 
    
         
             
                  @current_user_resource_name = "user"
         
     | 
| 
       69 
70 
     | 
    
         
             
                  @raise_error_on_missing_policy = false
         
     | 
| 
       70 
71 
     | 
    
         
             
                  @disabled_features = []
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @buttons_on_form_footers = false
         
     | 
| 
       71 
73 
     | 
    
         
             
                end
         
     | 
| 
       72 
74 
     | 
    
         | 
| 
       73 
75 
     | 
    
         
             
                def locale_tag
         
     | 
| 
         @@ -8,12 +8,31 @@ module Avo 
     | 
|
| 
       8 
8 
     | 
    
         
             
                  class_attribute :description
         
     | 
| 
       9 
9 
     | 
    
         
             
                  class_attribute :items_holder
         
     | 
| 
       10 
10 
     | 
    
         
             
                  class_attribute :grid_cols, default: 3
         
     | 
| 
      
 11 
     | 
    
         
            +
                  class_attribute :visible, default: true
         
     | 
| 
      
 12 
     | 
    
         
            +
                  class_attribute :index, default: 0
         
     | 
| 
       11 
13 
     | 
    
         | 
| 
       12 
14 
     | 
    
         
             
                  class << self
         
     | 
| 
       13 
     | 
    
         
            -
                    def card(klass)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    def card(klass, label: nil, description: nil, cols: nil, rows: nil, refresh_every: nil, options: {})
         
     | 
| 
       14 
16 
     | 
    
         
             
                      self.items_holder ||= []
         
     | 
| 
       15 
17 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                      self.items_holder << klass.new(dashboard: self 
     | 
| 
      
 18 
     | 
    
         
            +
                      self.items_holder << klass.new(dashboard: self,
         
     | 
| 
      
 19 
     | 
    
         
            +
                        label: label,
         
     | 
| 
      
 20 
     | 
    
         
            +
                        description: description,
         
     | 
| 
      
 21 
     | 
    
         
            +
                        cols: cols,
         
     | 
| 
      
 22 
     | 
    
         
            +
                        rows: rows,
         
     | 
| 
      
 23 
     | 
    
         
            +
                        refresh_every: refresh_every,
         
     | 
| 
      
 24 
     | 
    
         
            +
                        options: options,
         
     | 
| 
      
 25 
     | 
    
         
            +
                        index: index
         
     | 
| 
      
 26 
     | 
    
         
            +
                      )
         
     | 
| 
      
 27 
     | 
    
         
            +
                      self.index += 1
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    def item_at_index(index)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      items.find do |item|
         
     | 
| 
      
 32 
     | 
    
         
            +
                        next if item.index.blank?
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                        item.index == index
         
     | 
| 
      
 35 
     | 
    
         
            +
                      end
         
     | 
| 
       17 
36 
     | 
    
         
             
                    end
         
     | 
| 
       18 
37 
     | 
    
         | 
| 
       19 
38 
     | 
    
         
             
                    def divider(**args)
         
     | 
| 
         @@ -48,6 +67,22 @@ module Avo 
     | 
|
| 
       48 
67 
     | 
    
         
             
                    def navigation_path
         
     | 
| 
       49 
68 
     | 
    
         
             
                      "#{Avo::App.root_path}/dashboards/#{id}"
         
     | 
| 
       50 
69 
     | 
    
         
             
                    end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                    def is_visible?
         
     | 
| 
      
 72 
     | 
    
         
            +
                      # Default is true
         
     | 
| 
      
 73 
     | 
    
         
            +
                      return true if visible == true
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                      # Hide if false
         
     | 
| 
      
 76 
     | 
    
         
            +
                      return false if visible == false
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                      if visible.respond_to? :call
         
     | 
| 
      
 79 
     | 
    
         
            +
                        ::Avo::Hosts::DashboardVisibility.new(block: visible, dashboard: self).handle
         
     | 
| 
      
 80 
     | 
    
         
            +
                      end
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    def is_hidden?
         
     | 
| 
      
 84 
     | 
    
         
            +
                      !is_visible?
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
       51 
86 
     | 
    
         
             
                  end
         
     | 
| 
       52 
87 
     | 
    
         
             
                end
         
     | 
| 
       53 
88 
     | 
    
         
             
              end
         
     | 
| 
         @@ -3,12 +3,14 @@ module Avo 
     | 
|
| 
       3 
3 
     | 
    
         
             
                class BaseDivider
         
     | 
| 
       4 
4 
     | 
    
         
             
                  attr_reader :label
         
     | 
| 
       5 
5 
     | 
    
         
             
                  attr_reader :invisible
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_reader :index
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
       7 
8 
     | 
    
         
             
                  class_attribute :id
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                  def initialize(label: nil, invisible: false)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize(label: nil, invisible: false, index: nil)
         
     | 
| 
       10 
11 
     | 
    
         
             
                    @label = label
         
     | 
| 
       11 
12 
     | 
    
         
             
                    @invisible = invisible
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @index = index
         
     | 
| 
       12 
14 
     | 
    
         
             
                  end
         
     | 
| 
       13 
15 
     | 
    
         | 
| 
       14 
16 
     | 
    
         
             
                  def is_divider?
         
     | 
| 
         @@ -10,6 +10,7 @@ module Avo 
     | 
|
| 
       10 
10 
     | 
    
         
             
                  delegate :view_context, to: "Avo::App"
         
     | 
| 
       11 
11 
     | 
    
         
             
                  delegate :main_app, to: :view_context
         
     | 
| 
       12 
12 
     | 
    
         
             
                  delegate :avo, to: :view_context
         
     | 
| 
      
 13 
     | 
    
         
            +
                  delegate :t, to: ::I18n
         
     | 
| 
       13 
14 
     | 
    
         | 
| 
       14 
15 
     | 
    
         
             
                  attr_reader :id
         
     | 
| 
       15 
16 
     | 
    
         
             
                  attr_reader :block
         
     | 
| 
         @@ -48,7 +49,7 @@ module Avo 
     | 
|
| 
       48 
49 
     | 
    
         
             
                    @id = id
         
     | 
| 
       49 
50 
     | 
    
         
             
                    @name = args[:name]
         
     | 
| 
       50 
51 
     | 
    
         
             
                    @translation_key = args[:translation_key]
         
     | 
| 
       51 
     | 
    
         
            -
                    @translation_enabled =  
     | 
| 
      
 52 
     | 
    
         
            +
                    @translation_enabled = ::Avo::App.translation_enabled
         
     | 
| 
       52 
53 
     | 
    
         
             
                    @block = block
         
     | 
| 
       53 
54 
     | 
    
         
             
                    @required = args[:required] || false
         
     | 
| 
       54 
55 
     | 
    
         
             
                    @readonly = args[:readonly] || false
         
     | 
| 
         @@ -94,18 +95,26 @@ module Avo 
     | 
|
| 
       94 
95 
     | 
    
         
             
                    @translation_key
         
     | 
| 
       95 
96 
     | 
    
         
             
                  end
         
     | 
| 
       96 
97 
     | 
    
         | 
| 
      
 98 
     | 
    
         
            +
                  # Getting the name of the resource (user/users, post/posts)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  # We'll first check to see if the user passed a name
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # Secondly we'll try to find a translation key
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # We'll fallback to humanizing the id
         
     | 
| 
       97 
102 
     | 
    
         
             
                  def name
         
     | 
| 
      
 103 
     | 
    
         
            +
                    default = @id.to_s.humanize(keep_id_suffix: true)
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
       98 
105 
     | 
    
         
             
                    return @name if @name.present?
         
     | 
| 
       99 
106 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
                    return  
     | 
| 
      
 107 
     | 
    
         
            +
                    return t(translation_key, count: 1, default: default).capitalize if translation_key
         
     | 
| 
       101 
108 
     | 
    
         | 
| 
       102 
     | 
    
         
            -
                     
     | 
| 
      
 109 
     | 
    
         
            +
                    default
         
     | 
| 
       103 
110 
     | 
    
         
             
                  end
         
     | 
| 
       104 
111 
     | 
    
         | 
| 
       105 
112 
     | 
    
         
             
                  def plural_name
         
     | 
| 
       106 
     | 
    
         
            -
                     
     | 
| 
      
 113 
     | 
    
         
            +
                    default = name.pluralize
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    return t(translation_key, count: 2, default: default).capitalize if translation_key
         
     | 
| 
       107 
116 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                     
     | 
| 
      
 117 
     | 
    
         
            +
                    default
         
     | 
| 
       109 
118 
     | 
    
         
             
                  end
         
     | 
| 
       110 
119 
     | 
    
         | 
| 
       111 
120 
     | 
    
         
             
                  def placeholder
         
     | 
| 
         @@ -181,8 +190,12 @@ module Avo 
     | 
|
| 
       181 
190 
     | 
    
         
             
                    "#{type.camelize}Field"
         
     | 
| 
       182 
191 
     | 
    
         
             
                  end
         
     | 
| 
       183 
192 
     | 
    
         | 
| 
      
 193 
     | 
    
         
            +
                  # Try and build the component class or fallback to a blank one
         
     | 
| 
       184 
194 
     | 
    
         
             
                  def component_for_view(view = :index)
         
     | 
| 
       185 
     | 
    
         
            -
                    "Avo::Fields::#{view_component_name}::#{view.to_s.camelize}Component" 
     | 
| 
      
 195 
     | 
    
         
            +
                    component_class = "::Avo::Fields::#{view_component_name}::#{view.to_s.camelize}Component"
         
     | 
| 
      
 196 
     | 
    
         
            +
                    component_class.constantize
         
     | 
| 
      
 197 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 198 
     | 
    
         
            +
                    ::Avo::BlankFieldComponent
         
     | 
| 
       186 
199 
     | 
    
         
             
                  end
         
     | 
| 
       187 
200 
     | 
    
         | 
| 
       188 
201 
     | 
    
         
             
                  def model_errors
         
     |