Improve post list filter by status (#267)

This commit is contained in:
Riccardo Graziosi
2024-01-25 14:50:39 +01:00
committed by GitHub
parent 30b7b0f5f4
commit a7d67652bf
9 changed files with 59 additions and 36 deletions

View File

@@ -48,7 +48,7 @@
height: 40px;
&:hover {
filter: brightness(90%);
filter: brightness(85%);
cursor: pointer;
}
@@ -57,6 +57,10 @@
}
}
.postStatusListItemSelected {
filter: brightness(85%);
}
.resetFilter {
@extend
.flex-grow-0,
@@ -67,6 +71,7 @@
width: 30px;
height: 30px;
font-weight: bold;
}
.postList {

View File

@@ -16,10 +16,13 @@ class PostsController < ApplicationController
.left_outer_joins(:likes)
.left_outer_joins(:comments)
.group('posts.id')
.where(filter_params)
.where(board_id: params[:board_id] || Board.first.id)
.search_by_name_or_description(params[:search])
.order('hotness DESC')
.page(params[:page])
# apply post status filter if present
posts = posts.where(post_status_id: params[:post_status_ids].map { |id| id == "0" ? nil : id }) if params[:post_status_ids].present?
render json: posts
end
@@ -107,15 +110,6 @@ class PostsController < ApplicationController
end
private
def filter_params
defaults = Board.first ? { board_id: Board.first.id } : {}
params
.permit(:board_id, :post_status_id, :page, :search)
.with_defaults(defaults)
.except(:page, :search)
end
def post_create_params
params

View File

@@ -48,7 +48,7 @@ export const requestPosts = (
boardId: number,
page: number,
searchQuery: string,
postStatusId: number,
postStatusIds: Array<number>,
): ThunkAction<void, State, null, Action<string>> => async (dispatch) => {
dispatch(postsRequestStart());
@@ -57,7 +57,14 @@ export const requestPosts = (
params += `page=${page}`;
params += `&board_id=${boardId}`;
if (searchQuery) params += `&search=${searchQuery}`;
if (postStatusId) params += `&post_status_id=${postStatusId}`;
if (postStatusIds) {
params += '&';
for (let i = 0; i < postStatusIds.length; i++) {
params += `post_status_ids[]=${postStatusIds[i]}`;
if (i !== postStatusIds.length-1) params += '&';
}
}
const response = await fetch(`/posts?${params}`);
const json = await response.json();

View File

@@ -25,7 +25,7 @@ interface Props {
boardId: number,
page?: number,
searchQuery?: string,
postStatusId?: number,
postStatusIds?: Array<number>,
): void;
requestPostStatuses(): void;
handleSearchFilterChange(searchQuery: string): void;
@@ -44,21 +44,21 @@ class BoardP extends React.Component<Props> {
const { searchQuery } = this.props.posts.filters;
const prevSearchQuery = prevProps.posts.filters.searchQuery;
const { postStatusId } = this.props.posts.filters;
const prevPostStatusId = prevProps.posts.filters.postStatusId;
const { postStatusIds } = this.props.posts.filters;
const prevPostStatusIds = prevProps.posts.filters.postStatusIds;
// search filter changed
if (searchQuery !== prevSearchQuery) {
if (this.searchFilterTimeoutId) clearInterval(this.searchFilterTimeoutId);
this.searchFilterTimeoutId = setTimeout(() => (
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusId)
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds)
), 500);
}
// post status filter changed
if (postStatusId !== prevPostStatusId) {
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusId);
if (postStatusIds.length !== prevPostStatusIds.length) {
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds);
}
}
@@ -95,7 +95,7 @@ class BoardP extends React.Component<Props> {
areLoading={postStatuses.areLoading}
error={postStatuses.error}
currentFilter={filters.postStatusId}
currentFilter={filters.postStatusIds}
handleFilterClick={handlePostStatusFilterChange}
/>
</Sidebar>
@@ -113,7 +113,7 @@ class BoardP extends React.Component<Props> {
posts.areLoading ?
null
:
requestPosts(board.id, posts.page + 1, filters.searchQuery, filters.postStatusId)
requestPosts(board.id, posts.page + 1, filters.searchQuery, filters.postStatusIds)
}
isLoggedIn={isLoggedIn}

View File

@@ -14,7 +14,7 @@ interface Props {
error: string;
handleFilterClick(postStatusId: number): void;
currentFilter: number;
currentFilter: Array<number>;
}
const PostStatusFilter = ({
@@ -33,13 +33,19 @@ const PostStatusFilter = ({
color={postStatus.color}
handleClick={() => handleFilterClick(postStatus.id)}
isCurrentFilter={postStatus.id === currentFilter}
handleResetFilter={() => handleFilterClick(0)}
isCurrentFilter={currentFilter.includes(postStatus.id)}
key={i}
/>
))
}
<PostStatusListItem
name={I18n.t('common.no_status')}
color='black'
handleClick={() => handleFilterClick(0)}
isCurrentFilter={currentFilter.includes(0)}
/>
{ areLoading ? <Spinner /> : null }
{ error ? <DangerText>{error}</DangerText> : null }
</SidebarBox>

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import PostStatusLabel from '../common/PostStatusLabel';
import Button from '../common/Button';
import { CancelIcon } from '../common/Icons';
interface Props {
name: string;
@@ -9,7 +10,6 @@ interface Props {
handleClick(): void;
isCurrentFilter: boolean;
handleResetFilter(): void;
}
const PostStatusListItem = ({
@@ -17,19 +17,18 @@ const PostStatusListItem = ({
color,
handleClick,
isCurrentFilter,
handleResetFilter,
}: Props) => (
<div className={
"postStatusListItemContainer " + `postStatus${name.replace(/ /g, '')}`
}>
<a onClick={handleClick} className="postStatusListItemLink">
<div className="postStatusListItem">
<div className={`postStatusListItem${isCurrentFilter ? ' postStatusListItemSelected' : ''}`}>
<PostStatusLabel name={name} color={color} />
</div>
</a>
{
isCurrentFilter ?
<Button onClick={handleResetFilter} className="resetFilter" outline>
<Button onClick={handleClick} className="resetFilter" outline>
X
</Button>
:

View File

@@ -17,8 +17,8 @@ const mapStateToProps = (state: State) => ({
});
const mapDispatchToProps = (dispatch: any) => ({
requestPosts(boardId: number, page: number = 1, searchQuery: string = '', postStatusId: number = null) {
dispatch(requestPosts(boardId, page, searchQuery, postStatusId));
requestPosts(boardId: number, page: number = 1, searchQuery: string = '', postStatusIds: Array<number> = null) {
dispatch(requestPosts(boardId, page, searchQuery, postStatusIds));
},
requestPostStatuses() {

View File

@@ -6,12 +6,12 @@ import {
export interface FiltersState {
searchQuery: string;
postStatusId: number;
postStatusIds: Array<number>;
}
const initialState: FiltersState = {
searchQuery: '',
postStatusId: null,
postStatusIds: [],
}
const filtersReducer = (
@@ -28,7 +28,9 @@ const filtersReducer = (
case SET_POST_STATUS_FILTER:
return {
...state,
postStatusId: action.postStatusId,
postStatusIds: state.postStatusIds.includes(action.postStatusId)
? state.postStatusIds.filter(id => id !== action.postStatusId)
: [...state.postStatusIds, action.postStatusId],
};
default:

View File

@@ -131,14 +131,24 @@ feature 'board', type: :system, js: true do
expect(page).to have_no_content(/#{post2.title}/i)
expect(page).to have_no_content(/#{post3.title}/i)
# you can also clear the filter
# you can also filter by multiple statuses
within sidebar do
find(reset_filter).click
selector = ".postStatus#{post_status2.name.gsub(' ', '')}"
find(selector).click
end
expect(page).to have_content(/#{post1.title}/i)
expect(page).to have_content(/#{post2.title}/i)
expect(page).to have_content(/#{post3.title}/i)
expect(page).to have_no_content(/#{post3.title}/i)
# you can also clear the filter
within sidebar do
find(reset_filter, match: :first).click
end
expect(page).to have_no_content(/#{post1.title}/i)
expect(page).to have_content(/#{post2.title}/i)
expect(page).to have_no_content(/#{post3.title}/i)
end
it 'enables users to search posts by title and description' do