mirror of
https://github.com/astuto/astuto.git
synced 2025-12-15 03:07:52 +01:00
Improve post list filter by status (#267)
This commit is contained in:
committed by
GitHub
parent
30b7b0f5f4
commit
a7d67652bf
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
:
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user