Add dueDate and assignee on notes (#988)

* Add dueDate and assignee on notes

* Fix tests

* Fix tests
This commit is contained in:
Charles Bochet
2023-07-29 15:36:21 -07:00
committed by GitHub
parent d9f6ae8663
commit 8601ed04ae
46 changed files with 875 additions and 205 deletions

View File

@ -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>

View File

@ -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>
)}
</>
);
}

View File

@ -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>