diff --git a/pages/accountLists/[accountListId]/tasks/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tasks/[[...contactId]].page.tsx index 60f3db633..1dc903e5f 100644 --- a/pages/accountLists/[accountListId]/tasks/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tasks/[[...contactId]].page.tsx @@ -335,8 +335,8 @@ const TasksPage: React.FC = () => { isChecked={isRowChecked(task.id)} useTopMargin={index === 0} getContactHrefObject={getContactHrefObject} - contactDetailsOpen={contactDetailsOpen} removeSelectedIds={deselectMultipleIds} + filterPanelOpen={filterPanelOpen} /> )} diff --git a/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.test.tsx b/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.test.tsx index fcce4e74e..6e44ef64e 100644 --- a/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.test.tsx @@ -75,7 +75,9 @@ describe('ContactTaskRow', () => { expect(await findByText(task.subject)).toBeVisible(); expect( - await findByText(`${task.user?.firstName} ${task.user?.lastName}`), + await findByText( + `${task.user?.firstName?.[0]}${task.user?.lastName?.[0]}`, + ), ).toBeVisible(); expect(queryByTestId('loadingRow')).toBeNull(); @@ -101,8 +103,34 @@ describe('ContactTaskRow', () => { mocks: { startAt, result: ResultEnum.None, + user: { + firstName: 'John', + lastName: 'Wayne', + }, }, }); + const taskWithTags = gqlMock(TaskRowFragmentDoc, { + mocks: { + id: '123', + startAt, + result: ResultEnum.None, + tagList: ['testTag'], + user: null, + }, + }); + + it('renders the assignee avatar', async () => { + const { findByText } = render(); + + expect(await findByText('JW')).toBeVisible(); + }); + + it('renders the tag icon', async () => { + const { findByTestId } = render(); + + expect(await findByTestId('tagIcon-123')).toBeVisible(); + }); + it('handles complete button click', async () => { const { findByText, getByRole } = render(); @@ -166,7 +194,7 @@ describe('ContactTaskRow', () => { const { getByText } = render(); - expect(getByText(activity?.value)).toBeVisible(); + expect(getByText(activity?.value.split(' - ')[1].trim())).toBeVisible(); }, ); }); diff --git a/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.tsx b/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.tsx index 867d06c2d..6d4f4c052 100644 --- a/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.tsx +++ b/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.tsx @@ -1,15 +1,28 @@ import React, { useState } from 'react'; -import { Box, Checkbox, Skeleton, Typography } from '@mui/material'; +import LocalOffer from '@mui/icons-material/LocalOffer'; +import { + Avatar, + Box, + Checkbox, + Skeleton, + Tooltip, + Typography, +} from '@mui/material'; import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; +import { StyledCheckbox } from 'src/components/Contacts/ContactRow/ContactRow'; import { TaskModalEnum } from 'src/components/Task/Modal/TaskModal'; +import { + CommentTooltipText, + TooltipTypography, +} from 'src/components/Task/TaskRow/CommentTooltipText'; +import { TaskActionPhase } from 'src/components/Task/TaskRow/TaskActionPhase'; import { TaskRowFragment } from 'src/components/Task/TaskRow/TaskRow.generated'; import { StarredItemIcon } from 'src/components/common/StarredItemIcon/StarredItemIcon'; import { usePhaseData } from 'src/hooks/usePhaseData'; import useTaskModal from 'src/hooks/useTaskModal'; import theme from 'src/theme'; -import { getLocalizedTaskType } from 'src/utils/functions/getLocalizedTaskType'; import { DeleteTaskIconButton } from '../DeleteTaskIconButton/DeleteTaskIconButton'; import { StarTaskIconButton } from '../StarTaskIconButton/StarTaskIconButton'; import { TaskCommentsButton } from './TaskCommentsButton/TaskCommentsButton'; @@ -36,12 +49,6 @@ const TaskItemWrap = styled(Box)(({ theme }) => ({ height: '100%', })); -const TaskType = styled(Typography)(({ theme }) => ({ - fontSize: 14, - fontWeight: 700, - color: theme.palette.text.primary, -})); - const TaskDescription = styled(Typography)(({ theme }) => ({ fontSize: 14, color: theme.palette.text.primary, @@ -66,14 +73,6 @@ const SubjectWrap = styled(Box)(({}) => ({ }, })); -const AssigneeName = styled(Typography)(({ theme }) => ({ - fontSize: 14, - color: theme.palette.text.primary, - margin: theme.spacing(1), - overflow: 'hidden', - textOverflow: 'ellipsis', -})); - const StarIconWrap = styled(Box)(({ theme }) => ({ margin: theme.spacing(1), })); @@ -156,12 +155,13 @@ export const ContactTaskRow: React.FC = ({ const dateToShow = completedAt ?? startAt; const taskDate = (dateToShow && DateTime.fromISO(dateToShow)) || null; const assigneeName = user ? `${user.firstName} ${user.lastName}` : ''; + const tagList = !!task.tagList.length ? task.tagList.join(', ') : ''; const activityData = activityType ? activityTypes.get(activityType) : null; return !hasBeenDeleted ? ( - onTaskCheckToggle(task.id)} @@ -177,30 +177,82 @@ export const ContactTaskRow: React.FC = ({ onClick={handleSubjectPressed} onMouseEnter={() => preloadTaskModal(TaskModalEnum.Edit)} > - - {activityData && `${activityData.phase} - `} - {getLocalizedTaskType(t, activityType)} - - {subject} + + + + {subject} + - {assigneeName} - - + {(assigneeName || tagList) && ( + + {assigneeName && ( + + {t('Assignee: ') + assigneeName} + + )} + {tagList && ( + {t('Tags: ') + tagList} + )} + + } + placement="top" + arrow + enterTouchDelay={0} + > + {assigneeName ? ( + + {(task?.user?.firstName?.[0] || '') + + task?.user?.lastName?.[0] || ''} + + ) : ( + + )} + + )} + + + + ) : null + } + placement="top" + arrow + > + + preloadTaskModal(TaskModalEnum.Comments)} + small + /> + + - preloadTaskModal(TaskModalEnum.Comments)} - detailsPage - /> ({ display: 'flex', justifyContent: 'center', alignItems: 'center', - margin: theme.spacing(1), + margin: theme.spacing(1, 0.5), })); const TaskCommentIcon = styled(CalendarToday, { @@ -32,7 +32,7 @@ const DateText = styled(Typography, { : isComplete ? theme.palette.text.secondary : theme.palette.text.primary, - marginLeft: theme.spacing(1), + marginLeft: small ? theme.spacing(0.5) : theme.spacing(1), }), ); diff --git a/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTasksTab.tsx b/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTasksTab.tsx index fa35fe685..6315bae06 100644 --- a/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTasksTab.tsx +++ b/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTasksTab.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import Add from '@mui/icons-material/Add'; import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline'; -import { Box, Button, Checkbox, Divider, Typography } from '@mui/material'; +import { Box, Button, Divider, Hidden, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; @@ -18,6 +18,7 @@ import { TaskFilterSetInput } from 'src/graphql/types.generated'; import { useGetTaskIdsForMassSelectionQuery } from 'src/hooks/GetIdsForMassSelection.generated'; import { useMassSelection } from 'src/hooks/useMassSelection'; import useTaskModal from 'src/hooks/useTaskModal'; +import { StyledCheckbox } from '../../ContactRow/ContactRow'; import { ContactTaskRow } from './ContactTaskRow/ContactTaskRow'; import { useContactPhaseQuery, @@ -220,7 +221,7 @@ export const ContactTasksTab: React.FC = ({ - = ({ placeholder={t('Search Tasks')} page={PageEnum.Task} /> + {!!ids.length && ( + + + {t('{{count}} Selected', { count: ids.length })} + + + )} diff --git a/src/components/Contacts/ContactDetails/ContactTasksTab/DeleteTaskIconButton/DeleteTaskIconButton.tsx b/src/components/Contacts/ContactDetails/ContactTasksTab/DeleteTaskIconButton/DeleteTaskIconButton.tsx index d901d53e7..1b877de3b 100644 --- a/src/components/Contacts/ContactDetails/ContactTasksTab/DeleteTaskIconButton/DeleteTaskIconButton.tsx +++ b/src/components/Contacts/ContactDetails/ContactTasksTab/DeleteTaskIconButton/DeleteTaskIconButton.tsx @@ -5,8 +5,10 @@ import { useTranslation } from 'react-i18next'; import { DeletedItemIcon } from '../../../../common/DeleteItemIcon/DeleteItemIcon'; import { DeleteConfirmation } from '../../../../common/Modal/DeleteConfirmation/DeleteConfirmation'; -const DeleteButton = styled(IconButton)(({ theme }) => ({ - margin: theme.spacing(1), +const DeleteButton = styled(IconButton, { + shouldForwardProp: (prop) => prop !== 'small', +})<{ small?: boolean }>(({ small, theme }) => ({ + margin: small ? theme.spacing(0.5) : theme.spacing(1), })); interface DeleteTaskIconButtonProps { @@ -14,6 +16,7 @@ interface DeleteTaskIconButtonProps { taskId: string; onDeleteConfirm?: () => void; removeSelectedIds?: (id: string[]) => void; + small?: boolean; } export const DeleteTaskIconButton: React.FC = ({ @@ -21,6 +24,7 @@ export const DeleteTaskIconButton: React.FC = ({ taskId, onDeleteConfirm, removeSelectedIds, + small = false, }) => { const { t } = useTranslation(); @@ -31,6 +35,7 @@ export const DeleteTaskIconButton: React.FC = ({ setRemoveDialogOpen(true)} data-testid={`DeleteIconButton-${taskId}`} + small={small} > diff --git a/src/components/Task/Modal/Form/TaskModalForm.test.tsx b/src/components/Task/Modal/Form/TaskModalForm.test.tsx index 9c42ebe89..40576b5bb 100644 --- a/src/components/Task/Modal/Form/TaskModalForm.test.tsx +++ b/src/components/Task/Modal/Form/TaskModalForm.test.tsx @@ -368,7 +368,7 @@ describe('TaskModalForm', () => { diff --git a/src/components/Task/Modal/Form/TaskModalForm.tsx b/src/components/Task/Modal/Form/TaskModalForm.tsx index fbf817d9b..ce414b415 100644 --- a/src/components/Task/Modal/Form/TaskModalForm.tsx +++ b/src/components/Task/Modal/Form/TaskModalForm.tsx @@ -242,7 +242,7 @@ const TaskModalForm = ({ nextAction: task.nextAction ?? null, tagList: additionalTags ?? [], contactIds: task.contacts.nodes.map(({ id }) => id), - userId: task.user?.id ?? session.data?.user.userID ?? null, + userId: task.user?.id ?? null, notificationTimeBefore: task.notificationTimeBefore, notificationType: task.notificationType, notificationTimeUnit: task.notificationTimeUnit, diff --git a/src/components/Task/TaskRow/CommentTooltipText.test.tsx b/src/components/Task/TaskRow/CommentTooltipText.test.tsx new file mode 100644 index 000000000..79f24fd78 --- /dev/null +++ b/src/components/Task/TaskRow/CommentTooltipText.test.tsx @@ -0,0 +1,46 @@ +import { render } from '@testing-library/react'; +import { DateTime } from 'luxon'; +import TestRouter from '__tests__/util/TestRouter'; +import { GqlMockedProvider, gqlMock } from '__tests__/util/graphqlMocking'; +import { + TaskModalCommentFragment, + TaskModalCommentFragmentDoc, +} from '../Modal/Comments/TaskListComments.generated'; +import { CommentTooltipText } from './CommentTooltipText'; + +const accountListId = 'account-list-1'; +const commentId = 'comment-1'; +const router = { + query: { accountListId }, + isReady: true, +}; + +const TestComponent: React.FC = () => { + const comment = gqlMock( + TaskModalCommentFragmentDoc, + { + mocks: { + id: commentId, + body: 'Comment', + updatedAt: DateTime.local(2020, 1, 2).toISODate() ?? '', + }, + }, + ); + + return ( + + + + + + ); +}; + +describe('CommentTooltipText', () => { + it('should render', async () => { + const { findByText, getByText } = render(); + + expect(await findByText('Comment')).toBeInTheDocument(); + expect(getByText('Jan 2, 2020')).toBeInTheDocument(); + }); +}); diff --git a/src/components/Task/TaskRow/CommentTooltipText.tsx b/src/components/Task/TaskRow/CommentTooltipText.tsx new file mode 100644 index 000000000..c6590b61d --- /dev/null +++ b/src/components/Task/TaskRow/CommentTooltipText.tsx @@ -0,0 +1,27 @@ +import { Typography } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import { DateTime } from 'luxon'; +import { useLocale } from 'src/hooks/useLocale'; +import { dateFormat } from 'src/lib/intlFormat'; + +export const TooltipTypography = styled(Typography)(() => ({ + fontSize: 11, +})); + +interface CommentTooltipTextProps { + comments: { id: string; body: string; updatedAt: string }[]; +} +export const CommentTooltipText: React.FC = ({ + comments, +}) => { + const locale = useLocale(); + const latestComment = comments.at(-1) ?? null; + return latestComment ? ( + <> + {latestComment.body} + + {dateFormat(DateTime.fromISO(latestComment.updatedAt), locale)} + + + ) : null; +}; diff --git a/src/components/Task/TaskRow/TaskActionPhase.tsx b/src/components/Task/TaskRow/TaskActionPhase.tsx new file mode 100644 index 000000000..cc2498d75 --- /dev/null +++ b/src/components/Task/TaskRow/TaskActionPhase.tsx @@ -0,0 +1,62 @@ +import { Box, Typography } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import { useTranslation } from 'react-i18next'; +import { ActivityTypeEnum } from 'src/graphql/types.generated'; +import { ActivityData } from 'src/hooks/usePhaseData'; +import { getLocalizedTaskType } from 'src/utils/functions/getLocalizedTaskType'; + +export const SubjectWrapOuter = styled(Box)(({ theme }) => ({ + width: 'fit-content', + alignItems: 'center', + marginRight: theme.spacing(1), +})); + +export const SubjectWrapInner = styled(Box)(({}) => ({ + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + '&:hover': { + textDecoration: 'underline', + }, +})); + +const TaskPhase = styled(Typography)(() => ({ + fontSize: '14px', + letterSpacing: '0.25', + whiteSpace: 'nowrap', + fontWeight: 700, +})); + +const TaskType = styled(Typography)(({ theme }) => ({ + fontSize: '14px', + letterSpacing: '0.25', + whiteSpace: 'nowrap', + fontWeight: 400, + marginRight: theme.spacing(0.5), +})); + +interface TaskActionPhaseProps { + activityData: ActivityData | null | undefined; + activityType: ActivityTypeEnum | null | undefined; +} + +export const TaskActionPhase: React.FC = ({ + activityData, + activityType, +}) => { + const { t } = useTranslation(); + return ( + + + {activityData?.phase ? activityData.phase : ''} + {getLocalizedTaskType(t, activityType)} + + + ); +}; diff --git a/src/components/Task/TaskRow/TaskRow.graphql b/src/components/Task/TaskRow/TaskRow.graphql index 682e941c2..f298e543e 100644 --- a/src/components/Task/TaskRow/TaskRow.graphql +++ b/src/components/Task/TaskRow/TaskRow.graphql @@ -4,6 +4,11 @@ fragment TaskRow on Task { startAt completedAt comments { + nodes { + id + body + updatedAt + } totalCount } contacts(first: 25) { diff --git a/src/components/Task/TaskRow/TaskRow.stories.tsx b/src/components/Task/TaskRow/TaskRow.stories.tsx index d427d9739..58ff37b4e 100644 --- a/src/components/Task/TaskRow/TaskRow.stories.tsx +++ b/src/components/Task/TaskRow/TaskRow.stories.tsx @@ -31,6 +31,7 @@ export const Default = (): ReactElement => { isChecked={false} onContactSelected={onContactSelected} onTaskCheckToggle={onTaskCheckSelected} + filterPanelOpen={false} /> ); }; @@ -49,6 +50,7 @@ export const Starred = (): ReactElement => { isChecked={false} onContactSelected={onContactSelected} onTaskCheckToggle={onTaskCheckSelected} + filterPanelOpen={false} /> ); }; @@ -63,6 +65,7 @@ export const Checked = (): ReactElement => { isChecked={true} onContactSelected={onContactSelected} onTaskCheckToggle={onTaskCheckSelected} + filterPanelOpen={false} /> ); }; @@ -82,6 +85,7 @@ export const Complete = (): ReactElement => { isChecked={false} onContactSelected={onContactSelected} onTaskCheckToggle={onTaskCheckSelected} + filterPanelOpen={false} /> ); }; @@ -101,6 +105,7 @@ export const Late = (): ReactElement => { isChecked={false} onContactSelected={onContactSelected} onTaskCheckToggle={onTaskCheckSelected} + filterPanelOpen={false} /> ); }; diff --git a/src/components/Task/TaskRow/TaskRow.test.tsx b/src/components/Task/TaskRow/TaskRow.test.tsx index 77bcec096..9465a344f 100644 --- a/src/components/Task/TaskRow/TaskRow.test.tsx +++ b/src/components/Task/TaskRow/TaskRow.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { ThemeProvider } from '@mui/material/styles'; -import { render } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { GqlMockedProvider, gqlMock } from '__tests__/util/graphqlMocking'; import { ActivityTypeEnum, ResultEnum } from 'src/graphql/types.generated'; @@ -61,6 +61,7 @@ describe('TaskRow', () => { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , @@ -89,6 +90,7 @@ describe('TaskRow', () => { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , @@ -99,18 +101,20 @@ describe('TaskRow', () => { expect(await findByText(task.contacts.nodes[0].name)).toBeVisible(); }); - it('should render late', async () => { + it('should render late and without assignee', async () => { const task = gqlMock(TaskRowFragmentDoc, { mocks: { + id: '123', startAt: lateStartAt, result: ResultEnum.None, + user: null, contacts: { nodes: [{}], }, }, }); - const { findByText } = render( + const { findByText, queryByTestId } = render( { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , @@ -127,6 +132,10 @@ describe('TaskRow', () => { expect(await findByText(task.subject)).toBeVisible(); expect(await findByText(task.contacts.nodes[0].name)).toBeVisible(); + + await waitFor(() => { + expect(queryByTestId('assigneeAvatar-123')).not.toBeInTheDocument(); + }); }); it('should render with Assignee', async () => { @@ -156,6 +165,7 @@ describe('TaskRow', () => { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , @@ -166,7 +176,7 @@ describe('TaskRow', () => { expect(await findByText(task.contacts.nodes[0].name)).toBeVisible(); expect( - await findByText(`${assignee.firstName} ${assignee.lastName}`), + await findByText(`${assignee.firstName[0]}${assignee.lastName[0]}`), ).toBeVisible(); }); @@ -179,7 +189,7 @@ describe('TaskRow', () => { }, }); - const { getByText, getByRole } = render( + const { getAllByText, getByRole } = render( { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , ); - expect(getByText(task.subject)).toBeVisible(); + expect(getAllByText(task.subject).length).toBe(1); userEvent.click(getByRole('checkbox', { hidden: true })); expect(onTaskCheckSelected).toHaveBeenCalledWith(task.id); }); @@ -205,7 +216,7 @@ describe('TaskRow', () => { }, }); - const { findByText, getByTestId } = render( + const { getAllByText, getByTestId } = render( { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , ); - expect(await findByText(task.subject)).toBeVisible(); + expect(getAllByText(task.subject).length).toBe(1); userEvent.click(getByTestId('task-row')); expect(onTaskCheckSelected).toHaveBeenCalledWith(task.id); }); @@ -231,7 +243,7 @@ describe('TaskRow', () => { }, }); - const { findByText, getByRole } = render( + const { getAllByText, getByRole } = render( { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , ); - expect(await findByText(task.subject)).toBeVisible(); + expect(getAllByText(task.subject).length).toBe(1); userEvent.click(getByRole('img', { hidden: true, name: 'Check' })); userEvent.click(getByRole('img', { hidden: true, name: 'Check' })); expect(openTaskModal).toHaveBeenCalledWith({ @@ -274,6 +287,7 @@ describe('TaskRow', () => { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , @@ -292,7 +306,7 @@ describe('TaskRow', () => { }, }); - const { findByText, getByRole } = render( + const { getAllByText, getByRole } = render( { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , ); - expect(await findByText(task.subject)).toBeVisible(); + expect(getAllByText(task.subject).length).toBe(1); userEvent.click(getByRole('img', { hidden: true, name: 'Comment' })); userEvent.click(getByRole('img', { hidden: true, name: 'Comment' })); @@ -325,7 +340,7 @@ describe('TaskRow', () => { }, }); - const { findByText, getByTestId } = render( + const { getAllByText, getByTestId } = render( { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , ); - expect(await findByText(task.subject)).toBeVisible(); + expect(getAllByText(task.subject).length).toBe(1); userEvent.click(getByTestId('subject-wrap')); expect(openTaskModal).toHaveBeenCalledWith({ taskId: task.id, @@ -364,6 +380,7 @@ describe('TaskRow', () => { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , @@ -407,6 +424,7 @@ describe('TaskRow', () => { onTaskCheckToggle={onTaskCheckSelected} onContactSelected={onContactSelected} isChecked={false} + filterPanelOpen={false} /> , diff --git a/src/components/Task/TaskRow/TaskRow.tsx b/src/components/Task/TaskRow/TaskRow.tsx index 89c01d04f..aee9e14a9 100644 --- a/src/components/Task/TaskRow/TaskRow.tsx +++ b/src/components/Task/TaskRow/TaskRow.tsx @@ -1,7 +1,9 @@ import React from 'react'; +import LocalOffer from '@mui/icons-material/LocalOffer'; import { + Avatar, Box, - Checkbox, + Chip, Hidden, Tooltip, Typography, @@ -11,8 +13,8 @@ import { styled, useTheme } from '@mui/material/styles'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; import { GetContactHrefObject } from 'pages/accountLists/[accountListId]/contacts/ContactsWrapper'; +import { StyledCheckbox } from 'src/components/Contacts/ContactRow/ContactRow'; import { usePhaseData } from 'src/hooks/usePhaseData'; -import { getLocalizedTaskType } from 'src/utils/functions/getLocalizedTaskType'; import useTaskModal from '../../../hooks/useTaskModal'; import { TaskCommentsButton } from '../../Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/TaskCommentsButton/TaskCommentsButton'; import { TaskCompleteButton } from '../../Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/TaskCompleteButton/TaskCompleteButton'; @@ -20,46 +22,26 @@ import { TaskDate } from '../../Contacts/ContactDetails/ContactTasksTab/ContactT import { DeleteTaskIconButton } from '../../Contacts/ContactDetails/ContactTasksTab/DeleteTaskIconButton/DeleteTaskIconButton'; import { StarTaskIconButton } from '../../Contacts/ContactDetails/ContactTasksTab/StarTaskIconButton/StarTaskIconButton'; import { TaskModalEnum } from '../Modal/TaskModal'; +import { CommentTooltipText } from './CommentTooltipText'; +import { TaskActionPhase } from './TaskActionPhase'; import { TaskRowFragment } from './TaskRow.generated'; import { TaskRowContactName } from './TaskRowContactName'; -const SubjectWrapOuter = styled(Box)(({ theme }) => ({ - width: 'fit-content', - display: 'flex', - alignItems: 'center', - marginRight: theme.spacing(1), -})); - -const SubjectWrapInner = styled(Box)(({}) => ({ - display: 'flex', - '&:hover': { - textDecoration: 'underline', - }, -})); - const ContactText = styled(Typography)(({ theme }) => ({ margin: '0px', fontFamily: theme.typography.fontFamily, color: theme.palette.text.primary, fontSize: '14px', letterSpacing: '0.25', -})); - -const TaskPhase = styled(Typography)(() => ({ - fontSize: '14px', - letterSpacing: '0.25', - whiteSpace: 'nowrap', - fontWeight: 700, -})); - -const TaskType = styled(Typography, { - shouldForwardProp: (prop) => prop !== 'condensed', -})<{ condensed?: boolean }>(({ theme, condensed = false }) => ({ - fontSize: '14px', - letterSpacing: '0.25', + overflow: 'hidden', whiteSpace: 'nowrap', - fontWeight: condensed ? 400 : 700, - marginRight: theme.spacing(0.5), + textOverflow: 'ellipsis', + flexGrow: 1, + display: 'inline', + '&:hover': { + textDecoration: 'underline', + cursor: 'pointer', + }, })); type OnContactClickFunction = ( @@ -75,8 +57,8 @@ interface TaskRowProps { onTaskCheckToggle: (taskId: string) => void; useTopMargin?: boolean; getContactHrefObject?: GetContactHrefObject; - contactDetailsOpen?: boolean; removeSelectedIds?: (id: string[]) => void; + filterPanelOpen: boolean; } export const TaskRow: React.FC = ({ @@ -87,8 +69,8 @@ export const TaskRow: React.FC = ({ onTaskCheckToggle, useTopMargin, getContactHrefObject, - contactDetailsOpen, removeSelectedIds, + filterPanelOpen, }) => { const { activityType, @@ -105,8 +87,9 @@ export const TaskRow: React.FC = ({ const activityData = activityType ? activityTypes.get(activityType) : null; const theme = useTheme(); - const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); - const condensed = isSmallScreen || contactDetailsOpen; + const isLarge = useMediaQuery(theme.breakpoints.down('lg')); + const isMedium = useMediaQuery(theme.breakpoints.down('md')); + const condensed = (filterPanelOpen && isLarge) || isMedium; const TaskRowWrapper = styled(Box)(({ theme }) => ({ ...(isChecked ? { backgroundColor: theme.palette.cruGrayLight.main } : {}), @@ -157,6 +140,11 @@ export const TaskRow: React.FC = ({ const assigneeName = `${task?.user?.firstName ?? ''} ${ task.user?.lastName ?? '' }`; + const tagListString = !!task.tagList.length + ? t('Tags: ') + task.tagList.join(', ') + : ''; + const tagsToShow = 3; + const areMoreTags = tagsToShow < task.tagList.length; return ( @@ -178,7 +166,7 @@ export const TaskRow: React.FC = ({ > - = ({ }} > {(activityData?.phase || activityType) && ( - - { - handleSubjectPressed(); - e.stopPropagation(); - }} - onMouseEnter={() => preloadTaskModal(TaskModalEnum.Edit)} - style={{ - minWidth: condensed ? '92px' : 'none', - flexDirection: condensed ? 'column' : 'row', - }} - > - - {activityData?.phase && condensed - ? activityData.phase - : activityData?.phase - ? activityData.phase + ' - ' + '\u00A0' - : ''} - - - {activityType ? getLocalizedTaskType(t, activityType) : ''} - - - + { + handleSubjectPressed(); + e.stopPropagation(); + }} + onMouseEnter={() => preloadTaskModal(TaskModalEnum.Edit)} + > + + )} - - + { handleSubjectPressed(); @@ -250,11 +224,10 @@ export const TaskRow: React.FC = ({ }} onMouseEnter={() => preloadTaskModal(TaskModalEnum.Edit)} > - - {subject} - - - + {subject} + + + = ({ - {assigneeName} - - e.stopPropagation()}> - preloadTaskModal(TaskModalEnum.Comments)} - /> - + {!!task?.tagList.length && ( + + {condensed ? ( + + ) : ( + + {task.tagList.slice(0, tagsToShow).map((task, idx) => ( + + ))} + {areMoreTags && '...'} + + )} + + )} + {task?.user && ( + + + {(task?.user?.firstName?.[0] || '') + + task?.user?.lastName?.[0] || ''} + + + )} - - - - + + + + ) : null + } + placement="top" + arrow + > + e.stopPropagation()}> preloadTaskModal(TaskModalEnum.Comments)} - small + small={isMedium} + detailsPage={isMedium} /> - - + + + e.stopPropagation()}> e.stopPropagation()}>