implementar el buscador en el panel

ahora el índice de artículos incorporar buscador de texto libre.

además todos los filtros de búsqueda se mantienen entre búsquedas,
entonces al filtrar por tipo de artículo y término, se aplican ambos y
al cambiar el tipo se mantiene la búsqueda de texto.
This commit is contained in:
f 2021-05-07 16:17:25 -03:00
parent 34a05e860d
commit ad871baca6
8 changed files with 82 additions and 68 deletions

View file

@ -21,6 +21,10 @@ $form-feedback-invalid-color: $magenta;
$form-feedback-icon-valid-color: $black; $form-feedback-icon-valid-color: $black;
$component-active-bg: $magenta; $component-active-bg: $magenta;
$spacers: (
2-plus: 0.75rem
);
@import "bootstrap"; @import "bootstrap";
@import "editor"; @import "editor";

View file

@ -16,29 +16,25 @@ class PostsController < ApplicationController
authorize Post authorize Post
@site = find_site @site = find_site
@category = params.dig(:category) @q = params[:q]
@layout = params.dig(:layout) dictionary = IndexedPost.to_dictionary(locale: locale)
@locale = locale
# XXX: Cada vez que cambiamos un Post tocamos el sitio con lo que es # XXX: Cada vez que cambiamos un Post tocamos el sitio con lo que es
# más simple saber si hubo cambios. # más simple saber si hubo cambios.
if @category || @layout || stale?(@site) if filter_params.present? || stale?(@site)
@posts = @site.posts(lang: locale) # Todos los artículos de este sitio para el idioma actual
@posts = @posts.where(categories: @category) if @category @posts = @site.indexed_posts.where(locale: dictionary)
@posts = @posts.where(layout: @layout) if @layout # De este tipo
@posts = @posts.where(layout: filter_params[:layout]) if filter_params[:layout]
# Que estén dentro de la categoría
@posts = @posts.in_category(filter_params[:category]) if filter_params[:category]
# Aplicar los parámetros de búsqueda
@posts = @posts.search(locale, filter_params[:q]) if filter_params[:q].present?
# A los que este usuarie tiene acceso
@posts = PostPolicy::Scope.new(current_usuarie, @posts).resolve @posts = PostPolicy::Scope.new(current_usuarie, @posts).resolve
@category_name = if uuid?(@category)
@site.posts(lang: locale).find(@category, uuid: true)&.title&.value
else
@category
end
# Filtrar los posts que les invitades no pueden ver # Filtrar los posts que les invitades no pueden ver
@usuarie = @site.usuarie? current_usuarie @usuarie = @site.usuarie? current_usuarie
# Orden descendiente por número y luego por fecha
@posts.sort_by!(:order, :date).reverse!
end end
end end
@ -169,4 +165,14 @@ class PostsController < ApplicationController
def forget_content def forget_content
flash[:js] = { target: 'editor', action: 'forget-content', keys: (params[:storage_keys] || []).to_json } flash[:js] = { target: 'editor', action: 'forget-content', keys: (params[:storage_keys] || []).to_json }
end end
private
# Los parámetros de filtros que vamos a mantener en todas las URLs,
# solo los que no estén vacíos.
#
# @return [Hash]
def filter_params
@filter_params ||= params.permit(:q, :category, :layout).to_h.select { |_,v| v.present? }
end
end end

View file

@ -28,6 +28,8 @@ class IndexedPost < ApplicationRecord
# Trae los IndexedPost en el orden en que van a terminar en el sitio. # Trae los IndexedPost en el orden en que van a terminar en el sitio.
default_scope lambda { order(order: :desc, created_at: :desc) } default_scope lambda { order(order: :desc, created_at: :desc) }
scope :in_category, lambda { |category| where("front_matter->'categories' ? :category", category: category.to_s) }
scope :by_usuarie, lambda { |usuarie| where("front_matter->'usuaries' @> :usuarie::jsonb", usuarie: usuarie.to_s) }
belongs_to :site belongs_to :site

View file

@ -11,7 +11,7 @@ class Post
# Devuelve una versión indexable del Post # Devuelve una versión indexable del Post
# #
# @return [IndexedPosts] # @return [IndexedPost]
def to_index def to_index
IndexedPost.find_or_create_by(id: uuid.value).tap do |indexed_post| IndexedPost.find_or_create_by(id: uuid.value).tap do |indexed_post|
indexed_post.layout = layout.name indexed_post.layout = layout.name
@ -41,9 +41,16 @@ class Post
# #
# @return [Hash] # @return [Hash]
def indexable_front_matter def indexable_front_matter
return {} unless attribute? :categories {}.tap do |indexable_front_matter|
indexable_front_matter = {
usuaries: usuaries.map(&:id),
draft: attribute?(:draft) ? draft.value : false
}
{ categories: categories.indexable_values } if attribute? :categories
indexable_front_matter[:categories] = categories.indexable_values
end
end
end end
# Devuelve un documento indexable en texto plano # Devuelve un documento indexable en texto plano

View file

@ -59,9 +59,7 @@ class PostPolicy
def resolve def resolve
return scope if scope&.first&.site&.usuarie? usuarie return scope if scope&.first&.site&.usuarie? usuarie
scope.select do |post| scope.by_usuarie(usuarie.id)
post.usuaries.include? usuarie
end
end end
end end
end end

View file

@ -3,7 +3,7 @@
@site.name, @site.name,
link_to(t('posts.index'), link_to(t('posts.index'),
site_posts_path(@site)), site_posts_path(@site)),
@category_name] @category]
%main.row %main.row
%aside.menu.col-md-3 %aside.menu.col-md-3
@ -14,15 +14,13 @@
%table.mb-3 %table.mb-3
- @site.layouts.each do |layout| - @site.layouts.each do |layout|
- next if layout.hidden? - next if layout.hidden?
- filter = params[:layout] == layout.value
%tr %tr
%th= layout.humanized_name %th= layout.humanized_name
%td.pl-3= link_to t('posts.add'), %td.pl-3= link_to t('posts.add'), new_site_post_path(@site, **@filter_params), class: 'badge badge-secondary'
new_site_post_path(@site, layout: layout.name), - if @filter_params[:layout] == layout.value
class: 'badge badge-secondary' %td= link_to t('posts.remove_filter'), site_posts_path(@site, **@filter_params.merge(layout: nil)), class: 'badge badge-primary'
%td= link_to t(filter ? 'posts.remove_filter' : 'posts.filter'), - else
site_posts_path(@site, layout: (filter ? nil : layout.value)), %td= link_to t('posts.filter'), site_posts_path(@site, **@filter_params.merge(layout: layout.value)), class: 'badge badge-secondary'
class: 'badge badge-' + (filter ? 'primary' : 'secondary')
- if policy(@site).edit? - if policy(@site).edit?
= link_to t('sites.edit.btn', site: @site.title), edit_site_path(@site), class: 'btn' = link_to t('sites.edit.btn', site: @site.title), edit_site_path(@site), class: 'btn'
@ -48,19 +46,24 @@
%section.col %section.col
= render 'layouts/flash' = render 'layouts/flash'
.d-flex.justify-content-between.align-items-center.pl-2-plus.pr-2-plus.mb-2
%form
- @filter_params.each do |param, value|
- next if param == 'q'
%input{ type: 'input', name: param, value: value }
.form-group.flex-grow-0.m-0
%input.form-control.border.border-magenta{ type: 'search', placeholder: 'Buscar', name: 'q', value: @q }
%input.sr-only{ type: 'submit' }
- if @site.locales.size > 1
%nav#locales
- @site.locales.each do |locale|
= link_to t("locales.#{locale}.name"), site_posts_path(@site, **@filter_params.merge(locale: locale)),
class: "mr-2 mt-2 mb-2 #{locale == @locale ? 'active font-weight-bold' : ''}"
- if @posts.empty? - if @posts.empty?
%h2= t('posts.none') %h2= t('posts.empty')
- else - else
= form_tag site_posts_reorder_path, method: :post do = form_tag site_posts_reorder_path, method: :post do
.d-flex.justify-content-between.align-items-center
-#
TODO: Pensar una interfaz mejor para cuando haya más de tres
idiomas
- unless @site.locales.length == 1
.locales
- @site.locales.each do |locale|
= link_to t("locales.#{locale}.name"), site_posts_path(@site, locale: locale),
class: "mr-2 mt-2 mb-2#{locale == @locale ? 'active font-weight-bold' : ''}"
%table.table{ data: { controller: 'reorder' } } %table.table{ data: { controller: 'reorder' } }
%caption.sr-only= t('posts.caption') %caption.sr-only= t('posts.caption')
%thead %thead
@ -76,6 +79,7 @@
%button.btn{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom') %button.btn{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom')
%tbody %tbody
- dir = t("locales.#{@locale}.dir") - dir = t("locales.#{@locale}.dir")
- size = @posts.size
- @posts.each_with_index do |post, i| - @posts.each_with_index do |post, i|
-# -#
TODO: Solo les usuaries cachean porque tenemos que separar TODO: Solo les usuaries cachean porque tenemos que separar
@ -84,45 +88,36 @@
TODO: Verificar qué pasa cuando se gestiona el sitio en TODO: Verificar qué pasa cuando se gestiona el sitio en
distintos idiomas a la vez distintos idiomas a la vez
- cache_if @usuarie, post do - cache_if @usuarie, post do
- checkbox_id = "checkbox-#{post.uuid.value}" - checkbox_id = "checkbox-#{post.id}"
%tr{ id: post.uuid.value, data: { target: 'reorder.row' } } %tr{ id: post.id, data: { target: 'reorder.row' } }
%td %td
.custom-control.custom-checkbox .custom-control.custom-checkbox
%input.custom-control-input{ id: checkbox_id, type: 'checkbox', autocomplete: 'off', data: { action: 'reorder#select' } } %input.custom-control-input{ id: checkbox_id, type: 'checkbox', autocomplete: 'off', data: { action: 'reorder#select' } }
%label.custom-control-label{ for: checkbox_id } %label.custom-control-label{ for: checkbox_id }
%span.sr-only= t('posts.reorder.select') %span.sr-only= t('posts.reorder.select')
-# Orden más alto es mayor prioridad -# Orden más alto es mayor prioridad
= hidden_field 'post[reorder]', post.uuid.value, = hidden_field 'post[reorder]', post.id,
value: @posts.length - i, value: size - i,
data: { reorder: true } data: { reorder: true }
%td.w-100{ class: dir } %td.w-100{ class: dir }
= link_to site_post_path(@site, post.id) do = link_to site_post_path(@site, post.path) do
%span{ lang: post.lang.value, dir: dir }= post.title.value %span{ lang: post.locale, dir: dir }= post.title
- if post.attributes.include? :draft - if post.front_matter['draft'].present?
- if post.draft.value %span.badge.badge-primary
%span.badge.badge-primary = post_label_t(:draft, post: post)
= post_label_t(:draft, post: post) - if post.front_matter['categories'].present?
- if post.attributes.include? :categories %br
- unless post.categories.value.empty? %small
%br - post.front_matter['categories'].each do |category|
%small = link_to site_posts_path(@site, **@filter_params.merge(category: category)) do
- (post.categories.respond_to?(:belongs_to) ? post.categories.belongs_to : post.categories.value).each do |c| %span{ lang: post.locale, dir: dir }= category
= link_to site_posts_path(@site, category: (c.respond_to?(:uuid) ? c.uuid.value : c)) do
%span{ lang: post.lang.value, dir: dir }= (c.respond_to?(:title) ? c.title.value : c)
%td %td
= post.date.value.strftime('%F') = post.created_at.strftime('%F')
%br/ %br/
- if post.attribute? :order = post.order
= post.order.value
%td %td
- if @usuarie || policy(post).edit? - if @usuarie || policy(post).edit?
= link_to t('posts.edit'), = link_to t('posts.edit'), edit_site_post_path(@site, post.path), class: 'btn btn-block'
edit_site_post_path(@site, post.id),
class: 'btn btn-block'
- if @usuarie || policy(post).destroy? - if @usuarie || policy(post).destroy?
= link_to t('posts.destroy'), = link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-block', method: :delete, data: { confirm: t('posts.confirm_destroy') }
site_post_path(@site, post.id),
class: 'btn btn-block',
method: :delete,
data: { confirm: t('posts.confirm_destroy') }

View file

@ -397,6 +397,7 @@ en:
en: 'English' en: 'English'
ar: 'Arabic' ar: 'Arabic'
posts: posts:
empty: "There are no results for those search parameters."
attribute_ro: attribute_ro:
file: file:
download: Download file download: Download file

View file

@ -459,6 +459,7 @@ es:
en: 'inglés' en: 'inglés'
ar: 'árabe' ar: 'árabe'
posts: posts:
empty: No hay artículos con estos parámetros de búsqueda.
caption: Lista de artículos caption: Lista de artículos
attribute_ro: attribute_ro:
file: file: