View component I use for Index pages that are linked up to pagy and ransack, this component provides standard interface with title, subtitle, search, active filter and new button. Styling can change as required.
rails g component IndexContainer
# frozen_string_literal: true
class IndexContainerComponent < ApplicationComponent
attr_reader :title, :sub_title, :new_path
renders_one :search_form
def initialize(title:, new_path: nil, sub_title: "")
@title = title
@sub_title = sub_title
@new_path = new_path
end
end
<div class="py-6 px-6">
<div class="">
<div class="mx-auto max-w-full px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900 border-b-2 pb-2"><%= title %></h1>
<h2 class="text-sm text-gray-700 pt-2"><%= sub_title %></h1>
<div class="flex flex-1 justify-between px-4 md:px-0">
<div class="flex flex-1 pt-2">
<% if search_form.present? %>
<%= search_form %>
<% end %>
</div>
<% if new_path.present? %>
<div class="ml-4 flex items-center md:ml-6">
<%= render(PrimaryLinkButtonComponent.new(title: "New", small: true, path: new_path )) %>
</div>
<% end %>
</div>
</div>
<div class="mx-auto max-w-full px-4 sm:px-6 md:px-8">
<div class="py-4">
<%= content %>
</div>
</div>
</div>
</div>
Note if look at earlier revisions of gist will see had tried adding Ransack::Helpers to the component to use search_form_for but when deploying via hatchbox it was causing errors so now put in partial. Actually maybe better design as can send any search form want anyway
<%= search_form_for query, method: :get, data: { turbo_frame: frame, turbo_action: "advance" } do |f| %>
<label for="name_cont" class="sr-only">Search</label>
<div class="flex">
<div class="relative flex-grow focus-within:z-10">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<!-- Heroicon name: mini/magnifying-glass -->
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
</svg>
</div>
<%= f.search_field search_field_name, class: "md:w-96 w-full rounded-md border-gray-300 pl-10 focus:border-indigo-500 focus:ring-indigo-500 sm:block sm:text-sm", placeholder: "Search #{placeholder}", oninput: "this.form.requestSubmit()" %>
</div>
<% if active_filter %>
<div class="relative flex-grow focus-within:z-10 ml-3 mt-2">
<%= render Forms::CheckboxInputComponent.new(f, :active_eq, label: "Active", oninput: "this.form.requestSubmit()") %>
</div>
<% end %>
</div>
<% end %>