diff --git a/app/components/avo/paginator_component.html.erb b/app/components/avo/paginator_component.html.erb index 976add7fa..fa63f490b 100644 --- a/app/components/avo/paginator_component.html.erb +++ b/app/components/avo/paginator_component.html.erb @@ -1,4 +1,4 @@ -
+
" class="flex flex-col sm:flex-row items-center justify-between aborder-t aborder-slate-200 rounded-2xl space-y-2 sm:space-y-0">
diff --git a/app/components/avo/views/resource_index_component.html.erb b/app/components/avo/views/resource_index_component.html.erb index 9cbe4aa5d..6241bdf64 100644 --- a/app/components/avo/views/resource_index_component.html.erb +++ b/app/components/avo/views/resource_index_component.html.erb @@ -36,7 +36,18 @@
">
<% if show_search_input %> - <%= render partial: "avo/partials/resource_search", locals: {resource: @resource.route_key, via_reflection: via_reflection} %> + <%#= render partial: "avo/partials/resource_search", locals: {resource: @resource.route_key, via_reflection: via_reflection} %> + + <% else %> <%# Offset for the space-y-2 property when the search is missing %>
diff --git a/app/controllers/avo/base_controller.rb b/app/controllers/avo/base_controller.rb index efc350ab3..6ac5f05eb 100644 --- a/app/controllers/avo/base_controller.rb +++ b/app/controllers/avo/base_controller.rb @@ -36,6 +36,9 @@ def index @query = @query.includes(*@resource.includes) end + # Apply the search query if configured on the resource + safe_call :apply_search + # Eager load attachments if @resource.attachments.present? @resource.attachments.each do |attachment| @@ -64,6 +67,38 @@ def index end set_component_for __method__ + + respond_to do |format| + format.html + format.turbo_stream do + render turbo_stream: [ + turbo_stream.replace( + "#{@resource.model_key}_list", + partial: "avo/index/resource_table_component", + locals: { + resources: @resources, + resource: @resource, + reflection: @reflection, + parent_record: @parent_record, + parent_resource: @parent_resource, + pagy: @pagy, + query: @query, + actions: @actions + } + ), + turbo_stream.replace("#{@resource.model_key}_pagination") do + Avo::Current.view_context.render Avo::PaginatorComponent.new( + pagy: @pagy, + turbo_frame: @turbo_frame, + index_params: @index_params, + resource: @resource, + parent_record: @parent_record, + parent_resource: @parent_resource + ) + end + ] + end + end end def show @@ -325,17 +360,31 @@ def set_index_params @index_params = {} set_pagination_params + set_search_params + set_sorting_params + set_view_type_params - # Sorting - @index_params[:sort_by] = params[:sort_by] || @resource.sort_by_param + validate_view_type + end + + def set_search_params + @index_params[:q] = params[:q] if params[:q].present? + end + def set_sorting_params + @index_params[:sort_by] = params[:sort_by] || @resource.sort_by_param @index_params[:sort_direction] = params[:sort_direction] || @resource.default_sort_direction + end - # View types + def set_view_type_params available_view_types = @resource.available_view_types @index_params[:available_view_types] = available_view_types - @index_params[:view_type] = if params[:view_type].present? + @index_params[:view_type] = determine_view_type(available_view_types) + end + + def determine_view_type(available_view_types) + if params[:view_type].present? params[:view_type] elsif available_view_types.size == 1 available_view_types.first @@ -346,8 +395,10 @@ def set_index_params view: @view ).handle end + end - if available_view_types.exclude? @index_params[:view_type].to_sym + def validate_view_type + if @index_params[:available_view_types].exclude? @index_params[:view_type].to_sym raise "View type '#{@index_params[:view_type]}' is unavailable for #{@resource.class}." end end @@ -660,5 +711,19 @@ def set_pagination_params def set_query @query ||= @resource.class.query_scope end + + def apply_search + return if @resource.class.search_query.nil? + return if @index_params[:q].nil? + + search_query = @resource.search[:query] + return unless search_query.present? + + @query = Avo::ExecutionContext.new( + target: @resource.class.search_query, + params: params.merge(q: @index_params[:q]), + query: @query + ).handle + end end end diff --git a/app/javascript/js/controllers.js b/app/javascript/js/controllers.js index 5e07add73..3fc2e70a7 100644 --- a/app/javascript/js/controllers.js +++ b/app/javascript/js/controllers.js @@ -36,6 +36,7 @@ import RecordSelectorController from './controllers/record_selector_controller' import ReloadBelongsToFieldController from './controllers/fields/reload_belongs_to_field_controller' import ResourceEditController from './controllers/resource_edit_controller' import ResourceIndexController from './controllers/resource_index_controller' +import ResourceSearchController from './controllers/resource_search_controller' import ResourceShowController from './controllers/resource_show_controller' import SearchController from './controllers/search_controller' import SelectController from './controllers/select_controller' @@ -82,6 +83,7 @@ application.register('preview', PreviewController) application.register('record-selector', RecordSelectorController) application.register('resource-edit', ResourceEditController) application.register('resource-index', ResourceIndexController) +application.register('resource-search', ResourceSearchController) application.register('resource-show', ResourceShowController) application.register('search', SearchController) application.register('select-filter', SelectFilterController) diff --git a/app/javascript/js/controllers/resource_search_controller.js b/app/javascript/js/controllers/resource_search_controller.js new file mode 100644 index 000000000..e40c837ac --- /dev/null +++ b/app/javascript/js/controllers/resource_search_controller.js @@ -0,0 +1,68 @@ +import { Controller } from '@hotwired/stimulus' +import { get } from '@rails/request.js' + +export default class extends Controller { + static targets = ['input'] + + static values = { + debounce: { type: Number, default: 300 }, + } + + connect() { + console.log('Resource search controller connected') + } + + search() { + this.debouncedSearch() + } + + debouncedSearch = this.debounce(this.performSearch, this.debounceValue) + + async performSearch() { + const query = this.inputTarget.value + const currentUrl = new URL(window.location.href) + + // Get existing search params + const searchParams = new URLSearchParams(window.location.search) + + // Update the search parameter + if (query) { + searchParams.set('q', query) + } else { + searchParams.delete('q') + } + + // Reset to first page when searching + searchParams.set('page', '1') + + // Construct the new URL with all parameters + const newUrl = `${currentUrl.pathname}?${searchParams.toString()}` + + // Replace current URL without affecting browser history + window.history.replaceState({}, '', newUrl) + + try { + await get(newUrl, { + responseKind: 'turbo-stream', + headers: { + Accept: 'text/vnd.turbo-stream.html', + }, + }) + } catch (error) { + console.error('Error performing search:', error) + } + } + + debounce(func, wait) { + let timeout + + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout) + func.apply(this, args) + } + clearTimeout(timeout) + timeout = setTimeout(later, wait) + } + } +} diff --git a/app/views/avo/index/_resource_table_component.html.erb b/app/views/avo/index/_resource_table_component.html.erb new file mode 100644 index 000000000..a935fae12 --- /dev/null +++ b/app/views/avo/index/_resource_table_component.html.erb @@ -0,0 +1,10 @@ +<%= render Avo::Index::ResourceTableComponent.new( + resources: resources, + resource: resource, + reflection: reflection, + parent_record: parent_record, + parent_resource: parent_resource, + pagy: pagy, + query: query, + actions: actions +) %> diff --git a/package.json b/package.json index 10ef9549b..76e6cea51 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@hotwired/stimulus": "^3.2.2", "@hotwired/turbo-rails": "^8.0.13", "@rails/activestorage": "^6.1.710", + "@rails/request.js": "^0.0.11", "@stimulus-components/clipboard": "^5.0.0", "@stimulus-components/password-visibility": "^3.0.0", "@tailwindcss/container-queries": "^0.1.1", diff --git a/yarn.lock b/yarn.lock index 33643750c..1746619ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1754,6 +1754,11 @@ dependencies: spark-md5 "^3.0.0" +"@rails/request.js@^0.0.11": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@rails/request.js/-/request.js-0.0.11.tgz#4d9be25a49d97911c64ccd0f00b79d57fca4c3b4" + integrity sha512-2U3uYS0kbljt+pAstN+LIlZOl7xmOKig5N6FrvtUWO1wq0zR1Hf90fHfD2SYiyV8yH1nyKpoTmbLqWT0xe1zDg== + "@remirror/core-constants@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-3.0.0.tgz#96fdb89d25c62e7b6a5d08caf0ce5114370e3b8f"