Merge pull request #1538 from gitroomhq/feat/list-view-post-filters
Add state filter (all/scheduled/draft/published) to list view
This commit is contained in:
commit
6fc51da7e3
5 changed files with 85 additions and 12 deletions
|
|
@ -26,6 +26,8 @@ import { expandPostsList, expandPosts } from '@gitroom/helpers/utils/posts.list.
|
|||
extend(isoWeek);
|
||||
extend(weekOfYear);
|
||||
|
||||
export type ListStateFilter = 'all' | 'scheduled' | 'draft' | 'published';
|
||||
|
||||
export const CalendarContext = createContext({
|
||||
startDate: newDayjs().startOf('isoWeek').format('YYYY-MM-DD'),
|
||||
endDate: newDayjs().endOf('isoWeek').format('YYYY-MM-DD'),
|
||||
|
|
@ -78,6 +80,10 @@ export const CalendarContext = createContext({
|
|||
setListPage: (page: number) => {
|
||||
/** empty **/
|
||||
},
|
||||
listState: 'all' as ListStateFilter,
|
||||
setListState: (state: ListStateFilter) => {
|
||||
/** empty **/
|
||||
},
|
||||
});
|
||||
|
||||
export interface Integrations {
|
||||
|
|
@ -144,6 +150,11 @@ export const CalendarWeekProvider: FC<{
|
|||
|
||||
// List view state
|
||||
const [listPage, setListPage] = useState(0);
|
||||
const [listState, setListStateRaw] = useState<ListStateFilter>('all');
|
||||
const setListState = useCallback((next: ListStateFilter) => {
|
||||
setListStateRaw(next);
|
||||
setListPage(0);
|
||||
}, []);
|
||||
|
||||
// Initialize with current date range based on URL params or defaults
|
||||
const initStartDate = searchParams.get('startDate');
|
||||
|
|
@ -190,8 +201,9 @@ export const CalendarWeekProvider: FC<{
|
|||
page: listPage.toString(),
|
||||
limit: '100',
|
||||
customer: filters?.customer?.toString() || '',
|
||||
state: listState,
|
||||
}).toString();
|
||||
}, [listPage, filters.customer]);
|
||||
}, [listPage, filters.customer, listState]);
|
||||
|
||||
const loadListData = useCallback(async () => {
|
||||
const response = await fetch(`/posts/list?${listParams}`);
|
||||
|
|
@ -341,6 +353,8 @@ export const CalendarWeekProvider: FC<{
|
|||
listPage,
|
||||
listTotalPages,
|
||||
setListPage,
|
||||
listState,
|
||||
setListState,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -493,7 +493,15 @@ export const MonthView = () => {
|
|||
export const ListView = () => {
|
||||
const t = useT();
|
||||
const user = useUser();
|
||||
const { integrations, loading, listPosts } = useCalendar();
|
||||
const { integrations, loading, listPosts, listState } = useCalendar();
|
||||
const emptyMessage =
|
||||
listState === 'scheduled'
|
||||
? t('no_upcoming_posts', 'No upcoming posts scheduled')
|
||||
: listState === 'draft'
|
||||
? t('no_draft_posts', 'No draft posts')
|
||||
: listState === 'published'
|
||||
? t('no_published_posts', 'No published posts')
|
||||
: t('no_posts', 'No posts');
|
||||
|
||||
// Use shared post actions hook
|
||||
const { editPost, deletePost, copyDebugJson, openStatistics, openMissingRelease } = usePostActions();
|
||||
|
|
@ -522,9 +530,7 @@ export const ListView = () => {
|
|||
if (listPosts.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col flex-1 items-center justify-center">
|
||||
<div className="text-textColor text-[16px]">
|
||||
{t('no_upcoming_posts', 'No upcoming posts scheduled')}
|
||||
</div>
|
||||
<div className="text-textColor text-[16px]">{emptyMessage}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useCalendar } from '@gitroom/frontend/components/launches/calendar.context';
|
||||
import { useCalendar, ListStateFilter } from '@gitroom/frontend/components/launches/calendar.context';
|
||||
import clsx from 'clsx';
|
||||
import dayjs from 'dayjs';
|
||||
import { useCallback } from 'react';
|
||||
|
|
@ -259,6 +259,21 @@ export const Filters = () => {
|
|||
|
||||
const isListView = calendar.display === 'list';
|
||||
|
||||
const setListStateFilter = useCallback(
|
||||
(next: ListStateFilter) => () => {
|
||||
if (calendar.listState === next) return;
|
||||
calendar.setListState(next);
|
||||
},
|
||||
[calendar]
|
||||
);
|
||||
|
||||
const listStateOptions: { value: ListStateFilter; label: string }[] = [
|
||||
{ value: 'all', label: t('all', 'All') },
|
||||
{ value: 'scheduled', label: t('scheduled', 'Scheduled') },
|
||||
{ value: 'draft', label: t('draft', 'Draft') },
|
||||
{ value: 'published', label: t('published', 'Published') },
|
||||
];
|
||||
|
||||
const previousPage = useCallback(() => {
|
||||
if (calendar.listPage > 0) {
|
||||
calendar.setListPage(calendar.listPage - 1);
|
||||
|
|
@ -393,6 +408,21 @@ export const Filters = () => {
|
|||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500]">
|
||||
{listStateOptions.map((option) => (
|
||||
<div
|
||||
key={option.value}
|
||||
onClick={setListStateFilter(option.value)}
|
||||
className={clsx(
|
||||
'pt-[6px] pb-[5px] cursor-pointer min-w-[80px] px-[12px] text-center rounded-[6px]',
|
||||
calendar.listState === option.value &&
|
||||
'text-textItemFocused bg-boxFocused'
|
||||
)}
|
||||
>
|
||||
{option.label}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex-1" />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -224,6 +224,26 @@ export class PostsRepository {
|
|||
const limit = query.limit || 20;
|
||||
const skip = page * limit;
|
||||
|
||||
const stateFilter = query.state || 'all';
|
||||
const stateAndDate =
|
||||
stateFilter === 'scheduled'
|
||||
? {
|
||||
state: State.QUEUE,
|
||||
publishDate: { gte: dayjs.utc().toDate() },
|
||||
}
|
||||
: stateFilter === 'draft'
|
||||
? { state: State.DRAFT }
|
||||
: stateFilter === 'published'
|
||||
? { state: State.PUBLISHED }
|
||||
: {
|
||||
state: {
|
||||
in: [State.QUEUE, State.DRAFT, State.PUBLISHED, State.ERROR],
|
||||
},
|
||||
};
|
||||
|
||||
const orderDirection: 'asc' | 'desc' =
|
||||
stateFilter === 'published' ? 'desc' : 'asc';
|
||||
|
||||
const where = {
|
||||
AND: [
|
||||
{
|
||||
|
|
@ -233,12 +253,8 @@ export class PostsRepository {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
publishDate: {
|
||||
gte: dayjs.utc().toDate(),
|
||||
},
|
||||
},
|
||||
],
|
||||
...stateAndDate,
|
||||
deletedAt: null as Date | null,
|
||||
parentPostId: null as string | null,
|
||||
intervalInDays: null as number | null,
|
||||
|
|
@ -257,7 +273,7 @@ export class PostsRepository {
|
|||
skip,
|
||||
take: limit,
|
||||
orderBy: {
|
||||
publishDate: 'asc',
|
||||
publishDate: orderDirection,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,12 @@ import {
|
|||
IsNumber,
|
||||
Min,
|
||||
Max,
|
||||
IsIn,
|
||||
} from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
|
||||
export type PostListStateFilter = 'all' | 'scheduled' | 'draft' | 'published';
|
||||
|
||||
export class GetPostsListDto {
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
|
|
@ -24,4 +27,8 @@ export class GetPostsListDto {
|
|||
@IsOptional()
|
||||
@IsString()
|
||||
customer?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['all', 'scheduled', 'draft', 'published'])
|
||||
state?: PostListStateFilter = 'all';
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue