feat(MyTasks): allow filtering by task complete status
This commit is contained in:
151
frontend/src/MyTasks/MyTasksStatus.tsx
Normal file
151
frontend/src/MyTasks/MyTasksStatus.tsx
Normal file
@ -0,0 +1,151 @@
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Checkmark } from 'shared/icons';
|
||||
import { TaskStatusFilter, TaskStatus, TaskSince } from 'shared/components/Lists';
|
||||
import { MyTasksStatus } from 'shared/generated/graphql';
|
||||
import { Popup } from 'shared/components/PopupMenu';
|
||||
|
||||
export const ActionsList = styled.ul`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export const ActionExtraMenuContainer = styled.div`
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: -4px;
|
||||
padding-left: 2px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const ActionItem = styled.li`
|
||||
position: relative;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
&:hover {
|
||||
background: ${props => props.theme.colors.primary};
|
||||
}
|
||||
&:hover ${ActionExtraMenuContainer} {
|
||||
visibility: visible;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ActionTitle = styled.span`
|
||||
margin-left: 20px;
|
||||
`;
|
||||
|
||||
export const ActionExtraMenu = styled.ul`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
padding: 5px;
|
||||
padding-top: 8px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 5px 25px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
color: #c2c6dc;
|
||||
background: #262c49;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-color: #414561;
|
||||
`;
|
||||
|
||||
export const ActionExtraMenuItem = styled.li`
|
||||
position: relative;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
&:hover {
|
||||
background: rgb(${props => props.theme.colors.primary});
|
||||
}
|
||||
`;
|
||||
const ActionExtraMenuSeparator = styled.li`
|
||||
color: ${props => props.theme.colors.text.primary};
|
||||
font-size: 12px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
`;
|
||||
|
||||
const ActiveIcon = styled(Checkmark)`
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
type MyTasksStatusProps = {
|
||||
status: MyTasksStatus;
|
||||
onChangeStatus: (status: MyTasksStatus) => void;
|
||||
};
|
||||
|
||||
const MyTasksStatusPopup: React.FC<MyTasksStatusProps> = ({ status: initialStatus, onChangeStatus }) => {
|
||||
const [status, setStatus] = useState(initialStatus);
|
||||
const handleStatusChange = (f: MyTasksStatus) => {
|
||||
setStatus(f);
|
||||
onChangeStatus(f);
|
||||
};
|
||||
return (
|
||||
<Popup tab={0} title={null}>
|
||||
<ActionsList>
|
||||
<ActionItem onClick={() => handleStatusChange(MyTasksStatus.Incomplete)}>
|
||||
{status === MyTasksStatus.Incomplete && <ActiveIcon width={12} height={12} />}
|
||||
<ActionTitle>Incomplete Tasks</ActionTitle>
|
||||
</ActionItem>
|
||||
<ActionItem>
|
||||
{status !== MyTasksStatus.Incomplete && status !== MyTasksStatus.All && <ActiveIcon width={12} height={12} />}
|
||||
<ActionTitle>Compelete Tasks</ActionTitle>
|
||||
<ActionExtraMenuContainer>
|
||||
<ActionExtraMenu>
|
||||
<ActionExtraMenuItem onClick={() => handleStatusChange(MyTasksStatus.CompleteAll)}>
|
||||
{status === MyTasksStatus.CompleteAll && <ActiveIcon width={12} height={12} />}
|
||||
<ActionTitle>All completed tasks</ActionTitle>
|
||||
</ActionExtraMenuItem>
|
||||
<ActionExtraMenuSeparator>Marked complete since</ActionExtraMenuSeparator>
|
||||
<ActionExtraMenuItem onClick={() => handleStatusChange(MyTasksStatus.CompleteToday)}>
|
||||
{status === MyTasksStatus.CompleteToday && <ActiveIcon width={12} height={12} />}
|
||||
<ActionTitle>Today</ActionTitle>
|
||||
</ActionExtraMenuItem>
|
||||
<ActionExtraMenuItem onClick={() => handleStatusChange(MyTasksStatus.CompleteYesterday)}>
|
||||
{status === MyTasksStatus.CompleteYesterday && <ActiveIcon width={12} height={12} />}
|
||||
|
||||
<ActionTitle>Yesterday</ActionTitle>
|
||||
</ActionExtraMenuItem>
|
||||
<ActionExtraMenuItem onClick={() => handleStatusChange(MyTasksStatus.CompleteOneWeek)}>
|
||||
{status === MyTasksStatus.CompleteOneWeek && <ActiveIcon width={12} height={12} />}
|
||||
<ActionTitle>1 week</ActionTitle>
|
||||
</ActionExtraMenuItem>
|
||||
<ActionExtraMenuItem onClick={() => handleStatusChange(MyTasksStatus.CompleteTwoWeek)}>
|
||||
{status === MyTasksStatus.CompleteTwoWeek && <ActiveIcon width={12} height={12} />}
|
||||
<ActionTitle>2 weeks</ActionTitle>
|
||||
</ActionExtraMenuItem>
|
||||
<ActionExtraMenuItem onClick={() => handleStatusChange(MyTasksStatus.CompleteThreeWeek)}>
|
||||
{status === MyTasksStatus.CompleteThreeWeek && <ActiveIcon width={12} height={12} />}
|
||||
<ActionTitle>3 weeks</ActionTitle>
|
||||
</ActionExtraMenuItem>
|
||||
</ActionExtraMenu>
|
||||
</ActionExtraMenuContainer>
|
||||
</ActionItem>
|
||||
<ActionItem onClick={() => handleStatusChange(MyTasksStatus.All)}>
|
||||
{status === MyTasksStatus.All && <ActiveIcon width={12} height={12} />}
|
||||
<ActionTitle>All Tasks</ActionTitle>
|
||||
</ActionItem>
|
||||
</ActionsList>
|
||||
</Popup>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyTasksStatusPopup;
|
@ -19,7 +19,7 @@ import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||
import updateApolloCache from 'shared/utils/cache';
|
||||
import produce from 'immer';
|
||||
import NOOP from 'shared/utils/noop';
|
||||
import { Sort, Cogs, CaretDown, CheckCircle, CaretRight } from 'shared/icons';
|
||||
import { Sort, Cogs, CaretDown, CheckCircle, CaretRight, CheckCircleOutline } from 'shared/icons';
|
||||
import Select from 'react-select';
|
||||
import { editorColourStyles } from 'shared/components/Select';
|
||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||
@ -27,12 +27,36 @@ import DueDateManager from 'shared/components/DueDateManager';
|
||||
import dayjs from 'dayjs';
|
||||
import useStickyState from 'shared/hooks/useStickyState';
|
||||
import MyTasksSortPopup from './MyTasksSort';
|
||||
import MyTasksStatusPopup from './MyTasksStatus';
|
||||
import TaskEntry from './TaskEntry';
|
||||
|
||||
type TaskRouteProps = {
|
||||
taskID: string;
|
||||
};
|
||||
|
||||
function prettyStatus(status: MyTasksStatus) {
|
||||
switch (status) {
|
||||
case MyTasksStatus.All:
|
||||
return 'All tasks';
|
||||
case MyTasksStatus.Incomplete:
|
||||
return 'Incomplete tasks';
|
||||
case MyTasksStatus.CompleteAll:
|
||||
return 'All completed tasks';
|
||||
case MyTasksStatus.CompleteToday:
|
||||
return 'Completed tasks: today';
|
||||
case MyTasksStatus.CompleteYesterday:
|
||||
return 'Completed tasks: yesterday';
|
||||
case MyTasksStatus.CompleteOneWeek:
|
||||
return 'Completed tasks: 1 week';
|
||||
case MyTasksStatus.CompleteTwoWeek:
|
||||
return 'Completed tasks: 2 weeks';
|
||||
case MyTasksStatus.CompleteThreeWeek:
|
||||
return 'Completed tasks: 3 weeks';
|
||||
default:
|
||||
return 'unknown tasks';
|
||||
}
|
||||
}
|
||||
|
||||
function prettySort(sort: MyTasksSort) {
|
||||
if (sort === MyTasksSort.None) {
|
||||
return 'Sort';
|
||||
@ -492,7 +516,10 @@ const Projects = () => {
|
||||
{ sort: MyTasksSort.None, status: MyTasksStatus.All },
|
||||
'my_tasks_filter',
|
||||
);
|
||||
const { data } = useMyTasksQuery({ variables: { sort: filters.sort, status: filters.status } });
|
||||
const { data } = useMyTasksQuery({
|
||||
variables: { sort: filters.sort, status: filters.status },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
const [dateEditor, setDateEditor] = useState<DateEditorState>({ open: false, pos: null, task: null });
|
||||
const onEditDueDate = (task: Task, $target: React.RefObject<HTMLElement>) => {
|
||||
if ($target && $target.current && data) {
|
||||
@ -524,7 +551,7 @@ const Projects = () => {
|
||||
});
|
||||
}
|
||||
};
|
||||
const { showPopup } = usePopup();
|
||||
const { showPopup, hidePopup } = usePopup();
|
||||
const [updateTaskDueDate] = useUpdateTaskDueDateMutation();
|
||||
const $editorContents = useRef<HTMLDivElement>(null);
|
||||
const $dateContents = useRef<HTMLDivElement>(null);
|
||||
@ -653,9 +680,23 @@ const Projects = () => {
|
||||
<ProjectBar>
|
||||
<ProjectActions />
|
||||
<ProjectActions>
|
||||
<ProjectAction disabled>
|
||||
<CheckCircle width={13} height={13} />
|
||||
<ProjectActionText>All Tasks</ProjectActionText>
|
||||
<ProjectAction
|
||||
onClick={$target => {
|
||||
showPopup(
|
||||
$target,
|
||||
<MyTasksStatusPopup
|
||||
status={filters.status}
|
||||
onChangeStatus={status => {
|
||||
setFilters(prev => ({ ...prev, status }));
|
||||
hidePopup();
|
||||
}}
|
||||
/>,
|
||||
{ width: 185 },
|
||||
);
|
||||
}}
|
||||
>
|
||||
<CheckCircleOutline width={13} height={13} />
|
||||
<ProjectActionText>{prettyStatus(filters.status)}</ProjectActionText>
|
||||
</ProjectAction>
|
||||
<ProjectAction
|
||||
onClick={$target => {
|
||||
@ -663,8 +704,12 @@ const Projects = () => {
|
||||
$target,
|
||||
<MyTasksSortPopup
|
||||
sort={filters.sort}
|
||||
onChangeSort={sort => setFilters(prev => ({ ...prev, sort }))}
|
||||
onChangeSort={sort => {
|
||||
setFilters(prev => ({ ...prev, sort }));
|
||||
hidePopup();
|
||||
}}
|
||||
/>,
|
||||
{ width: 185 },
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
Reference in New Issue
Block a user