Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(datatrak): RN-1313: My tasks section #5776

Merged
merged 16 commits into from
Jul 18, 2024
23 changes: 13 additions & 10 deletions packages/central-server/src/apiV2/tasks/GETTasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,23 @@ export class GETTasks extends GETHandler {
}

async getDbQueryOptions() {
const { multiJoin, sort, ...restOfOptions } = await super.getDbQueryOptions();
const { multiJoin, sort, rawSort, ...restOfOptions } = await super.getDbQueryOptions();

return {
const options = {
...restOfOptions,
// Strip table prefix from `task_status` and `assignee_name` as these are customColumns
sort:
!restOfOptions.rawSort &&
sort?.map(s =>
s
.replace('task.task_status', 'task_status')
.replace('task.assignee_name', 'assignee_name'),
),
// Appending the multi-join from the Record class so that we can fetch the `task_status` and `assignee_name`
multiJoin: mergeMultiJoin(multiJoin, this.models.task.DatabaseRecordClass.joins),
};

if (rawSort) {
options.rawSort = rawSort;
} else {
// Strip table prefix from `task_status` and `assignee_name` as these are customColumns
options.sort = sort?.map(s =>
s.replace('task.task_status', 'task_status').replace('task.assignee_name', 'assignee_name'),
);
}

return options;
}
}
1 change: 0 additions & 1 deletion packages/datatrak-web-server/src/routes/TasksRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ export class TasksRoute extends Route<TasksRequest> {

this.formatFilters();
await this.processFilterSettings();

// If no sort is provided, default to sorting completed and cancelled tasks to the bottom and by due date
const rawSort =
!sort &&
Expand Down
18 changes: 10 additions & 8 deletions packages/datatrak-web/src/api/queries/useTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ import { get } from '../api';

type Filter = {
id: string;
value: string;
value: string | object;
};

type SortBy = {
id: string;
desc: boolean;
};

export const useTasks = (
projectId?: string,
pageSize?: number,
page?: number,
filters: Filter[] = [],
sortBy?: SortBy[],
) => {
interface UseTasksOptions {
projectId?: string;
pageSize?: number;
page?: number;
filters?: Filter[];
sortBy?: SortBy[];
}

export const useTasks = ({ projectId, pageSize, page, filters = [], sortBy }: UseTasksOptions) => {
return useQuery(
['tasks', projectId, pageSize, page, filters, sortBy],
(): Promise<DatatrakWebTasksRequest.ResBody> =>
Expand Down
57 changes: 57 additions & 0 deletions packages/datatrak-web/src/features/Tasks/NoTasksSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import React from 'react';
import styled from 'styled-components';
import { Typography } from '@material-ui/core';
import { Button as UIButton } from '@tupaia/ui-components';
import { Link } from 'react-router-dom';
import { ROUTES } from '../../constants';

const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
text-align: center;
`;

const Image = styled.img.attrs({
src: '/tupaia-high-five.svg',
alt: 'Illustration of two hands giving a high five',
})`
flex: 1;
height: auto;
min-height: 5rem;
width: auto;
margin: 0 auto 1rem;
`;

const Text = styled(Typography)`
text-align: center;
font-size: 0.9rem;
line-height: 1.5;
margin-block-end: 0.5rem;
`;

const Button = styled(UIButton)`
padding: 0.25rem 1rem;
margin-block-end: 0.5rem;

.MuiButton-label {
font-size: 0.75rem;
}
`;
export const NoTasksSection = () => (
<Container>
<Image />
<Text>
Congratulations, you have no tasks to complete! You can view all other tasks for your project
using the button below.
</Text>
<Button to={ROUTES.TASKS} component={Link}>
tcaiger marked this conversation as resolved.
Show resolved Hide resolved
View all tasks
</Button>
</Container>
);
4 changes: 2 additions & 2 deletions packages/datatrak-web/src/features/Tasks/StatusPill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ const Pill = styled.span<{
}>`
background-color: ${({ $color }) => `${$color}22`};
color: ${({ $color }) => $color};
font-size: 0.7rem;
font-size: 0.625rem;
padding-inline: 0.7rem;
padding-block: 0.3rem;
padding-block: 0.2rem;
border-radius: 20px;
.cell-content > div:has(&) {
overflow: visible;
Expand Down
111 changes: 111 additions & 0 deletions packages/datatrak-web/src/features/Tasks/TaskTile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import React from 'react';
import styled from 'styled-components';
import { generatePath, Link } from 'react-router-dom';
import ChatIcon from '@material-ui/icons/ChatBubbleOutline';
import { ROUTES } from '../../constants';
import { StatusPill } from './StatusPill';
import { displayDate } from '../../utils';
import { ButtonLink } from '../../components';

const TileContainer = styled.div`
display: flex;
text-align: left;
justify-content: space-between;
border-radius: 10px;
border: 1px solid ${({ theme }) => theme.palette.divider};
width: 100%;
padding: 0.4rem 0.7rem;
margin-block-end: 0.5rem;

.MuiButton-root {
padding: 0.2rem 1.2rem;
}

.MuiButton-label {
font-size: 0.75rem;
}
`;

const TileTitle = styled.div`
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-block-end: 0.25rem;
color: ${({ theme }) => theme.palette.text.primary};
`;

const TileLeft = styled.div`
flex: 1;
padding-inline-end: 1rem;
min-width: 0;
`;

const TileContent = styled.div`
display: flex;
font-size: 0.75rem;
color: ${({ theme }) => theme.palette.text.secondary};
align-items: center;

> span {
margin-inline-end: 0.6rem;
}
`;

const TileRight = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
`;

const CommentsContainer = styled.div`
display: flex;
align-items: center;

.MuiSvgIcon-root {
font-size: 1rem;
margin-inline-end: 0.2rem;
}
`;

const Comments = ({ commentsCount = 0 }) => {
if (!commentsCount) {
return null;
}
return (
<CommentsContainer>
<ChatIcon />
{commentsCount}
</CommentsContainer>
);
};

export const TaskTile = ({ task }) => {
const { survey, entity, taskStatus, dueDate } = task;
const surveyLink = generatePath(ROUTES.SURVEY, {
surveyCode: survey.code,
countryCode: entity.countryCode,
});
return (
<TileContainer>
<TileLeft>
<TileTitle>{survey.name}</TileTitle>
<TileContent>
<StatusPill status={taskStatus} />
<span>{displayDate(dueDate)}</span>
<Comments />
</TileContent>
</TileLeft>
<TileRight>
<ButtonLink to={surveyLink} component={Link}>
Complete task
</ButtonLink>
</TileRight>
</TileContainer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const useTasksTable = () => {
const urlFilters = searchParams.get('filters');
const filters = urlFilters ? JSON.parse(urlFilters) : [];

const { data, isLoading } = useTasks(projectId, pageSize, page, filters, sortBy);
const { data, isLoading } = useTasks({ projectId, pageSize, page, filters, sortBy });

const updateSorting = newSorting => {
searchParams.set('sortBy', JSON.stringify(newSorting));
Expand Down
2 changes: 2 additions & 0 deletions packages/datatrak-web/src/features/Tasks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
export { TaskPageHeader } from './TaskPageHeader';
export { TasksTable } from './TasksTable';
export { TaskDetails } from './TaskDetails';
export { NoTasksSection } from './NoTasksSection';
export { TaskTile } from './TaskTile';
export { CreateTaskModal } from './CreateTaskModal';
export { TaskActionsMenu } from './TaskActionsMenu';
35 changes: 20 additions & 15 deletions packages/datatrak-web/src/views/LandingPage/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { SurveyResponsesSection } from './SurveyResponsesSection';
import { LeaderboardSection } from './LeaderboardSection';
import { ActivityFeedSection } from './ActivityFeedSection';
import { RecentSurveysSection } from './RecentSurveysSection';
import { TasksSection } from './TasksSection';
import { DESKTOP_MEDIA_QUERY, HEADER_HEIGHT } from '../../constants';

const PageContainer = styled(BasePageContainer)`
Expand All @@ -21,20 +22,15 @@ const PageContainer = styled(BasePageContainer)`
const PageBody = styled.div`
display: flex;
flex-direction: column;
padding: 1.5rem 0;
padding: 0.5rem 0 1.5rem;
width: 100%;
max-width: 85rem;
margin: 0 auto;
height: auto;

${({ theme }) => theme.breakpoints.up('md')} {
height: calc(100vh - ${HEADER_HEIGHT});
padding: 2rem 2.75rem 0.8rem 2.75rem;
}

${DESKTOP_MEDIA_QUERY} {
padding-top: 4rem;
padding-bottom: 2.5rem;
padding: 0.2rem 1rem 0.8rem;
}
`;

Expand All @@ -44,6 +40,8 @@ const Grid = styled.div`
flex-direction: column;
margin-top: 1rem;
min-height: 0; // This is needed to stop the grid overflowing the flex container
max-width: 38rem;
margin-inline: auto;

.MuiButtonBase-root {
margin-left: 0; // clear spacing of adjacent buttons
Expand All @@ -57,11 +55,13 @@ const Grid = styled.div`
${({ theme }) => theme.breakpoints.up('md')} {
display: grid;
gap: 1.25rem;
grid-template-rows: auto auto;
grid-template-columns: 1fr 1fr;
grid-template-rows: 20rem auto auto;
grid-template-columns: 2fr 1fr;
grid-template-areas:
'recentSurveys leaderboard'
'recentResponses activityFeed';
'surveySelect tasks'
'recentSurveys recentResponses'
'activityFeed leaderboard';
max-width: none;

> section {
margin: 0;
Expand All @@ -70,27 +70,32 @@ const Grid = styled.div`
}

${({ theme }) => theme.breakpoints.up('lg')} {
grid-template-columns: 23% 1fr 1fr 28%;
grid-template-rows: 10.5rem auto auto;
grid-template-columns: 23% 1fr 1fr 30%;
grid-template-areas:
'recentSurveys recentSurveys recentSurveys leaderboard'
'surveySelect surveySelect surveySelect tasks'
'recentSurveys recentSurveys recentSurveys tasks'
'recentResponses activityFeed activityFeed leaderboard';
> div {
min-height: auto;
}
}

${DESKTOP_MEDIA_QUERY} {
margin-top: 2.5rem;
gap: 1.81rem;
}
`;

export const LandingPage = () => {
// Todo: Remove this once the feature is complete
const showTasks = process.env.REACT_APP_TUPAIA_TASKS;
tcaiger marked this conversation as resolved.
Show resolved Hide resolved

return (
<PageContainer>
<PageBody>
<SurveySelectSection />
<Grid>
<SurveySelectSection />
{showTasks && <TasksSection />}
<LeaderboardSection />
<RecentSurveysSection />
<SurveyResponsesSection />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const SectionHeading = styled(Typography).attrs({
variant: 'h2',
})`
font-size: 1rem;
line-height: 1.2;
font-weight: 500;
margin-bottom: 0.75rem;
`;
Loading
Loading