Add date filter to post list (#285)

This commit is contained in:
Riccardo Graziosi
2024-02-15 22:30:41 +01:00
committed by GitHub
parent 5221df1b2b
commit d866246518
11 changed files with 142 additions and 13 deletions

View File

@@ -74,6 +74,11 @@
font-weight: bold; font-weight: bold;
} }
.dateFilterBox {
label { @extend .m-0; }
#startDateFilter, #endDateFilter { @extend .mb-1; }
}
.postList { .postList {
@extend @extend
.d-flex, .d-flex,

View File

@@ -2,6 +2,9 @@ class PostsController < ApplicationController
before_action :authenticate_user!, only: [:create, :update, :destroy] before_action :authenticate_user!, only: [:create, :update, :destroy]
def index def index
start_date = params[:start_date] ? Date.parse(params[:start_date]) : Date.parse('1970-01-01')
end_date = params[:end_date] ? Date.parse(params[:end_date]) : Date.today
posts = Post posts = Post
.select( .select(
:id, :id,
@@ -17,6 +20,7 @@ class PostsController < ApplicationController
.left_outer_joins(:comments) .left_outer_joins(:comments)
.group('posts.id') .group('posts.id')
.where(board_id: params[:board_id] || Board.first.id) .where(board_id: params[:board_id] || Board.first.id)
.where(created_at: start_date.beginning_of_day..end_date.end_of_day)
.search_by_name_or_description(params[:search]) .search_by_name_or_description(params[:search])
.order_by(params[:sort_by]) .order_by(params[:sort_by])
.page(params[:page]) .page(params[:page])

View File

@@ -51,6 +51,7 @@ export const requestPosts = (
searchQuery: string, searchQuery: string,
postStatusIds: Array<number>, postStatusIds: Array<number>,
sortBy: SortByFilterValues, sortBy: SortByFilterValues,
date: { startDate: string; endDate: string },
): ThunkAction<void, State, null, Action<string>> => async (dispatch) => { ): ThunkAction<void, State, null, Action<string>> => async (dispatch) => {
dispatch(postsRequestStart()); dispatch(postsRequestStart());
@@ -68,6 +69,8 @@ export const requestPosts = (
} }
} }
if (sortBy) params += `&sort_by=${sortBy}`; if (sortBy) params += `&sort_by=${sortBy}`;
if (date.startDate) params += `&start_date=${date.startDate}`;
if (date.endDate) params += `&end_date=${date.endDate}`;
const response = await fetch(`/posts?${params}`); const response = await fetch(`/posts?${params}`);
const json = await response.json(); const json = await response.json();

View File

@@ -17,6 +17,13 @@ interface SetSortByFilterAction {
sortBy: SortByFilterValues; sortBy: SortByFilterValues;
} }
export const SET_DATE_FILTER = 'SET_DATE_FILTER';
interface SetDateFilterAction {
type: typeof SET_DATE_FILTER;
startDate: string;
endDate: string;
}
export const setSearchFilter = (searchQuery: string): SetSearchFilterAction => ({ export const setSearchFilter = (searchQuery: string): SetSearchFilterAction => ({
type: SET_SEARCH_FILTER, type: SET_SEARCH_FILTER,
@@ -33,8 +40,15 @@ export const setSortByFilter = (sortBy: SortByFilterValues): SetSortByFilterActi
sortBy, sortBy,
}); });
export const setDateFilter = (startDate: string, endDate: string): SetDateFilterAction => ({
type: SET_DATE_FILTER,
startDate,
endDate,
});
export type ChangeFiltersActionTypes = export type ChangeFiltersActionTypes =
SetSearchFilterAction | SetSearchFilterAction |
SetPostStatusFilterAction | SetPostStatusFilterAction |
SetSortByFilterAction; SetSortByFilterAction |
SetDateFilterAction;

View File

@@ -13,6 +13,7 @@ import { PostsState } from '../../reducers/postsReducer';
import { PostStatusesState } from '../../reducers/postStatusesReducer'; import { PostStatusesState } from '../../reducers/postStatusesReducer';
import SortByFilter from './SortByFilter'; import SortByFilter from './SortByFilter';
import { SortByFilterValues } from '../../actions/changeFilters'; import { SortByFilterValues } from '../../actions/changeFilters';
import DateFilter from './DateFilter';
interface Props { interface Props {
board: IBoard; board: IBoard;
@@ -29,11 +30,13 @@ interface Props {
searchQuery?: string, searchQuery?: string,
postStatusIds?: Array<number>, postStatusIds?: Array<number>,
sortBy?: SortByFilterValues, sortBy?: SortByFilterValues,
date?: { startDate: string; endDate: string },
): void; ): void;
requestPostStatuses(): void; requestPostStatuses(): void;
handleSearchFilterChange(searchQuery: string): void; handleSearchFilterChange(searchQuery: string): void;
handlePostStatusFilterChange(postStatusId: number): void; handlePostStatusFilterChange(postStatusId: number): void;
handleSortByFilterChange(sortBy: SortByFilterValues): void; handleSortByFilterChange(sortBy: SortByFilterValues): void;
handleDateFilterChange(startDate: string, endDate: string): void;
} }
class BoardP extends React.Component<Props> { class BoardP extends React.Component<Props> {
@@ -54,23 +57,31 @@ class BoardP extends React.Component<Props> {
const { sortBy } = this.props.posts.filters; const { sortBy } = this.props.posts.filters;
const prevSortBy = prevProps.posts.filters.sortBy; const prevSortBy = prevProps.posts.filters.sortBy;
const { startDate, endDate } = this.props.posts.filters.date;
const prevStartDate = prevProps.posts.filters.date.startDate;
const prevEndDate = prevProps.posts.filters.date.endDate;
const requestPostsWithFilters = () => (
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds, sortBy, { startDate, endDate })
);
// search filter changed // search filter changed
if (searchQuery !== prevSearchQuery) { if (searchQuery !== prevSearchQuery) {
if (this.searchFilterTimeoutId) clearInterval(this.searchFilterTimeoutId); if (this.searchFilterTimeoutId) clearInterval(this.searchFilterTimeoutId);
this.searchFilterTimeoutId = setTimeout(() => ( this.searchFilterTimeoutId = setTimeout(() => (
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds) requestPostsWithFilters()
), 500); ), 500);
} }
// post status filter changed // poststatus/sortby/date filter changed
if (postStatusIds.length !== prevPostStatusIds.length) { if (
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds); postStatusIds.length !== prevPostStatusIds.length ||
} sortBy !== prevSortBy ||
startDate !== prevStartDate ||
// sort by filter changed endDate !== prevEndDate
if (sortBy !== prevSortBy) { ) {
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds, sortBy); requestPostsWithFilters();
} }
} }
@@ -88,6 +99,7 @@ class BoardP extends React.Component<Props> {
handleSearchFilterChange, handleSearchFilterChange,
handlePostStatusFilterChange, handlePostStatusFilterChange,
handleSortByFilterChange, handleSortByFilterChange,
handleDateFilterChange,
} = this.props; } = this.props;
const { filters } = posts; const { filters } = posts;
@@ -105,10 +117,18 @@ class BoardP extends React.Component<Props> {
/> />
{ {
isPowerUser && isPowerUser &&
<>
<SortByFilter <SortByFilter
sortBy={filters.sortBy} sortBy={filters.sortBy}
handleChange={sortBy => handleSortByFilterChange(sortBy)} handleChange={sortBy => handleSortByFilterChange(sortBy)}
/> />
<DateFilter
startDate={filters.date.startDate}
endDate={filters.date.endDate}
handleChange={handleDateFilterChange}
/>
</>
} }
<PostStatusFilter <PostStatusFilter
postStatuses={postStatuses.items} postStatuses={postStatuses.items}

View File

@@ -0,0 +1,53 @@
import * as React from 'react';
import I18n from 'i18n-js';
import SidebarBox from '../common/SidebarBox';
import ActionLink from '../common/ActionLink';
import { ClearIcon } from '../common/Icons';
interface Props {
startDate: string;
endDate: string;
handleChange(startDate: string, endDate: string): void;
}
const DateFilter = ({ startDate, endDate, handleChange }: Props) => (
<SidebarBox title={I18n.t('board.filter_by_date_box.title')} customClass="dateFilterBox">
<label htmlFor="startDateFilter">
{ I18n.t('board.filter_by_date_box.from_label') }
</label>
<input
type="date"
value={startDate}
onChange={e => handleChange(e.target.value, endDate)}
id="startDateFilter"
className="form-control"
/>
<label htmlFor="endDateFilter">
{ I18n.t('board.filter_by_date_box.to_label') }
</label>
<input
type="date"
value={endDate}
onChange={e => handleChange(startDate, e.target.value)}
id="endDateFilter"
className="form-control"
/>
{
startDate || endDate ?
<ActionLink
onClick={() => handleChange('', '')}
icon={<ClearIcon />}
customClass="clearDateFilter"
>
{ I18n.t('common.buttons.clear') }
</ActionLink>
:
null
}
</SidebarBox>
);
export default DateFilter;

View File

@@ -6,7 +6,7 @@ import { FiEdit, FiDelete } from 'react-icons/fi';
import { ImCancelCircle } from 'react-icons/im'; import { ImCancelCircle } from 'react-icons/im';
import { TbLock, TbLockOpen } from 'react-icons/tb'; import { TbLock, TbLockOpen } from 'react-icons/tb';
import { MdContentCopy, MdDone, MdOutlineArrowBack } from 'react-icons/md'; import { MdContentCopy, MdDone, MdOutlineArrowBack } from 'react-icons/md';
import { GrTest } from 'react-icons/gr'; import { GrTest, GrClearOption } from 'react-icons/gr';
import { MdOutlineLibraryBooks } from "react-icons/md"; import { MdOutlineLibraryBooks } from "react-icons/md";
import { MdVerified } from "react-icons/md"; import { MdVerified } from "react-icons/md";
@@ -37,3 +37,5 @@ export const StaffIcon = () => (
<MdVerified /> <MdVerified />
</span> </span>
); );
export const ClearIcon = () => <GrClearOption />;

View File

@@ -7,6 +7,7 @@ import {
setPostStatusFilter, setPostStatusFilter,
SortByFilterValues, SortByFilterValues,
setSortByFilter, setSortByFilter,
setDateFilter,
} from '../actions/changeFilters'; } from '../actions/changeFilters';
import { State } from '../reducers/rootReducer'; import { State } from '../reducers/rootReducer';
@@ -25,8 +26,9 @@ const mapDispatchToProps = (dispatch: any) => ({
searchQuery: string = '', searchQuery: string = '',
postStatusIds: Array<number> = null, postStatusIds: Array<number> = null,
sortBy: SortByFilterValues = null, sortBy: SortByFilterValues = null,
date: { startDate: string; endDate: string } = { startDate: '', endDate: '' }
) { ) {
dispatch(requestPosts(boardId, page, searchQuery, postStatusIds, sortBy)); dispatch(requestPosts(boardId, page, searchQuery, postStatusIds, sortBy, date));
}, },
requestPostStatuses() { requestPostStatuses() {
@@ -43,7 +45,11 @@ const mapDispatchToProps = (dispatch: any) => ({
handleSortByFilterChange(sortBy: SortByFilterValues) { handleSortByFilterChange(sortBy: SortByFilterValues) {
dispatch(setSortByFilter(sortBy)); dispatch(setSortByFilter(sortBy));
} },
handleDateFilterChange(startDate: string, endDate: string) {
dispatch(setDateFilter(startDate, endDate));
},
}); });
export default connect( export default connect(

View File

@@ -4,18 +4,24 @@ import {
SET_POST_STATUS_FILTER, SET_POST_STATUS_FILTER,
SET_SORT_BY_FILTER, SET_SORT_BY_FILTER,
SortByFilterValues, SortByFilterValues,
SET_DATE_FILTER,
} from '../actions/changeFilters'; } from '../actions/changeFilters';
export interface FiltersState { export interface FiltersState {
searchQuery: string; searchQuery: string;
postStatusIds: Array<number>; postStatusIds: Array<number>;
sortBy: SortByFilterValues; sortBy: SortByFilterValues;
date: {
startDate?: string;
endDate?: string;
};
} }
const initialState: FiltersState = { const initialState: FiltersState = {
searchQuery: '', searchQuery: '',
postStatusIds: [], postStatusIds: [],
sortBy: 'newest', sortBy: 'newest',
date: { startDate: '', endDate: '' },
} }
const filtersReducer = ( const filtersReducer = (
@@ -43,6 +49,15 @@ const filtersReducer = (
sortBy: action.sortBy, sortBy: action.sortBy,
}; };
case SET_DATE_FILTER:
return {
...state,
date: {
startDate: action.startDate,
endDate: action.endDate,
},
};
default: default:
return state; return state;
} }

View File

@@ -26,6 +26,7 @@ import {
SET_SEARCH_FILTER, SET_SEARCH_FILTER,
SET_POST_STATUS_FILTER, SET_POST_STATUS_FILTER,
SET_SORT_BY_FILTER, SET_SORT_BY_FILTER,
SET_DATE_FILTER,
} from '../actions/changeFilters'; } from '../actions/changeFilters';
import { import {
@@ -95,6 +96,7 @@ const postsReducer = (
case SET_SEARCH_FILTER: case SET_SEARCH_FILTER:
case SET_POST_STATUS_FILTER: case SET_POST_STATUS_FILTER:
case SET_SORT_BY_FILTER: case SET_SORT_BY_FILTER:
case SET_DATE_FILTER:
return { return {
...state, ...state,
filters: filtersReducer(state.filters, action), filters: filtersReducer(state.filters, action),

View File

@@ -55,6 +55,7 @@ en:
confirm: 'Confirm' confirm: 'Confirm'
back: 'Back' back: 'Back'
test: 'Test' test: 'Test'
clear: 'Clear'
datetime: datetime:
now: 'just now' now: 'just now'
minutes: minutes:
@@ -113,6 +114,10 @@ en:
newest: 'Newest' newest: 'Newest'
most_voted: 'Most voted' most_voted: 'Most voted'
oldest: 'Oldest' oldest: 'Oldest'
filter_by_date_box:
title: 'Filter by date'
from_label: 'From'
to_label: 'To'
posts_list: posts_list:
empty: 'There are no posts' empty: 'There are no posts'
post: post: