# Adding index pages Index pages are common in the admin interface, and they are used to display a list of records for a specific model. Since these pages often have a similar appearance, we have a dedicated component to build them. This component helps avoid writing repetitive boilerplate code. ## The `index` action The `index` action is the standard action used to display index pages. It's a standard `GET` action that renders the `index` component. To support search scopes and filters the controller should include the `SolidusAdmin::ControllerHelpers::Search` module and call `apply_search_to` as follows: ```ruby class SolidusAdmin::UsersController < SolidusAdmin::BaseController include SolidusAdmin::ControllerHelpers::Search def index users = apply_search_to(Spree.user_class.order(id: :desc), param: :q) # ... ``` For pagination support, the index action should also call the `set_page_and_extract_portion_from` method provided by the `geared_pagination` gem. This method sets the `@page` instance variable to the paginated collection and returns the portion of the collection to be displayed on the current page. ```ruby def index users = apply_search_to(Spree.user_class.order(id: :desc), param: :q) set_page_and_extract_portion_from(users) # ... ``` Finally, the index action should render the `index` component passing the `@page` instance variable as the `collection` prop. ```ruby def index users = apply_search_to(Spree.user_class.order(id: :desc), param: :q) set_page_and_extract_portion_from(users) render component('users/index').new(page: @page) end ``` ## The `ui/pages/index` component The `ui/pages/index` component is an abstract component that provides sensible defaults for index pages. It also offers template methods that can be used to customize the behavior of these pages. We recommend examining existing index pages and the UI component itself to understand how they work. In this section, we'll cover the most important aspects. The index component requires only the `page` argument during initialization, all other parameters are provided through template methods. ```ruby class SolidusAdmin::Users::Index < Solidus::Admin::UI::Pages::Index def model_class Spree.user_class end end render component('users/index').new(page: @page) ``` ## Batch Actions Batch actions are operations that can be performed on multiple records simultaneously. The index page internally uses the `ui/table` component and depends on the `batch_actions` method to render the batch actions dropdown. In the component, batch actions are provided as an array of hashes, with each hash representing a single batch action. Each hash must contain the following keys: - `label`: the name of the batch action, this will be used as the label of the dropdown item - `icon`: the remix icon-name to be used as the icon of the dropdown item (see the `ui/icon` component for more information) - `action`: the name of the action to be performed when the batch action is selected. It can be a URL or a path - `method`: the HTTP method to be used when performing the action, such as `:delete` The `batch_actions` method is called in the context of the controller, so you can use any controller method or helper to build the batch actions. Batch actions will be submitted to the specified action with an `id` parameter containing the IDs of the selected records. Using `id` as the parameter name allows the same action to support both batch and single-record actions for standard routes. E.g. ```ruby # in the component def batch_actions [ { label: "Delete", icon: "trash", action: solidus_admin.delete_admin_users_path, method: :delete } ] end ``` ```ruby # in the controller def delete @users = Spree.user_class.where(id: params[:id]) @users.destroy_all flash[:success] = "Admin users deleted" redirect_to solidus_admin.users_path, status: :see_other end ``` ## Search Scopes Search scopes are used to filter the records displayed on the index page. The index page internally uses the `ui/table` component and relies on the `scopes` method to render the search scope buttons. In the component, search scopes are provided as an array of hashes, with each hash representing a single search scope. Each hash must contain the following keys: - `label`: the name of the search scope, used as the label of the button - `name`: the name of the search scope, sent via `q[scope]` parameter when the button is clicked - `default`: whether this is the default search scope, used to highlight the button when when the page is loaded On the controller side, search scopes can be defined with the `search_scope` helper, provided by `SolidusAdmin::ControllerHelpers::Search`. This helper takes a name, an optional `default` keyword argument, and a block. The block will be called with the current scope and should return a new ActiveRecord scope. E.g. ```ruby module SolidusAdmin class UsersController < SolidusAdmin::BaseController include SolidusAdmin::ControllerHelpers::Search search_scope(:customers, default: true) { _1.left_outer_joins(:role_users).where(role_users: { id: nil }) } search_scope(:admin) { _1.joins(:role_users).distinct } search_scope(:with_orders) { _1.joins(:orders).distinct } search_scope(:without_orders) { _1.left_outer_joins(:orders).where(orders: { id: nil }) } search_scope(:all) def index users = apply_search_to(Spree.user_class.order(id: :desc), param: :q) # ... ``` ## Filters Filters are used to narrow down the records displayed on the index page. The index page internally uses the `ui/table/ransack_filter` component and depends on the `filters` method to render the filters dropdown. In the component, filters are represented as an array of hashes, with each hash representing a single filter. Each hash must contain the following keys: - `label`: the name of the filter, used as the label in the filter bar - `attribute`: the name of the ransack-enabled attribute to be filtered - `predicate`: the name of the ransack predicate to be used, e.g. `eq`, `in`, `cont` - `options`: an array of options to be used for the filter, in the standard rails form of `[["label", "value"], ...]` On the controller side it's enough to add ransack support to the index action by including `SolidusAdmin::ControllerHelpers::Search` and calling `apply_search_to` as explained in the [Index action](#index-action) section. On the controller side, you simply need to add Ransack support to the index action by including the `SolidusAdmin::ControllerHelpers::Search` module and calling the `apply_search_to` method, as explained in the [Index action](#index-action) section.