From d866246518705beb765777c6719c0b5e88c95118 Mon Sep 17 00:00:00 2001 From: Riccardo Graziosi <31478034+riggraz@users.noreply.github.com> Date: Thu, 15 Feb 2024 22:30:41 +0100 Subject: [PATCH] Add date filter to post list (#285) --- app/assets/stylesheets/components/Board.scss | 5 ++ app/controllers/posts_controller.rb | 4 ++ app/javascript/actions/Post/requestPosts.ts | 3 ++ app/javascript/actions/changeFilters.ts | 16 +++++- app/javascript/components/Board/BoardP.tsx | 38 +++++++++---- .../components/Board/DateFilter.tsx | 53 +++++++++++++++++++ app/javascript/components/common/Icons.tsx | 4 +- app/javascript/containers/Board.tsx | 10 +++- app/javascript/reducers/filtersReducer.ts | 15 ++++++ app/javascript/reducers/postsReducer.ts | 2 + config/locales/en.yml | 5 ++ 11 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 app/javascript/components/Board/DateFilter.tsx diff --git a/app/assets/stylesheets/components/Board.scss b/app/assets/stylesheets/components/Board.scss index b6b54d52..2c5ea4a9 100644 --- a/app/assets/stylesheets/components/Board.scss +++ b/app/assets/stylesheets/components/Board.scss @@ -74,6 +74,11 @@ font-weight: bold; } + .dateFilterBox { + label { @extend .m-0; } + #startDateFilter, #endDateFilter { @extend .mb-1; } + } + .postList { @extend .d-flex, diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 5f68d420..4860a652 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -2,6 +2,9 @@ class PostsController < ApplicationController before_action :authenticate_user!, only: [:create, :update, :destroy] 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 .select( :id, @@ -17,6 +20,7 @@ class PostsController < ApplicationController .left_outer_joins(:comments) .group('posts.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]) .order_by(params[:sort_by]) .page(params[:page]) diff --git a/app/javascript/actions/Post/requestPosts.ts b/app/javascript/actions/Post/requestPosts.ts index cefc2534..a2f93051 100644 --- a/app/javascript/actions/Post/requestPosts.ts +++ b/app/javascript/actions/Post/requestPosts.ts @@ -51,6 +51,7 @@ export const requestPosts = ( searchQuery: string, postStatusIds: Array, sortBy: SortByFilterValues, + date: { startDate: string; endDate: string }, ): ThunkAction> => async (dispatch) => { dispatch(postsRequestStart()); @@ -68,6 +69,8 @@ export const requestPosts = ( } } 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 json = await response.json(); diff --git a/app/javascript/actions/changeFilters.ts b/app/javascript/actions/changeFilters.ts index 8980555b..66ea8c24 100644 --- a/app/javascript/actions/changeFilters.ts +++ b/app/javascript/actions/changeFilters.ts @@ -17,6 +17,13 @@ interface SetSortByFilterAction { 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 => ({ type: SET_SEARCH_FILTER, @@ -33,8 +40,15 @@ export const setSortByFilter = (sortBy: SortByFilterValues): SetSortByFilterActi sortBy, }); +export const setDateFilter = (startDate: string, endDate: string): SetDateFilterAction => ({ + type: SET_DATE_FILTER, + startDate, + endDate, +}); + export type ChangeFiltersActionTypes = SetSearchFilterAction | SetPostStatusFilterAction | - SetSortByFilterAction; \ No newline at end of file + SetSortByFilterAction | + SetDateFilterAction; \ No newline at end of file diff --git a/app/javascript/components/Board/BoardP.tsx b/app/javascript/components/Board/BoardP.tsx index be85bd9d..233fb9af 100644 --- a/app/javascript/components/Board/BoardP.tsx +++ b/app/javascript/components/Board/BoardP.tsx @@ -13,6 +13,7 @@ import { PostsState } from '../../reducers/postsReducer'; import { PostStatusesState } from '../../reducers/postStatusesReducer'; import SortByFilter from './SortByFilter'; import { SortByFilterValues } from '../../actions/changeFilters'; +import DateFilter from './DateFilter'; interface Props { board: IBoard; @@ -29,11 +30,13 @@ interface Props { searchQuery?: string, postStatusIds?: Array, sortBy?: SortByFilterValues, + date?: { startDate: string; endDate: string }, ): void; requestPostStatuses(): void; handleSearchFilterChange(searchQuery: string): void; handlePostStatusFilterChange(postStatusId: number): void; handleSortByFilterChange(sortBy: SortByFilterValues): void; + handleDateFilterChange(startDate: string, endDate: string): void; } class BoardP extends React.Component { @@ -54,23 +57,31 @@ class BoardP extends React.Component { const { sortBy } = this.props.posts.filters; 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 if (searchQuery !== prevSearchQuery) { if (this.searchFilterTimeoutId) clearInterval(this.searchFilterTimeoutId); this.searchFilterTimeoutId = setTimeout(() => ( - this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds) + requestPostsWithFilters() ), 500); } - // post status filter changed - if (postStatusIds.length !== prevPostStatusIds.length) { - this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds); - } - - // sort by filter changed - if (sortBy !== prevSortBy) { - this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds, sortBy); + // poststatus/sortby/date filter changed + if ( + postStatusIds.length !== prevPostStatusIds.length || + sortBy !== prevSortBy || + startDate !== prevStartDate || + endDate !== prevEndDate + ) { + requestPostsWithFilters(); } } @@ -88,6 +99,7 @@ class BoardP extends React.Component { handleSearchFilterChange, handlePostStatusFilterChange, handleSortByFilterChange, + handleDateFilterChange, } = this.props; const { filters } = posts; @@ -105,10 +117,18 @@ class BoardP extends React.Component { /> { isPowerUser && + <> handleSortByFilterChange(sortBy)} /> + + + } ( + + + handleChange(e.target.value, endDate)} + id="startDateFilter" + className="form-control" + /> + + + handleChange(startDate, e.target.value)} + id="endDateFilter" + className="form-control" + /> + + { + startDate || endDate ? + handleChange('', '')} + icon={} + customClass="clearDateFilter" + > + { I18n.t('common.buttons.clear') } + + : + null + } + +); + +export default DateFilter; \ No newline at end of file diff --git a/app/javascript/components/common/Icons.tsx b/app/javascript/components/common/Icons.tsx index 6d8885a8..f0510978 100644 --- a/app/javascript/components/common/Icons.tsx +++ b/app/javascript/components/common/Icons.tsx @@ -6,7 +6,7 @@ import { FiEdit, FiDelete } from 'react-icons/fi'; import { ImCancelCircle } from 'react-icons/im'; import { TbLock, TbLockOpen } from 'react-icons/tb'; 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 { MdVerified } from "react-icons/md"; @@ -37,3 +37,5 @@ export const StaffIcon = () => ( ); + +export const ClearIcon = () => ; \ No newline at end of file diff --git a/app/javascript/containers/Board.tsx b/app/javascript/containers/Board.tsx index 7fccc019..7375ea27 100644 --- a/app/javascript/containers/Board.tsx +++ b/app/javascript/containers/Board.tsx @@ -7,6 +7,7 @@ import { setPostStatusFilter, SortByFilterValues, setSortByFilter, + setDateFilter, } from '../actions/changeFilters'; import { State } from '../reducers/rootReducer'; @@ -25,8 +26,9 @@ const mapDispatchToProps = (dispatch: any) => ({ searchQuery: string = '', postStatusIds: Array = 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() { @@ -43,7 +45,11 @@ const mapDispatchToProps = (dispatch: any) => ({ handleSortByFilterChange(sortBy: SortByFilterValues) { dispatch(setSortByFilter(sortBy)); - } + }, + + handleDateFilterChange(startDate: string, endDate: string) { + dispatch(setDateFilter(startDate, endDate)); + }, }); export default connect( diff --git a/app/javascript/reducers/filtersReducer.ts b/app/javascript/reducers/filtersReducer.ts index 3b018769..3a5418ae 100644 --- a/app/javascript/reducers/filtersReducer.ts +++ b/app/javascript/reducers/filtersReducer.ts @@ -4,18 +4,24 @@ import { SET_POST_STATUS_FILTER, SET_SORT_BY_FILTER, SortByFilterValues, + SET_DATE_FILTER, } from '../actions/changeFilters'; export interface FiltersState { searchQuery: string; postStatusIds: Array; sortBy: SortByFilterValues; + date: { + startDate?: string; + endDate?: string; + }; } const initialState: FiltersState = { searchQuery: '', postStatusIds: [], sortBy: 'newest', + date: { startDate: '', endDate: '' }, } const filtersReducer = ( @@ -43,6 +49,15 @@ const filtersReducer = ( sortBy: action.sortBy, }; + case SET_DATE_FILTER: + return { + ...state, + date: { + startDate: action.startDate, + endDate: action.endDate, + }, + }; + default: return state; } diff --git a/app/javascript/reducers/postsReducer.ts b/app/javascript/reducers/postsReducer.ts index a1df2d7d..25ba190c 100644 --- a/app/javascript/reducers/postsReducer.ts +++ b/app/javascript/reducers/postsReducer.ts @@ -26,6 +26,7 @@ import { SET_SEARCH_FILTER, SET_POST_STATUS_FILTER, SET_SORT_BY_FILTER, + SET_DATE_FILTER, } from '../actions/changeFilters'; import { @@ -95,6 +96,7 @@ const postsReducer = ( case SET_SEARCH_FILTER: case SET_POST_STATUS_FILTER: case SET_SORT_BY_FILTER: + case SET_DATE_FILTER: return { ...state, filters: filtersReducer(state.filters, action), diff --git a/config/locales/en.yml b/config/locales/en.yml index 1591d99c..777d3e9e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -55,6 +55,7 @@ en: confirm: 'Confirm' back: 'Back' test: 'Test' + clear: 'Clear' datetime: now: 'just now' minutes: @@ -113,6 +114,10 @@ en: newest: 'Newest' most_voted: 'Most voted' oldest: 'Oldest' + filter_by_date_box: + title: 'Filter by date' + from_label: 'From' + to_label: 'To' posts_list: empty: 'There are no posts' post: