app/components/avo/views/resource_index_component.rb
# frozen_string_literal: true
class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
include Avo::ResourcesHelper
include Avo::ApplicationHelper
attr_reader :scopes, :query, :turbo_frame, :parent_record, :parent_resource, :resource, :actions
def initialize(
resource: nil,
resources: nil,
records: [],
pagy: nil,
index_params: {},
filters: [],
actions: [],
reflection: nil,
turbo_frame: "",
parent_record: nil,
parent_resource: nil,
applied_filters: [],
query: nil,
scopes: nil
)
@resource = resource
@resources = resources
@records = records
@pagy = pagy
@index_params = index_params
@filters = filters
@actions = actions
@reflection = reflection
@turbo_frame = turbo_frame
@parent_record = parent_record
@parent_resource = parent_resource
@applied_filters = applied_filters
@view = Avo::ViewInquirer.new("index")
@query = query
@scopes = scopes
end
def title
if @reflection.present?
return name if field.present?
reflection_resource.plural_name
else
@resource.plural_name
end
end
def view_type
@index_params[:view_type]
end
def available_view_types
@index_params[:available_view_types]
end
# The Create button is dependent on the new? policy method.
# The create? should be called only when the user clicks the Save button so the developers gets access to the params from the form.
def can_see_the_create_button?
return authorize_association_for(:create) if @reflection.present?
@resource.authorization.authorize_action(:new, raise_exception: false) && !has_reflection_and_is_read_only
end
def can_attach?
klass = @reflection
klass = @reflection.through_reflection if klass.is_a? ::ActiveRecord::Reflection::ThroughReflection
@reflection.present? && klass.is_a?(::ActiveRecord::Reflection::HasManyReflection) && !has_reflection_and_is_read_only && authorize_association_for(:attach)
end
def create_path
args = {}
if @reflection.present?
args = {
via_resource_class: @parent_resource.class,
via_relation_class: reflection_model_class,
via_record_id: @parent_record.to_param
}
if @reflection.is_a? ActiveRecord::Reflection::ThroughReflection
args[:via_relation] = params[:resource_name]
end
if @reflection.is_a? ActiveRecord::Reflection::HasManyReflection
args[:via_relation] = @reflection.name
end
if @reflection.inverse_of.present?
args[:via_relation] = @reflection.inverse_of.name
end
end
helpers.new_resource_path(resource: @resource, **args)
end
def attach_path
current_path = CGI.unescape(request.env["PATH_INFO"]).split("/").select(&:present?)
Avo.root_path(
paths: [*current_path, "new"],
query: {
view: @parent_resource&.view&.to_s,
for_attribute: field&.try(:for_attribute)
}.compact
)
end
def singular_resource_name
if @reflection.present?
return name.singularize if field.present?
reflection_resource.name
else
@resource.singular_name || @resource.model_class.model_name.name.downcase
end
end
def description
# If this is a has many association, the user can pass a description to be shown just for this association.
if @reflection.present?
return field.description if field.present? && field.description
return
end
@resource.description
end
def show_search_input
return false unless authorized_to_search?
return false unless resource.class.search_query.present?
return false if field&.hide_search_input
true
end
def authorized_to_search?
# Hide the search if the authorization prevents it
resource.authorization.authorize_action("search", raise_exception: false)
end
def render_dynamic_filters_button
return unless Avo.avo_dynamic_filters_installed?
return unless resource.has_filters?
return if Avo::DynamicFilters.configuration.always_expanded
a_button size: :sm,
color: :primary,
icon: "filter",
data: {
controller: "avo-filters",
action: "click->avo-filters#toggleFiltersArea",
avo_filters_dynamic_filters_component_id_value: dynamic_filters_component_id
} do
Avo::DynamicFilters.configuration.button_label
end
end
def scopes_list
Avo::Advanced::Scopes::ListComponent.new(
scopes: scopes,
resource: resource,
turbo_frame: turbo_frame,
parent_record: parent_record,
query: query,
loader: resource.entity_loader(:scope)
)
end
def can_render_scopes?
defined?(Avo::Advanced)
end
private
def reflection_model_class
@reflection.active_record.to_s
end
def name
field.custom_name? ? field.name : field.plural_name
end
def via_reflection
return unless @reflection.present?
{
association: "has_many",
association_id: @reflection.name,
class: reflection_model_class,
id: @parent_record.to_param,
view: @parent_resource.view
}
end
def header_visible?
search_query_present? || filters_present? || has_many_view_types? || has_dynamic_filters?
end
def has_dynamic_filters?
Avo.avo_dynamic_filters_installed? && resource.has_filters?
end
def search_query_present?
@resource.class.search_query.present?
end
def filters_present?
@filters.present?
end
def has_many_view_types?
available_view_types.count > 1
end
# Generate a unique component id for the current component.
# This is used to identify the component in the DOM.
def dynamic_filters_component_id
@dynamic_filters_component_id ||= "dynamic_filters_component_id_#{SecureRandom.hex(3)}"
end
def reloadable
field&.reloadable?
end
def linkable?
field&.linkable?
end
end