mirror of
https://github.com/astuto/astuto.git
synced 2025-12-15 19:27:52 +01:00
Add date filter to post list (#285)
This commit is contained in:
committed by
GitHub
parent
5221df1b2b
commit
d866246518
@@ -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,
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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}
|
||||||
|
|||||||
53
app/javascript/components/Board/DateFilter.tsx
Normal file
53
app/javascript/components/Board/DateFilter.tsx
Normal 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;
|
||||||
@@ -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 />;
|
||||||
@@ -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(
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user