Add dueDate and assignee on notes (#988)
* Add dueDate and assignee on notes * Fix tests * Fix tests
This commit is contained in:
@ -7,12 +7,13 @@ import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRi
|
||||
import { GET_ACTIVITIES_BY_TARGETS } from '@/activities/queries';
|
||||
import { IconNotes } from '@/ui/icon';
|
||||
import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip';
|
||||
import { Activity, useUpdateActivityMutation } from '~/generated/graphql';
|
||||
import { Activity, User, useUpdateActivityMutation } from '~/generated/graphql';
|
||||
import {
|
||||
beautifyExactDate,
|
||||
beautifyPastDateRelativeToNow,
|
||||
} from '~/utils/date-utils';
|
||||
|
||||
import { TimelineActivityCardFooter } from './TimelineActivityCardFooter';
|
||||
import { TimelineActivityTitle } from './TimelineActivityTitle';
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
@ -73,19 +74,19 @@ const StyledCard = styled.div`
|
||||
align-items: flex-start;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(3)};
|
||||
max-width: 400px;
|
||||
padding: ${({ theme }) => theme.spacing(3)};
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
width: 400px;
|
||||
`;
|
||||
|
||||
const StyledCardContent = styled.div`
|
||||
align-self: stretch;
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
|
||||
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@ -104,6 +105,11 @@ const StyledTooltip = styled(Tooltip)`
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledCardDetailsContainer = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledTimelineItemContainer = styled.div`
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
@ -115,7 +121,9 @@ type OwnProps = {
|
||||
activity: Pick<
|
||||
Activity,
|
||||
'id' | 'title' | 'body' | 'createdAt' | 'completedAt' | 'type'
|
||||
> & { author: Pick<Activity['author'], 'displayName'> };
|
||||
> & { author: Pick<Activity['author'], 'displayName'> } & {
|
||||
assignee?: Pick<User, 'id' | 'displayName'> | null;
|
||||
};
|
||||
};
|
||||
|
||||
export function TimelineActivity({ activity }: OwnProps) {
|
||||
@ -130,8 +138,10 @@ export function TimelineActivity({ activity }: OwnProps) {
|
||||
(value: boolean) => {
|
||||
updateActivityMutation({
|
||||
variables: {
|
||||
id: activity.id,
|
||||
completedAt: value ? new Date().toISOString() : null,
|
||||
where: { id: activity.id },
|
||||
data: {
|
||||
completedAt: value ? new Date().toISOString() : null,
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? ''],
|
||||
});
|
||||
@ -165,15 +175,20 @@ export function TimelineActivity({ activity }: OwnProps) {
|
||||
</StyledVerticalLineContainer>
|
||||
<StyledCardContainer>
|
||||
<StyledCard onClick={() => openActivityRightDrawer(activity.id)}>
|
||||
<TimelineActivityTitle
|
||||
title={activity.title ?? ''}
|
||||
completed={!!activity.completedAt}
|
||||
type={activity.type}
|
||||
onCompletionChange={handleActivityCompletionChange}
|
||||
/>
|
||||
<StyledCardContent>
|
||||
<OverflowingTextWithTooltip text={body ? body : '(No content)'} />
|
||||
</StyledCardContent>
|
||||
<StyledCardDetailsContainer>
|
||||
<TimelineActivityTitle
|
||||
title={activity.title ?? ''}
|
||||
completed={!!activity.completedAt}
|
||||
type={activity.type}
|
||||
onCompletionChange={handleActivityCompletionChange}
|
||||
/>
|
||||
<StyledCardContent>
|
||||
<OverflowingTextWithTooltip
|
||||
text={body ? body : '(No content)'}
|
||||
/>
|
||||
</StyledCardContent>
|
||||
</StyledCardDetailsContainer>
|
||||
<TimelineActivityCardFooter activity={activity} />
|
||||
</StyledCard>
|
||||
</StyledCardContainer>
|
||||
</StyledTimelineItemContainer>
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { UserChip } from '@/users/components/UserChip';
|
||||
import { Activity, User } from '~/generated/graphql';
|
||||
import { beautifyExactDate } from '~/utils/date-utils';
|
||||
|
||||
type OwnProps = {
|
||||
activity: Pick<Activity, 'id' | 'dueAt'> & {
|
||||
assignee?: Pick<User, 'id' | 'displayName' | 'avatarUrl'> | null;
|
||||
};
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
border-top: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
width: calc(100% - ${({ theme }) => theme.spacing(4)});
|
||||
`;
|
||||
|
||||
const StyledVerticalSeparator = styled.div`
|
||||
border-left: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
height: 24px;
|
||||
`;
|
||||
|
||||
export function TimelineActivityCardFooter({ activity }: OwnProps) {
|
||||
return (
|
||||
<>
|
||||
{(activity.assignee || activity.dueAt) && (
|
||||
<StyledContainer>
|
||||
{activity.assignee && (
|
||||
<UserChip
|
||||
id={activity.assignee.id}
|
||||
name={activity.assignee.displayName ?? ''}
|
||||
pictureUrl={activity.assignee.avatarUrl ?? ''}
|
||||
/>
|
||||
)}
|
||||
{activity.dueAt && (
|
||||
<>
|
||||
{activity.assignee && <StyledVerticalSeparator />}
|
||||
{beautifyExactDate(activity.dueAt)}
|
||||
</>
|
||||
)}
|
||||
</StyledContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -12,15 +12,19 @@ const StyledTitleContainer = styled.div`
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.lg};
|
||||
|
||||
width: calc(100% - ${({ theme }) => theme.spacing(6)});
|
||||
`;
|
||||
|
||||
const StyledTitleText = styled.div<{ completed?: boolean }>`
|
||||
text-decoration: ${({ completed }) => (completed ? 'line-through' : 'none')};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledCheckboxContainer = styled.div`
|
||||
const StyledTitleText = styled.div<{
|
||||
completed?: boolean;
|
||||
hasCheckbox?: boolean;
|
||||
}>`
|
||||
text-decoration: ${({ completed }) => (completed ? 'line-through' : 'none')};
|
||||
width: ${({ hasCheckbox, theme }) =>
|
||||
!hasCheckbox ? '100%;' : `calc(100% - ${theme.spacing(5)});`};
|
||||
`;
|
||||
|
||||
const StyledCheckboxContainer = styled.div<{ hasCheckbox?: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -55,7 +59,10 @@ export function TimelineActivityTitle({
|
||||
/>
|
||||
</StyledCheckboxContainer>
|
||||
)}
|
||||
<StyledTitleText completed={completed}>
|
||||
<StyledTitleText
|
||||
completed={completed}
|
||||
hasCheckbox={type === ActivityType.Task}
|
||||
>
|
||||
<OverflowingTextWithTooltip text={title ? title : '(No title)'} />
|
||||
</StyledTitleText>
|
||||
</StyledTitleContainer>
|
||||
|
||||
Reference in New Issue
Block a user