feat: add comments badge to task card
This commit is contained in:
		@@ -1,18 +1,27 @@
 | 
				
			|||||||
import styled, { css, keyframes } from 'styled-components';
 | 
					import styled, { css, keyframes } from 'styled-components';
 | 
				
			||||||
import { mixin } from 'shared/utils/styles';
 | 
					import { mixin } from 'shared/utils/styles';
 | 
				
			||||||
import TextareaAutosize from 'react-autosize-textarea';
 | 
					import TextareaAutosize from 'react-autosize-textarea';
 | 
				
			||||||
import { CheckCircle, CheckSquareOutline, Clock } from 'shared/icons';
 | 
					import { CheckCircle, CheckSquareOutline, Clock, Bubble } from 'shared/icons';
 | 
				
			||||||
import TaskAssignee from 'shared/components/TaskAssignee';
 | 
					import TaskAssignee from 'shared/components/TaskAssignee';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CardMember = styled(TaskAssignee)<{ zIndex: number }>`
 | 
					export const CardMember = styled(TaskAssignee)<{ zIndex: number }>`
 | 
				
			||||||
  box-shadow: 0 0 0 2px ${props => props.theme.colors.bg.secondary},
 | 
					  box-shadow: 0 0 0 2px ${(props) => props.theme.colors.bg.secondary},
 | 
				
			||||||
    inset 0 0 0 1px ${props => mixin.rgba(props.theme.colors.bg.secondary, 0.07)};
 | 
					    inset 0 0 0 1px ${(props) => mixin.rgba(props.theme.colors.bg.secondary, 0.07)};
 | 
				
			||||||
  z-index: ${props => props.zIndex};
 | 
					  z-index: ${(props) => props.zIndex};
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CommentsIcon = styled(Bubble)<{ color: 'success' | 'normal' }>`
 | 
				
			||||||
 | 
					  ${(props) =>
 | 
				
			||||||
 | 
					    props.color === 'success' &&
 | 
				
			||||||
 | 
					    css`
 | 
				
			||||||
 | 
					      fill: ${props.theme.colors.success};
 | 
				
			||||||
 | 
					      stroke: ${props.theme.colors.success};
 | 
				
			||||||
 | 
					    `}
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ChecklistIcon = styled(CheckSquareOutline)<{ color: 'success' | 'normal' }>`
 | 
					export const ChecklistIcon = styled(CheckSquareOutline)<{ color: 'success' | 'normal' }>`
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.color === 'success' &&
 | 
					    props.color === 'success' &&
 | 
				
			||||||
    css`
 | 
					    css`
 | 
				
			||||||
      fill: ${props.theme.colors.success};
 | 
					      fill: ${props.theme.colors.success};
 | 
				
			||||||
@@ -21,7 +30,7 @@ export const ChecklistIcon = styled(CheckSquareOutline)<{ color: 'success' | 'no
 | 
				
			|||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ClockIcon = styled(Clock)<{ color: string }>`
 | 
					export const ClockIcon = styled(Clock)<{ color: string }>`
 | 
				
			||||||
  fill: ${props => props.color};
 | 
					  fill: ${(props) => props.color};
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const EditorTextarea = styled(TextareaAutosize)`
 | 
					export const EditorTextarea = styled(TextareaAutosize)`
 | 
				
			||||||
@@ -40,7 +49,7 @@ export const EditorTextarea = styled(TextareaAutosize)`
 | 
				
			|||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 14px;
 | 
				
			||||||
  line-height: 18px;
 | 
					  line-height: 18px;
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  &:focus {
 | 
					  &:focus {
 | 
				
			||||||
    border: none;
 | 
					    border: none;
 | 
				
			||||||
    outline: none;
 | 
					    outline: none;
 | 
				
			||||||
@@ -54,6 +63,22 @@ export const ListCardBadges = styled.div`
 | 
				
			|||||||
  margin-left: -2px;
 | 
					  margin-left: -2px;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CommentsBadge = styled.div`
 | 
				
			||||||
 | 
					  color: #5e6c84;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  margin: 0 6px 4px 0;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  max-width: 100%;
 | 
				
			||||||
 | 
					  min-height: 20px;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  padding: 2px;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  text-overflow: ellipsis;
 | 
				
			||||||
 | 
					  vertical-align: top;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ListCardBadge = styled.div`
 | 
					export const ListCardBadge = styled.div`
 | 
				
			||||||
  color: #5e6c84;
 | 
					  color: #5e6c84;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
@@ -76,7 +101,7 @@ export const DescriptionBadge = styled(ListCardBadge)`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const DueDateCardBadge = styled(ListCardBadge)<{ isPastDue: boolean }>`
 | 
					export const DueDateCardBadge = styled(ListCardBadge)<{ isPastDue: boolean }>`
 | 
				
			||||||
  font-size: 12px;
 | 
					  font-size: 12px;
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.isPastDue &&
 | 
					    props.isPastDue &&
 | 
				
			||||||
    css`
 | 
					    css`
 | 
				
			||||||
      padding-left: 4px;
 | 
					      padding-left: 4px;
 | 
				
			||||||
@@ -91,7 +116,7 @@ export const ListCardBadgeText = styled.span<{ color?: 'success' | 'normal' }>`
 | 
				
			|||||||
  padding: 0 4px 0 6px;
 | 
					  padding: 0 4px 0 6px;
 | 
				
			||||||
  vertical-align: top;
 | 
					  vertical-align: top;
 | 
				
			||||||
  white-space: nowrap;
 | 
					  white-space: nowrap;
 | 
				
			||||||
  ${props => props.color === 'success' && `color: ${props.theme.colors.success};`}
 | 
					  ${(props) => props.color === 'success' && `color: ${props.theme.colors.success};`}
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ListCardContainer = styled.div<{ isActive: boolean; editable: boolean }>`
 | 
					export const ListCardContainer = styled.div<{ isActive: boolean; editable: boolean }>`
 | 
				
			||||||
@@ -102,7 +127,7 @@ export const ListCardContainer = styled.div<{ isActive: boolean; editable: boole
 | 
				
			|||||||
  cursor: pointer !important;
 | 
					  cursor: pointer !important;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  background-color: ${props =>
 | 
					  background-color: ${(props) =>
 | 
				
			||||||
    props.isActive && !props.editable
 | 
					    props.isActive && !props.editable
 | 
				
			||||||
      ? mixin.darken(props.theme.colors.bg.secondary, 0.1)
 | 
					      ? mixin.darken(props.theme.colors.bg.secondary, 0.1)
 | 
				
			||||||
      : `${props.theme.colors.bg.secondary}`};
 | 
					      : `${props.theme.colors.bg.secondary}`};
 | 
				
			||||||
@@ -119,7 +144,7 @@ export const ListCardDetails = styled.div<{ complete: boolean }>`
 | 
				
			|||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  z-index: 10;
 | 
					  z-index: 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ${props => props.complete && 'opacity: 0.6;'}
 | 
					  ${(props) => props.complete && 'opacity: 0.6;'}
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const labelVariantExpandAnimation = keyframes`
 | 
					const labelVariantExpandAnimation = keyframes`
 | 
				
			||||||
@@ -157,7 +182,7 @@ export const ListCardLabelsWrapper = styled.div`
 | 
				
			|||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ListCardLabel = styled.span<{ variant: 'small' | 'large' }>`
 | 
					export const ListCardLabel = styled.span<{ variant: 'small' | 'large' }>`
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.variant === 'small'
 | 
					    props.variant === 'small'
 | 
				
			||||||
      ? css`
 | 
					      ? css`
 | 
				
			||||||
          height: 8px;
 | 
					          height: 8px;
 | 
				
			||||||
@@ -183,14 +208,14 @@ export const ListCardLabel = styled.span<{ variant: 'small' | 'large' }>`
 | 
				
			|||||||
  color: #fff;
 | 
					  color: #fff;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  background-color: ${props => props.color};
 | 
					  background-color: ${(props) => props.color};
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ListCardLabels = styled.div<{ toggleLabels: boolean; toggleDirection: 'expand' | 'shrink' }>`
 | 
					export const ListCardLabels = styled.div<{ toggleLabels: boolean; toggleDirection: 'expand' | 'shrink' }>`
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    opacity: 0.8;
 | 
					    opacity: 0.8;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.toggleLabels &&
 | 
					    props.toggleLabels &&
 | 
				
			||||||
    props.toggleDirection === 'expand' &&
 | 
					    props.toggleDirection === 'expand' &&
 | 
				
			||||||
    css`
 | 
					    css`
 | 
				
			||||||
@@ -201,7 +226,7 @@ export const ListCardLabels = styled.div<{ toggleLabels: boolean; toggleDirectio
 | 
				
			|||||||
        animation: ${labelTextVariantExpandAnimation} 0.45s ease-out;
 | 
					        animation: ${labelTextVariantExpandAnimation} 0.45s ease-out;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    `}
 | 
					    `}
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.toggleLabels &&
 | 
					    props.toggleLabels &&
 | 
				
			||||||
    props.toggleDirection === 'shrink' &&
 | 
					    props.toggleDirection === 'shrink' &&
 | 
				
			||||||
    css`
 | 
					    css`
 | 
				
			||||||
@@ -225,7 +250,7 @@ export const ListCardOperation = styled.span`
 | 
				
			|||||||
  top: 2px;
 | 
					  top: 2px;
 | 
				
			||||||
  z-index: 100;
 | 
					  z-index: 100;
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    background-color: ${props => mixin.darken(props.theme.colors.bg.secondary, 0.25)};
 | 
					    background-color: ${(props) => mixin.darken(props.theme.colors.bg.secondary, 0.25)};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -234,7 +259,7 @@ export const CardTitle = styled.div`
 | 
				
			|||||||
  margin: 0 0 4px;
 | 
					  margin: 0 0 4px;
 | 
				
			||||||
  overflow: hidden;
 | 
					  overflow: hidden;
 | 
				
			||||||
  text-decoration: none;
 | 
					  text-decoration: none;
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  display: block;
 | 
					  display: block;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
@@ -251,7 +276,7 @@ export const CardMembers = styled.div`
 | 
				
			|||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CompleteIcon = styled(CheckCircle)`
 | 
					export const CompleteIcon = styled(CheckCircle)`
 | 
				
			||||||
  fill: ${props => props.theme.colors.success};
 | 
					  fill: ${(props) => props.theme.colors.success};
 | 
				
			||||||
  margin-right: 4px;
 | 
					  margin-right: 4px;
 | 
				
			||||||
  flex-shrink: 0;
 | 
					  flex-shrink: 0;
 | 
				
			||||||
  margin-bottom: -2px;
 | 
					  margin-bottom: -2px;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,8 @@ import {
 | 
				
			|||||||
  CardTitle,
 | 
					  CardTitle,
 | 
				
			||||||
  CardMembers,
 | 
					  CardMembers,
 | 
				
			||||||
  CardTitleText,
 | 
					  CardTitleText,
 | 
				
			||||||
 | 
					  CommentsIcon,
 | 
				
			||||||
 | 
					  CommentsBadge,
 | 
				
			||||||
} from './Styles';
 | 
					} from './Styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DueDate = {
 | 
					type DueDate = {
 | 
				
			||||||
@@ -47,6 +49,7 @@ type Props = {
 | 
				
			|||||||
  dueDate?: DueDate;
 | 
					  dueDate?: DueDate;
 | 
				
			||||||
  checklists?: Checklist | null;
 | 
					  checklists?: Checklist | null;
 | 
				
			||||||
  labels?: Array<ProjectLabel>;
 | 
					  labels?: Array<ProjectLabel>;
 | 
				
			||||||
 | 
					  comments?: { unread: boolean; total: number } | null;
 | 
				
			||||||
  watched?: boolean;
 | 
					  watched?: boolean;
 | 
				
			||||||
  wrapperProps?: any;
 | 
					  wrapperProps?: any;
 | 
				
			||||||
  members?: Array<TaskUser> | null;
 | 
					  members?: Array<TaskUser> | null;
 | 
				
			||||||
@@ -72,6 +75,7 @@ const Card = React.forwardRef(
 | 
				
			|||||||
      taskGroupID,
 | 
					      taskGroupID,
 | 
				
			||||||
      complete,
 | 
					      complete,
 | 
				
			||||||
      toggleLabels = false,
 | 
					      toggleLabels = false,
 | 
				
			||||||
 | 
					      comments,
 | 
				
			||||||
      toggleDirection = 'shrink',
 | 
					      toggleDirection = 'shrink',
 | 
				
			||||||
      setToggleLabels,
 | 
					      setToggleLabels,
 | 
				
			||||||
      onClick,
 | 
					      onClick,
 | 
				
			||||||
@@ -138,7 +142,7 @@ const Card = React.forwardRef(
 | 
				
			|||||||
        onMouseEnter={() => setActive(true)}
 | 
					        onMouseEnter={() => setActive(true)}
 | 
				
			||||||
        onMouseLeave={() => setActive(false)}
 | 
					        onMouseLeave={() => setActive(false)}
 | 
				
			||||||
        ref={$cardRef}
 | 
					        ref={$cardRef}
 | 
				
			||||||
        onClick={e => {
 | 
					        onClick={(e) => {
 | 
				
			||||||
          if (onClick) {
 | 
					          if (onClick) {
 | 
				
			||||||
            onClick(e);
 | 
					            onClick(e);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
@@ -151,7 +155,7 @@ const Card = React.forwardRef(
 | 
				
			|||||||
        <ListCardInnerContainer ref={$innerCardRef}>
 | 
					        <ListCardInnerContainer ref={$innerCardRef}>
 | 
				
			||||||
          {!isPublic && isActive && !editable && (
 | 
					          {!isPublic && isActive && !editable && (
 | 
				
			||||||
            <ListCardOperation
 | 
					            <ListCardOperation
 | 
				
			||||||
              onClick={e => {
 | 
					              onClick={(e) => {
 | 
				
			||||||
                e.stopPropagation();
 | 
					                e.stopPropagation();
 | 
				
			||||||
                if (onContextMenu) {
 | 
					                if (onContextMenu) {
 | 
				
			||||||
                  onContextMenu($innerCardRef, taskID, taskGroupID);
 | 
					                  onContextMenu($innerCardRef, taskID, taskGroupID);
 | 
				
			||||||
@@ -167,7 +171,7 @@ const Card = React.forwardRef(
 | 
				
			|||||||
                <ListCardLabels
 | 
					                <ListCardLabels
 | 
				
			||||||
                  toggleLabels={toggleLabels}
 | 
					                  toggleLabels={toggleLabels}
 | 
				
			||||||
                  toggleDirection={toggleDirection}
 | 
					                  toggleDirection={toggleDirection}
 | 
				
			||||||
                  onClick={e => {
 | 
					                  onClick={(e) => {
 | 
				
			||||||
                    e.stopPropagation();
 | 
					                    e.stopPropagation();
 | 
				
			||||||
                    if (onCardLabelClick) {
 | 
					                    if (onCardLabelClick) {
 | 
				
			||||||
                      onCardLabelClick();
 | 
					                      onCardLabelClick();
 | 
				
			||||||
@@ -177,7 +181,7 @@ const Card = React.forwardRef(
 | 
				
			|||||||
                  {labels
 | 
					                  {labels
 | 
				
			||||||
                    .slice()
 | 
					                    .slice()
 | 
				
			||||||
                    .sort((a, b) => a.labelColor.position - b.labelColor.position)
 | 
					                    .sort((a, b) => a.labelColor.position - b.labelColor.position)
 | 
				
			||||||
                    .map(label => (
 | 
					                    .map((label) => (
 | 
				
			||||||
                      <ListCardLabel
 | 
					                      <ListCardLabel
 | 
				
			||||||
                        onAnimationEnd={() => {
 | 
					                        onAnimationEnd={() => {
 | 
				
			||||||
                          if (setToggleLabels) {
 | 
					                          if (setToggleLabels) {
 | 
				
			||||||
@@ -198,13 +202,13 @@ const Card = React.forwardRef(
 | 
				
			|||||||
              <EditorContent>
 | 
					              <EditorContent>
 | 
				
			||||||
                {complete && <CompleteIcon width={16} height={16} />}
 | 
					                {complete && <CompleteIcon width={16} height={16} />}
 | 
				
			||||||
                <EditorTextarea
 | 
					                <EditorTextarea
 | 
				
			||||||
                  onChange={e => {
 | 
					                  onChange={(e) => {
 | 
				
			||||||
                    setCardTitle(e.currentTarget.value);
 | 
					                    setCardTitle(e.currentTarget.value);
 | 
				
			||||||
                    if (onCardTitleChange) {
 | 
					                    if (onCardTitleChange) {
 | 
				
			||||||
                      onCardTitleChange(e.currentTarget.value);
 | 
					                      onCardTitleChange(e.currentTarget.value);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                  }}
 | 
					                  }}
 | 
				
			||||||
                  onClick={e => {
 | 
					                  onClick={(e) => {
 | 
				
			||||||
                    e.stopPropagation();
 | 
					                    e.stopPropagation();
 | 
				
			||||||
                  }}
 | 
					                  }}
 | 
				
			||||||
                  onKeyDown={handleKeyDown}
 | 
					                  onKeyDown={handleKeyDown}
 | 
				
			||||||
@@ -235,6 +239,12 @@ const Card = React.forwardRef(
 | 
				
			|||||||
                  <List width={8} height={8} />
 | 
					                  <List width={8} height={8} />
 | 
				
			||||||
                </DescriptionBadge>
 | 
					                </DescriptionBadge>
 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
 | 
					              {comments && (
 | 
				
			||||||
 | 
					                <CommentsBadge>
 | 
				
			||||||
 | 
					                  <CommentsIcon color={comments.unread ? 'success' : 'normal'} width={8} height={8} />
 | 
				
			||||||
 | 
					                  <ListCardBadgeText color={comments.unread ? 'success' : 'normal'}>{comments.total}</ListCardBadgeText>
 | 
				
			||||||
 | 
					                </CommentsBadge>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
              {checklists && (
 | 
					              {checklists && (
 | 
				
			||||||
                <ListCardBadge>
 | 
					                <ListCardBadge>
 | 
				
			||||||
                  <ChecklistIcon
 | 
					                  <ChecklistIcon
 | 
				
			||||||
@@ -256,7 +266,7 @@ const Card = React.forwardRef(
 | 
				
			|||||||
                    size={28}
 | 
					                    size={28}
 | 
				
			||||||
                    zIndex={members.length - idx}
 | 
					                    zIndex={members.length - idx}
 | 
				
			||||||
                    member={member}
 | 
					                    member={member}
 | 
				
			||||||
                    onMemberProfile={$target => {
 | 
					                    onMemberProfile={($target) => {
 | 
				
			||||||
                      if (onCardMemberClick) {
 | 
					                      if (onCardMemberClick) {
 | 
				
			||||||
                        onCardMemberClick($target, taskID, member.id);
 | 
					                        onCardMemberClick($target, taskID, member.id);
 | 
				
			||||||
                      }
 | 
					                      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,24 +111,16 @@ function shouldStatusFilter(task: Task, filter: TaskStatusFilter) {
 | 
				
			|||||||
        const TODAY = REFERENCE.clone().startOf('day');
 | 
					        const TODAY = REFERENCE.clone().startOf('day');
 | 
				
			||||||
        return completedAt.isSame(TODAY, 'd');
 | 
					        return completedAt.isSame(TODAY, 'd');
 | 
				
			||||||
      case TaskSince.YESTERDAY:
 | 
					      case TaskSince.YESTERDAY:
 | 
				
			||||||
        const YESTERDAY = REFERENCE.clone()
 | 
					        const YESTERDAY = REFERENCE.clone().subtract(1, 'day').startOf('day');
 | 
				
			||||||
          .subtract(1, 'day')
 | 
					 | 
				
			||||||
          .startOf('day');
 | 
					 | 
				
			||||||
        return completedAt.isSameOrAfter(YESTERDAY, 'd');
 | 
					        return completedAt.isSameOrAfter(YESTERDAY, 'd');
 | 
				
			||||||
      case TaskSince.ONE_WEEK:
 | 
					      case TaskSince.ONE_WEEK:
 | 
				
			||||||
        const ONE_WEEK = REFERENCE.clone()
 | 
					        const ONE_WEEK = REFERENCE.clone().subtract(7, 'day').startOf('day');
 | 
				
			||||||
          .subtract(7, 'day')
 | 
					 | 
				
			||||||
          .startOf('day');
 | 
					 | 
				
			||||||
        return completedAt.isSameOrAfter(ONE_WEEK, 'd');
 | 
					        return completedAt.isSameOrAfter(ONE_WEEK, 'd');
 | 
				
			||||||
      case TaskSince.TWO_WEEKS:
 | 
					      case TaskSince.TWO_WEEKS:
 | 
				
			||||||
        const TWO_WEEKS = REFERENCE.clone()
 | 
					        const TWO_WEEKS = REFERENCE.clone().subtract(14, 'day').startOf('day');
 | 
				
			||||||
          .subtract(14, 'day')
 | 
					 | 
				
			||||||
          .startOf('day');
 | 
					 | 
				
			||||||
        return completedAt.isSameOrAfter(TWO_WEEKS, 'd');
 | 
					        return completedAt.isSameOrAfter(TWO_WEEKS, 'd');
 | 
				
			||||||
      case TaskSince.THREE_WEEKS:
 | 
					      case TaskSince.THREE_WEEKS:
 | 
				
			||||||
        const THREE_WEEKS = REFERENCE.clone()
 | 
					        const THREE_WEEKS = REFERENCE.clone().subtract(21, 'day').startOf('day');
 | 
				
			||||||
          .subtract(21, 'day')
 | 
					 | 
				
			||||||
          .startOf('day');
 | 
					 | 
				
			||||||
        return completedAt.isSameOrAfter(THREE_WEEKS, 'd');
 | 
					        return completedAt.isSameOrAfter(THREE_WEEKS, 'd');
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
@@ -203,14 +195,14 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
    let beforeDropDraggables: Array<DraggableElement> | null = null;
 | 
					    let beforeDropDraggables: Array<DraggableElement> | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (isList) {
 | 
					    if (isList) {
 | 
				
			||||||
      const droppedGroup = taskGroups.find(taskGroup => taskGroup.id === draggableId);
 | 
					      const droppedGroup = taskGroups.find((taskGroup) => taskGroup.id === draggableId);
 | 
				
			||||||
      if (droppedGroup) {
 | 
					      if (droppedGroup) {
 | 
				
			||||||
        droppedDraggable = {
 | 
					        droppedDraggable = {
 | 
				
			||||||
          id: draggableId,
 | 
					          id: draggableId,
 | 
				
			||||||
          position: droppedGroup.position,
 | 
					          position: droppedGroup.position,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        beforeDropDraggables = getSortedDraggables(
 | 
					        beforeDropDraggables = getSortedDraggables(
 | 
				
			||||||
          taskGroups.map(taskGroup => {
 | 
					          taskGroups.map((taskGroup) => {
 | 
				
			||||||
            return { id: taskGroup.id, position: taskGroup.position };
 | 
					            return { id: taskGroup.id, position: taskGroup.position };
 | 
				
			||||||
          }),
 | 
					          }),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@@ -234,13 +226,13 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      const curTaskGroup = taskGroups.findIndex(
 | 
					      const curTaskGroup = taskGroups.findIndex(
 | 
				
			||||||
        taskGroup => taskGroup.tasks.findIndex(task => task.id === draggableId) !== -1,
 | 
					        (taskGroup) => taskGroup.tasks.findIndex((task) => task.id === draggableId) !== -1,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      let targetTaskGroup = curTaskGroup;
 | 
					      let targetTaskGroup = curTaskGroup;
 | 
				
			||||||
      if (!isSameList) {
 | 
					      if (!isSameList) {
 | 
				
			||||||
        targetTaskGroup = taskGroups.findIndex(taskGroup => taskGroup.id === destination.droppableId);
 | 
					        targetTaskGroup = taskGroups.findIndex((taskGroup) => taskGroup.id === destination.droppableId);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      const droppedTask = taskGroups[curTaskGroup].tasks.find(task => task.id === draggableId);
 | 
					      const droppedTask = taskGroups[curTaskGroup].tasks.find((task) => task.id === draggableId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (droppedTask) {
 | 
					      if (droppedTask) {
 | 
				
			||||||
        droppedDraggable = {
 | 
					        droppedDraggable = {
 | 
				
			||||||
@@ -248,7 +240,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
          position: droppedTask.position,
 | 
					          position: droppedTask.position,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        beforeDropDraggables = getSortedDraggables(
 | 
					        beforeDropDraggables = getSortedDraggables(
 | 
				
			||||||
          taskGroups[targetTaskGroup].tasks.map(task => {
 | 
					          taskGroups[targetTaskGroup].tasks.map((task) => {
 | 
				
			||||||
            return { id: task.id, position: task.position };
 | 
					            return { id: task.id, position: task.position };
 | 
				
			||||||
          }),
 | 
					          }),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@@ -286,7 +278,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
      <BoardWrapper>
 | 
					      <BoardWrapper>
 | 
				
			||||||
        <DragDropContext onDragEnd={onDragEnd}>
 | 
					        <DragDropContext onDragEnd={onDragEnd}>
 | 
				
			||||||
          <Droppable direction="horizontal" type="column" droppableId="root">
 | 
					          <Droppable direction="horizontal" type="column" droppableId="root">
 | 
				
			||||||
            {provided => (
 | 
					            {(provided) => (
 | 
				
			||||||
              <Container {...provided.droppableProps} ref={provided.innerRef}>
 | 
					              <Container {...provided.droppableProps} ref={provided.innerRef}>
 | 
				
			||||||
                {taskGroups
 | 
					                {taskGroups
 | 
				
			||||||
                  .slice()
 | 
					                  .slice()
 | 
				
			||||||
@@ -294,14 +286,14 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
                  .map((taskGroup: TaskGroup, index: number) => {
 | 
					                  .map((taskGroup: TaskGroup, index: number) => {
 | 
				
			||||||
                    return (
 | 
					                    return (
 | 
				
			||||||
                      <Draggable draggableId={taskGroup.id} key={taskGroup.id} index={index}>
 | 
					                      <Draggable draggableId={taskGroup.id} key={taskGroup.id} index={index}>
 | 
				
			||||||
                        {columnDragProvided => (
 | 
					                        {(columnDragProvided) => (
 | 
				
			||||||
                          <Droppable type="tasks" droppableId={taskGroup.id}>
 | 
					                          <Droppable type="tasks" droppableId={taskGroup.id}>
 | 
				
			||||||
                            {(columnDropProvided, snapshot) => (
 | 
					                            {(columnDropProvided, snapshot) => (
 | 
				
			||||||
                              <List
 | 
					                              <List
 | 
				
			||||||
                                name={taskGroup.name}
 | 
					                                name={taskGroup.name}
 | 
				
			||||||
                                onOpenComposer={id => setCurrentComposer(id)}
 | 
					                                onOpenComposer={(id) => setCurrentComposer(id)}
 | 
				
			||||||
                                isComposerOpen={currentComposer === taskGroup.id}
 | 
					                                isComposerOpen={currentComposer === taskGroup.id}
 | 
				
			||||||
                                onSaveName={name => onChangeTaskGroupName(taskGroup.id, name)}
 | 
					                                onSaveName={(name) => onChangeTaskGroupName(taskGroup.id, name)}
 | 
				
			||||||
                                isPublic={isPublic}
 | 
					                                isPublic={isPublic}
 | 
				
			||||||
                                ref={columnDragProvided.innerRef}
 | 
					                                ref={columnDragProvided.innerRef}
 | 
				
			||||||
                                wrapperProps={columnDragProvided.draggableProps}
 | 
					                                wrapperProps={columnDragProvided.draggableProps}
 | 
				
			||||||
@@ -314,8 +306,8 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
                                <ListCards ref={columnDropProvided.innerRef} {...columnDropProvided.droppableProps}>
 | 
					                                <ListCards ref={columnDropProvided.innerRef} {...columnDropProvided.droppableProps}>
 | 
				
			||||||
                                  {taskGroup.tasks
 | 
					                                  {taskGroup.tasks
 | 
				
			||||||
                                    .slice()
 | 
					                                    .slice()
 | 
				
			||||||
                                    .filter(t => shouldStatusFilter(t, taskStatusFilter))
 | 
					                                    .filter((t) => shouldStatusFilter(t, taskStatusFilter))
 | 
				
			||||||
                                    .filter(t => shouldMetaFilter(t, taskMetaFilters))
 | 
					                                    .filter((t) => shouldMetaFilter(t, taskMetaFilters))
 | 
				
			||||||
                                    .sort((a: any, b: any) => a.position - b.position)
 | 
					                                    .sort((a: any, b: any) => a.position - b.position)
 | 
				
			||||||
                                    .sort((a: any, b: any) => sortTasks(a, b, taskSorting))
 | 
					                                    .sort((a: any, b: any) => sortTasks(a, b, taskSorting))
 | 
				
			||||||
                                    .map((task: Task, taskIndex: any) => {
 | 
					                                    .map((task: Task, taskIndex: any) => {
 | 
				
			||||||
@@ -326,7 +318,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
                                          index={taskIndex}
 | 
					                                          index={taskIndex}
 | 
				
			||||||
                                          isDragDisabled={taskSorting.type !== TaskSortingType.NONE}
 | 
					                                          isDragDisabled={taskSorting.type !== TaskSortingType.NONE}
 | 
				
			||||||
                                        >
 | 
					                                        >
 | 
				
			||||||
                                          {taskProvided => {
 | 
					                                          {(taskProvided) => {
 | 
				
			||||||
                                            return (
 | 
					                                            return (
 | 
				
			||||||
                                              <Card
 | 
					                                              <Card
 | 
				
			||||||
                                                toggleDirection={toggleDirection}
 | 
					                                                toggleDirection={toggleDirection}
 | 
				
			||||||
@@ -352,7 +344,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
                                                complete={task.complete ?? false}
 | 
					                                                complete={task.complete ?? false}
 | 
				
			||||||
                                                taskGroupID={taskGroup.id}
 | 
					                                                taskGroupID={taskGroup.id}
 | 
				
			||||||
                                                description=""
 | 
					                                                description=""
 | 
				
			||||||
                                                labels={task.labels.map(label => label.projectLabel)}
 | 
					                                                labels={task.labels.map((label) => label.projectLabel)}
 | 
				
			||||||
                                                dueDate={
 | 
					                                                dueDate={
 | 
				
			||||||
                                                  task.dueDate
 | 
					                                                  task.dueDate
 | 
				
			||||||
                                                    ? {
 | 
					                                                    ? {
 | 
				
			||||||
@@ -367,6 +359,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
                                                  onTaskClick(task);
 | 
					                                                  onTaskClick(task);
 | 
				
			||||||
                                                }}
 | 
					                                                }}
 | 
				
			||||||
                                                checklists={task.badges && task.badges.checklist}
 | 
					                                                checklists={task.badges && task.badges.checklist}
 | 
				
			||||||
 | 
					                                                comments={task.badges && task.badges.comments}
 | 
				
			||||||
                                                onCardMemberClick={onCardMemberClick}
 | 
					                                                onCardMemberClick={onCardMemberClick}
 | 
				
			||||||
                                                onContextMenu={onQuickEditorOpen}
 | 
					                                                onContextMenu={onQuickEditorOpen}
 | 
				
			||||||
                                              />
 | 
					                                              />
 | 
				
			||||||
@@ -381,7 +374,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
                                      onClose={() => {
 | 
					                                      onClose={() => {
 | 
				
			||||||
                                        setCurrentComposer('');
 | 
					                                        setCurrentComposer('');
 | 
				
			||||||
                                      }}
 | 
					                                      }}
 | 
				
			||||||
                                      onCreateCard={name => {
 | 
					                                      onCreateCard={(name) => {
 | 
				
			||||||
                                        onCreateTask(taskGroup.id, name);
 | 
					                                        onCreateTask(taskGroup.id, name);
 | 
				
			||||||
                                      }}
 | 
					                                      }}
 | 
				
			||||||
                                      isOpen
 | 
					                                      isOpen
 | 
				
			||||||
@@ -402,7 +395,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
        </DragDropContext>
 | 
					        </DragDropContext>
 | 
				
			||||||
        {!isPublic && (
 | 
					        {!isPublic && (
 | 
				
			||||||
          <AddList
 | 
					          <AddList
 | 
				
			||||||
            onSave={listName => {
 | 
					            onSave={(listName) => {
 | 
				
			||||||
              onCreateTaskGroup(listName);
 | 
					              onCreateTaskGroup(listName);
 | 
				
			||||||
            }}
 | 
					            }}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,14 +22,14 @@ export const MarkCompleteButton = styled.button<{ invert: boolean; disabled?: bo
 | 
				
			|||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  border-radius: ${props => props.theme.borderRadius.alternate};
 | 
					  border-radius: ${(props) => props.theme.borderRadius.alternate};
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  background: transparent;
 | 
					  background: transparent;
 | 
				
			||||||
  & span {
 | 
					  & span {
 | 
				
			||||||
    margin-left: 4px;
 | 
					    margin-left: 4px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.invert
 | 
					    props.invert
 | 
				
			||||||
      ? css`
 | 
					      ? css`
 | 
				
			||||||
          background: ${props.theme.colors.success};
 | 
					          background: ${props.theme.colors.success};
 | 
				
			||||||
@@ -63,7 +63,7 @@ export const MarkCompleteButton = styled.button<{ invert: boolean; disabled?: bo
 | 
				
			|||||||
            color: ${props.theme.colors.success};
 | 
					            color: ${props.theme.colors.success};
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        `}
 | 
					        `}
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.invert &&
 | 
					    props.invert &&
 | 
				
			||||||
    css`
 | 
					    css`
 | 
				
			||||||
      opacity: 0.6;
 | 
					      opacity: 0.6;
 | 
				
			||||||
@@ -89,7 +89,7 @@ export const SidebarTitle = styled.div`
 | 
				
			|||||||
  font-size: 12px;
 | 
					  font-size: 12px;
 | 
				
			||||||
  min-height: 24px;
 | 
					  min-height: 24px;
 | 
				
			||||||
  margin-left: 8px;
 | 
					  margin-left: 8px;
 | 
				
			||||||
  color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.75)};
 | 
					  color: ${(props) => mixin.rgba(props.theme.colors.text.primary, 0.75)};
 | 
				
			||||||
  padding-top: 4px;
 | 
					  padding-top: 4px;
 | 
				
			||||||
  letter-spacing: 0.5px;
 | 
					  letter-spacing: 0.5px;
 | 
				
			||||||
  text-transform: uppercase;
 | 
					  text-transform: uppercase;
 | 
				
			||||||
@@ -110,12 +110,12 @@ export const skeletonKeyframes = keyframes`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const SidebarButton = styled.div<{ $loading?: boolean }>`
 | 
					export const SidebarButton = styled.div<{ $loading?: boolean }>`
 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 14px;
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  min-height: 32px;
 | 
					  min-height: 32px;
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  border-radius: 6px;
 | 
					  border-radius: 6px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.$loading
 | 
					    props.$loading
 | 
				
			||||||
      ? css`
 | 
					      ? css`
 | 
				
			||||||
          background: ${props.theme.colors.bg.primary};
 | 
					          background: ${props.theme.colors.bg.primary};
 | 
				
			||||||
@@ -183,7 +183,7 @@ export const TaskDetailsTitleWrapper = styled.div<{ $loading?: boolean }>`
 | 
				
			|||||||
  margin: 8px 0 4px 0;
 | 
					  margin: 8px 0 4px 0;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  border-radius: 6px;
 | 
					  border-radius: 6px;
 | 
				
			||||||
  ${props => props.$loading && `background: ${props.theme.colors.bg.primary};`}
 | 
					  ${(props) => props.$loading && `background: ${props.theme.colors.bg.primary};`}
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskDetailsTitle = styled(TextareaAutosize)<{ $loading?: boolean }>`
 | 
					export const TaskDetailsTitle = styled(TextareaAutosize)<{ $loading?: boolean }>`
 | 
				
			||||||
@@ -201,7 +201,7 @@ export const TaskDetailsTitle = styled(TextareaAutosize)<{ $loading?: boolean }>
 | 
				
			|||||||
  &:disabled {
 | 
					  &:disabled {
 | 
				
			||||||
    opacity: 1;
 | 
					    opacity: 1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.$loading
 | 
					    props.$loading
 | 
				
			||||||
      ? css`
 | 
					      ? css`
 | 
				
			||||||
          background-image: linear-gradient(90deg, ${defaultBaseColor}, ${defaultHighlightColor}, ${defaultBaseColor});
 | 
					          background-image: linear-gradient(90deg, ${defaultBaseColor}, ${defaultHighlightColor}, ${defaultBaseColor});
 | 
				
			||||||
@@ -226,7 +226,7 @@ export const DueDateTitle = styled.div`
 | 
				
			|||||||
  font-size: 12px;
 | 
					  font-size: 12px;
 | 
				
			||||||
  min-height: 24px;
 | 
					  min-height: 24px;
 | 
				
			||||||
  margin-left: 8px;
 | 
					  margin-left: 8px;
 | 
				
			||||||
  color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.75)};
 | 
					  color: ${(props) => mixin.rgba(props.theme.colors.text.primary, 0.75)};
 | 
				
			||||||
  padding-top: 8px;
 | 
					  padding-top: 8px;
 | 
				
			||||||
  letter-spacing: 0.5px;
 | 
					  letter-spacing: 0.5px;
 | 
				
			||||||
  text-transform: uppercase;
 | 
					  text-transform: uppercase;
 | 
				
			||||||
@@ -237,7 +237,7 @@ export const AssignedUsersSection = styled.div`
 | 
				
			|||||||
  padding-right: 32px;
 | 
					  padding-right: 32px;
 | 
				
			||||||
  padding-top: 24px;
 | 
					  padding-top: 24px;
 | 
				
			||||||
  padding-bottom: 24px;
 | 
					  padding-bottom: 24px;
 | 
				
			||||||
  border-bottom: 1px solid ${props => props.theme.colors.alternate};
 | 
					  border-bottom: 1px solid ${(props) => props.theme.colors.alternate};
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
@@ -255,10 +255,10 @@ export const AssignUserIcon = styled.div`
 | 
				
			|||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    border: 1px solid ${props => mixin.rgba(props.theme.colors.text.secondary, 0.75)};
 | 
					    border: 1px solid ${(props) => mixin.rgba(props.theme.colors.text.secondary, 0.75)};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  &:hover svg {
 | 
					  &:hover svg {
 | 
				
			||||||
    fill: ${props => mixin.rgba(props.theme.colors.text.secondary, 0.75)};
 | 
					    fill: ${(props) => mixin.rgba(props.theme.colors.text.secondary, 0.75)};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -273,17 +273,17 @@ export const AssignUsersButton = styled.div`
 | 
				
			|||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  border: 1px solid transparent;
 | 
					  border: 1px solid transparent;
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    border: 1px solid ${props => mixin.darken(props.theme.colors.alternate, 0.15)};
 | 
					    border: 1px solid ${(props) => mixin.darken(props.theme.colors.alternate, 0.15)};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  &:hover ${AssignUserIcon} {
 | 
					  &:hover ${AssignUserIcon} {
 | 
				
			||||||
    border: 1px solid ${props => props.theme.colors.alternate};
 | 
					    border: 1px solid ${(props) => props.theme.colors.alternate};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const AssignUserLabel = styled.span`
 | 
					export const AssignUserLabel = styled.span`
 | 
				
			||||||
  flex: 1 1 auto;
 | 
					  flex: 1 1 auto;
 | 
				
			||||||
  line-height: 15px;
 | 
					  line-height: 15px;
 | 
				
			||||||
  color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.75)};
 | 
					  color: ${(props) => mixin.rgba(props.theme.colors.text.primary, 0.75)};
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ExtraActionsSection = styled.div`
 | 
					export const ExtraActionsSection = styled.div`
 | 
				
			||||||
@@ -295,7 +295,7 @@ export const ExtraActionsSection = styled.div`
 | 
				
			|||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ActionButtonsTitle = styled.h3`
 | 
					export const ActionButtonsTitle = styled.h3`
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  font-size: 12px;
 | 
					  font-size: 12px;
 | 
				
			||||||
  font-weight: 500;
 | 
					  font-weight: 500;
 | 
				
			||||||
  letter-spacing: 0.04em;
 | 
					  letter-spacing: 0.04em;
 | 
				
			||||||
@@ -305,7 +305,7 @@ export const ActionButton = styled(Button)`
 | 
				
			|||||||
  margin-top: 8px;
 | 
					  margin-top: 8px;
 | 
				
			||||||
  margin-left: -10px;
 | 
					  margin-left: -10px;
 | 
				
			||||||
  padding: 8px 16px;
 | 
					  padding: 8px 16px;
 | 
				
			||||||
  background: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.5)};
 | 
					  background: ${(props) => mixin.rgba(props.theme.colors.bg.primary, 0.5)};
 | 
				
			||||||
  text-align: left;
 | 
					  text-align: left;
 | 
				
			||||||
  transition: transform 0.2s ease;
 | 
					  transition: transform 0.2s ease;
 | 
				
			||||||
  & span {
 | 
					  & span {
 | 
				
			||||||
@@ -314,7 +314,7 @@ export const ActionButton = styled(Button)`
 | 
				
			|||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    box-shadow: none;
 | 
					    box-shadow: none;
 | 
				
			||||||
    transform: translateX(4px);
 | 
					    transform: translateX(4px);
 | 
				
			||||||
    background: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.75)};
 | 
					    background: ${(props) => mixin.rgba(props.theme.colors.bg.primary, 0.75)};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -333,10 +333,10 @@ export const HeaderActionIcon = styled.div`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  svg {
 | 
					  svg {
 | 
				
			||||||
    fill: ${props => mixin.rgba(props.theme.colors.text.primary, 0.75)};
 | 
					    fill: ${(props) => mixin.rgba(props.theme.colors.text.primary, 0.75)};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  &:hover svg {
 | 
					  &:hover svg {
 | 
				
			||||||
    fill: ${props => mixin.rgba(props.theme.colors.primary, 0.75)});
 | 
					    fill: ${(props) => mixin.rgba(props.theme.colors.primary, 0.75)});
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -393,7 +393,7 @@ export const MetaDetail = styled.div`
 | 
				
			|||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const MetaDetailTitle = styled.h3`
 | 
					export const MetaDetailTitle = styled.h3`
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  font-size: 12px;
 | 
					  font-size: 12px;
 | 
				
			||||||
  font-weight: 500;
 | 
					  font-weight: 500;
 | 
				
			||||||
  letter-spacing: 0.04em;
 | 
					  letter-spacing: 0.04em;
 | 
				
			||||||
@@ -412,7 +412,7 @@ export const MetaDetailContent = styled.div`
 | 
				
			|||||||
`;
 | 
					`;
 | 
				
			||||||
export const TaskDetailsAddLabel = styled.div`
 | 
					export const TaskDetailsAddLabel = styled.div`
 | 
				
			||||||
  border-radius: 3px;
 | 
					  border-radius: 3px;
 | 
				
			||||||
  background: ${props => mixin.darken(props.theme.colors.bg.secondary, 0.15)};
 | 
					  background: ${(props) => mixin.darken(props.theme.colors.bg.secondary, 0.15)};
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    opacity: 0.8;
 | 
					    opacity: 0.8;
 | 
				
			||||||
@@ -427,7 +427,7 @@ export const TaskDetailsAddLabelIcon = styled.div`
 | 
				
			|||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
  border-radius: 3px;
 | 
					  border-radius: 3px;
 | 
				
			||||||
  background: ${props => mixin.darken(props.theme.colors.bg.secondary, 0.15)};
 | 
					  background: ${(props) => mixin.darken(props.theme.colors.bg.secondary, 0.15)};
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    opacity: 0.8;
 | 
					    opacity: 0.8;
 | 
				
			||||||
@@ -443,7 +443,7 @@ export const TaskDetailLabel = styled.div<{ color: string }>`
 | 
				
			|||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    opacity: 0.8;
 | 
					    opacity: 0.8;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  background-color: ${props => props.color};
 | 
					  background-color: ${(props) => props.color};
 | 
				
			||||||
  color: #fff;
 | 
					  color: #fff;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
@@ -496,17 +496,22 @@ export const TabBarSection = styled.div`
 | 
				
			|||||||
  margin-top: 2px;
 | 
					  margin-top: 2px;
 | 
				
			||||||
  padding-left: 23px;
 | 
					  padding-left: 23px;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
  text-transform: uppercase;
 | 
					  text-transform: uppercase;
 | 
				
			||||||
  min-height: 35px;
 | 
					  min-height: 35px;
 | 
				
			||||||
  border-bottom: 1px solid #414561;
 | 
					  border-bottom: 1px solid #414561;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TabBarItem = styled.div`
 | 
					export const TabBarItem = styled.div`
 | 
				
			||||||
  box-shadow: inset 0 -2px ${props => props.theme.colors.primary};
 | 
					  box-shadow: inset 0 -2px ${(props) => props.theme.colors.primary};
 | 
				
			||||||
  padding: 12px 7px 14px 7px;
 | 
					  padding: 12px 7px 14px 7px;
 | 
				
			||||||
  margin-bottom: -1px;
 | 
					  margin-bottom: -1px;
 | 
				
			||||||
  margin-right: 36px;
 | 
					  margin-right: 36px;
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TabBarButton = styled(Button)`
 | 
				
			||||||
 | 
					  padding: 6px 12px;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CommentContainer = styled.div`
 | 
					export const CommentContainer = styled.div`
 | 
				
			||||||
@@ -542,13 +547,13 @@ export const CommentTextArea = styled(TextareaAutosize)<{ $showCommentActions: b
 | 
				
			|||||||
  line-height: 28px;
 | 
					  line-height: 28px;
 | 
				
			||||||
  padding: 4px 6px;
 | 
					  padding: 4px 6px;
 | 
				
			||||||
  border-radius: 6px;
 | 
					  border-radius: 6px;
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  background: #1f243e;
 | 
					  background: #1f243e;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  transition: max-height 200ms, height 200ms, min-height 200ms;
 | 
					  transition: max-height 200ms, height 200ms, min-height 200ms;
 | 
				
			||||||
  min-height: 36px;
 | 
					  min-height: 36px;
 | 
				
			||||||
  max-height: 36px;
 | 
					  max-height: 36px;
 | 
				
			||||||
  ${props =>
 | 
					  ${(props) =>
 | 
				
			||||||
    props.$showCommentActions
 | 
					    props.$showCommentActions
 | 
				
			||||||
      ? css`
 | 
					      ? css`
 | 
				
			||||||
          min-height: 80px;
 | 
					          min-height: 80px;
 | 
				
			||||||
@@ -561,7 +566,7 @@ export const CommentTextArea = styled(TextareaAutosize)<{ $showCommentActions: b
 | 
				
			|||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CommentEditorActions = styled.div<{ visible: boolean }>`
 | 
					export const CommentEditorActions = styled.div<{ visible: boolean }>`
 | 
				
			||||||
  display: ${props => (props.visible ? 'flex' : 'none')};
 | 
					  display: ${(props) => (props.visible ? 'flex' : 'none')};
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  padding: 5px 5px 5px 9px;
 | 
					  padding: 5px 5px 5px 9px;
 | 
				
			||||||
  border-top: 1px solid #414561;
 | 
					  border-top: 1px solid #414561;
 | 
				
			||||||
@@ -594,7 +599,7 @@ export const ActivityItemCommentAction = styled.div`
 | 
				
			|||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  svg {
 | 
					  svg {
 | 
				
			||||||
    fill: ${props => props.theme.colors.text.primary} !important;
 | 
					    fill: ${(props) => props.theme.colors.text.primary} !important;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -614,7 +619,7 @@ export const ActivityItemHeader = styled.div<{ editable?: boolean }>`
 | 
				
			|||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
  padding-left: 8px;
 | 
					  padding-left: 8px;
 | 
				
			||||||
  ${props => props.editable && 'width: 100%;'}
 | 
					  ${(props) => props.editable && 'width: 100%;'}
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
export const ActivityItemHeaderUser = styled(TaskAssignee)`
 | 
					export const ActivityItemHeaderUser = styled(TaskAssignee)`
 | 
				
			||||||
  align-items: start;
 | 
					  align-items: start;
 | 
				
			||||||
@@ -623,7 +628,7 @@ export const ActivityItemHeaderUser = styled(TaskAssignee)`
 | 
				
			|||||||
export const ActivityItemHeaderTitle = styled.div`
 | 
					export const ActivityItemHeaderTitle = styled.div`
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  padding-bottom: 2px;
 | 
					  padding-bottom: 2px;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -634,8 +639,8 @@ export const ActivityItemHeaderTitleName = styled.span`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const ActivityItemTimestamp = styled.span<{ margin: number }>`
 | 
					export const ActivityItemTimestamp = styled.span<{ margin: number }>`
 | 
				
			||||||
  font-size: 12px;
 | 
					  font-size: 12px;
 | 
				
			||||||
  color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.65)};
 | 
					  color: ${(props) => mixin.rgba(props.theme.colors.text.primary, 0.65)};
 | 
				
			||||||
  margin-left: ${props => props.margin}px;
 | 
					  margin-left: ${(props) => props.margin}px;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ActivityItemDetails = styled.div`
 | 
					export const ActivityItemDetails = styled.div`
 | 
				
			||||||
@@ -649,11 +654,11 @@ export const ActivityItemComment = styled.div<{ editable: boolean }>`
 | 
				
			|||||||
  border-radius: 3px;
 | 
					  border-radius: 3px;
 | 
				
			||||||
  ${mixin.boxShadowCard}
 | 
					  ${mixin.boxShadowCard}
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  padding: 8px 12px;
 | 
					  padding: 8px 12px;
 | 
				
			||||||
  margin: 4px 0;
 | 
					  margin: 4px 0;
 | 
				
			||||||
  background-color: ${props => mixin.darken(props.theme.colors.alternate, 0.1)};
 | 
					  background-color: ${(props) => mixin.darken(props.theme.colors.alternate, 0.1)};
 | 
				
			||||||
  ${props => props.editable && 'width: 100%;'}
 | 
					  ${(props) => props.editable && 'width: 100%;'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & span {
 | 
					  & span {
 | 
				
			||||||
    display: inline-flex;
 | 
					    display: inline-flex;
 | 
				
			||||||
@@ -683,7 +688,7 @@ export const ActivityItemCommentActions = styled.div`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const ActivityItemLog = styled.span`
 | 
					export const ActivityItemLog = styled.span`
 | 
				
			||||||
  margin-left: 2px;
 | 
					  margin-left: 2px;
 | 
				
			||||||
  color: ${props => props.theme.colors.text.primary};
 | 
					  color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ViewRawButton = styled.button`
 | 
					export const ViewRawButton = styled.button`
 | 
				
			||||||
@@ -694,9 +699,9 @@ export const ViewRawButton = styled.button`
 | 
				
			|||||||
  right: 4px;
 | 
					  right: 4px;
 | 
				
			||||||
  bottom: -24px;
 | 
					  bottom: -24px;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.25)};
 | 
					  color: ${(props) => mixin.rgba(props.theme.colors.text.primary, 0.25)};
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    color: ${props => props.theme.colors.text.primary};
 | 
					    color: ${(props) => props.theme.colors.text.primary};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,6 +79,7 @@ import {
 | 
				
			|||||||
  ActivityItemHeaderTitle,
 | 
					  ActivityItemHeaderTitle,
 | 
				
			||||||
  ActivityItemHeaderTitleName,
 | 
					  ActivityItemHeaderTitleName,
 | 
				
			||||||
  ActivityItemComment,
 | 
					  ActivityItemComment,
 | 
				
			||||||
 | 
					  TabBarButton,
 | 
				
			||||||
} from './Styles';
 | 
					} from './Styles';
 | 
				
			||||||
import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist';
 | 
					import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist';
 | 
				
			||||||
import onDragEnd from './onDragEnd';
 | 
					import onDragEnd from './onDragEnd';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,6 +69,12 @@ export type ChecklistBadge = {
 | 
				
			|||||||
  total: Scalars['Int'];
 | 
					  total: Scalars['Int'];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CommentsBadge = {
 | 
				
			||||||
 | 
					  __typename?: 'CommentsBadge';
 | 
				
			||||||
 | 
					  total: Scalars['Int'];
 | 
				
			||||||
 | 
					  unread: Scalars['Boolean'];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type CreateTaskChecklist = {
 | 
					export type CreateTaskChecklist = {
 | 
				
			||||||
  taskID: Scalars['UUID'];
 | 
					  taskID: Scalars['UUID'];
 | 
				
			||||||
  name: Scalars['String'];
 | 
					  name: Scalars['String'];
 | 
				
			||||||
@@ -1027,6 +1033,7 @@ export type TaskActivityData = {
 | 
				
			|||||||
export type TaskBadges = {
 | 
					export type TaskBadges = {
 | 
				
			||||||
  __typename?: 'TaskBadges';
 | 
					  __typename?: 'TaskBadges';
 | 
				
			||||||
  checklist?: Maybe<ChecklistBadge>;
 | 
					  checklist?: Maybe<ChecklistBadge>;
 | 
				
			||||||
 | 
					  comments?: Maybe<CommentsBadge>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TaskChecklist = {
 | 
					export type TaskChecklist = {
 | 
				
			||||||
@@ -1592,6 +1599,9 @@ export type TaskFieldsFragment = (
 | 
				
			|||||||
    & { checklist?: Maybe<(
 | 
					    & { checklist?: Maybe<(
 | 
				
			||||||
      { __typename?: 'ChecklistBadge' }
 | 
					      { __typename?: 'ChecklistBadge' }
 | 
				
			||||||
      & Pick<ChecklistBadge, 'complete' | 'total'>
 | 
					      & Pick<ChecklistBadge, 'complete' | 'total'>
 | 
				
			||||||
 | 
					    )>, comments?: Maybe<(
 | 
				
			||||||
 | 
					      { __typename?: 'CommentsBadge' }
 | 
				
			||||||
 | 
					      & Pick<CommentsBadge, 'unread' | 'total'>
 | 
				
			||||||
    )> }
 | 
					    )> }
 | 
				
			||||||
  ), taskGroup: (
 | 
					  ), taskGroup: (
 | 
				
			||||||
    { __typename?: 'TaskGroup' }
 | 
					    { __typename?: 'TaskGroup' }
 | 
				
			||||||
@@ -2693,6 +2703,10 @@ export const TaskFieldsFragmentDoc = gql`
 | 
				
			|||||||
      complete
 | 
					      complete
 | 
				
			||||||
      total
 | 
					      total
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    comments {
 | 
				
			||||||
 | 
					      unread
 | 
				
			||||||
 | 
					      total
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  taskGroup {
 | 
					  taskGroup {
 | 
				
			||||||
    id
 | 
					    id
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,10 @@ const TASK_FRAGMENT = gql`
 | 
				
			|||||||
        complete
 | 
					        complete
 | 
				
			||||||
        total
 | 
					        total
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      comments {
 | 
				
			||||||
 | 
					        unread
 | 
				
			||||||
 | 
					        total
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    taskGroup {
 | 
					    taskGroup {
 | 
				
			||||||
      id
 | 
					      id
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								frontend/src/shared/icons/Bubble.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								frontend/src/shared/icons/Bubble.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import Icon, { IconProps } from './Icon';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Bubble: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Icon width={width} height={height} className={className} viewBox="0 0 576 512">
 | 
				
			||||||
 | 
					      <path d="M416 192c0-88.4-93.1-160-208-160S0 103.6 0 192c0 34.3 14.1 65.9 38 92-13.4 30.2-35.5 54.2-35.8 54.5-2.2 2.3-2.8 5.7-1.5 8.7S4.8 352 8 352c36.6 0 66.9-12.3 88.7-25 32.2 15.7 70.3 25 111.3 25 114.9 0 208-71.6 208-160zm122 220c23.9-26 38-57.7 38-92 0-66.9-53.5-124.2-129.3-148.1.9 6.6 1.3 13.3 1.3 20.1 0 105.9-107.7 192-240 192-10.8 0-21.3-.8-31.7-1.9C207.8 439.6 281.8 480 368 480c41 0 79.1-9.2 111.3-25 21.8 12.7 52.1 25 88.7 25 3.2 0 6.1-1.9 7.3-4.8 1.3-2.9.7-6.3-1.5-8.7-.3-.3-22.4-24.2-35.8-54.5z" />
 | 
				
			||||||
 | 
					    </Icon>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Bubble;
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import Cross from './Cross';
 | 
					import Cross from './Cross';
 | 
				
			||||||
import Cog from './Cog';
 | 
					import Cog from './Cog';
 | 
				
			||||||
import Cogs from './Cogs';
 | 
					import Cogs from './Cogs';
 | 
				
			||||||
 | 
					import Bubble from './Bubble';
 | 
				
			||||||
import ArrowDown from './ArrowDown';
 | 
					import ArrowDown from './ArrowDown';
 | 
				
			||||||
import CheckCircleOutline from './CheckCircleOutline';
 | 
					import CheckCircleOutline from './CheckCircleOutline';
 | 
				
			||||||
import Briefcase from './Briefcase';
 | 
					import Briefcase from './Briefcase';
 | 
				
			||||||
@@ -110,5 +111,6 @@ export {
 | 
				
			|||||||
  Briefcase,
 | 
					  Briefcase,
 | 
				
			||||||
  DotCircle,
 | 
					  DotCircle,
 | 
				
			||||||
  ChevronRight,
 | 
					  ChevronRight,
 | 
				
			||||||
 | 
					  Bubble,
 | 
				
			||||||
  Cogs,
 | 
					  Cogs,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								frontend/src/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								frontend/src/types.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -59,8 +59,13 @@ type ChecklistBadge = {
 | 
				
			|||||||
  total: number;
 | 
					  total: number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CommentsBadge = {
 | 
				
			||||||
 | 
					  total: number;
 | 
				
			||||||
 | 
					  unread: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
type TaskBadges = {
 | 
					type TaskBadges = {
 | 
				
			||||||
  checklist?: ChecklistBadge | null;
 | 
					  checklist?: ChecklistBadge | null;
 | 
				
			||||||
 | 
					  comments?: CommentsBadge | null;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TaskActivityData = {
 | 
					type TaskActivityData = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,6 +74,7 @@ type Querier interface {
 | 
				
			|||||||
	GetAssignedTasksDueDateForUserID(ctx context.Context, arg GetAssignedTasksDueDateForUserIDParams) ([]Task, error)
 | 
						GetAssignedTasksDueDateForUserID(ctx context.Context, arg GetAssignedTasksDueDateForUserIDParams) ([]Task, error)
 | 
				
			||||||
	GetAssignedTasksProjectForUserID(ctx context.Context, arg GetAssignedTasksProjectForUserIDParams) ([]Task, error)
 | 
						GetAssignedTasksProjectForUserID(ctx context.Context, arg GetAssignedTasksProjectForUserIDParams) ([]Task, error)
 | 
				
			||||||
	GetAuthTokenByID(ctx context.Context, tokenID uuid.UUID) (AuthToken, error)
 | 
						GetAuthTokenByID(ctx context.Context, tokenID uuid.UUID) (AuthToken, error)
 | 
				
			||||||
 | 
						GetCommentCountForTask(ctx context.Context, taskID uuid.UUID) (int64, error)
 | 
				
			||||||
	GetCommentsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskComment, error)
 | 
						GetCommentsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskComment, error)
 | 
				
			||||||
	GetConfirmTokenByEmail(ctx context.Context, email string) (UserAccountConfirmToken, error)
 | 
						GetConfirmTokenByEmail(ctx context.Context, email string) (UserAccountConfirmToken, error)
 | 
				
			||||||
	GetConfirmTokenByID(ctx context.Context, confirmTokenID uuid.UUID) (UserAccountConfirmToken, error)
 | 
						GetConfirmTokenByID(ctx context.Context, confirmTokenID uuid.UUID) (UserAccountConfirmToken, error)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -95,3 +95,6 @@ SELECT task.* FROM task_assigned
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  ORDER BY task.due_date DESC, task_group.project_id DESC;
 | 
					  ORDER BY task.due_date DESC, task_group.project_id DESC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- name: GetCommentCountForTask :one
 | 
				
			||||||
 | 
					SELECT COUNT(*) FROM task_comment WHERE task_id = $1;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -316,6 +316,17 @@ func (q *Queries) GetAssignedTasksProjectForUserID(ctx context.Context, arg GetA
 | 
				
			|||||||
	return items, nil
 | 
						return items, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getCommentCountForTask = `-- name: GetCommentCountForTask :one
 | 
				
			||||||
 | 
					SELECT COUNT(*) FROM task_comment WHERE task_id = $1
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (q *Queries) GetCommentCountForTask(ctx context.Context, taskID uuid.UUID) (int64, error) {
 | 
				
			||||||
 | 
						row := q.db.QueryRowContext(ctx, getCommentCountForTask, taskID)
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
						err := row.Scan(&count)
 | 
				
			||||||
 | 
						return count, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getCommentsForTaskID = `-- name: GetCommentsForTaskID :many
 | 
					const getCommentsForTaskID = `-- name: GetCommentsForTaskID :many
 | 
				
			||||||
SELECT task_comment_id, task_id, created_at, updated_at, created_by, pinned, message FROM task_comment WHERE task_id = $1 ORDER BY created_at
 | 
					SELECT task_comment_id, task_id, created_at, updated_at, created_by, pinned, message FROM task_comment WHERE task_id = $1 ORDER BY created_at
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,6 +73,11 @@ type ComplexityRoot struct {
 | 
				
			|||||||
		Total    func(childComplexity int) int
 | 
							Total    func(childComplexity int) int
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CommentsBadge struct {
 | 
				
			||||||
 | 
							Total  func(childComplexity int) int
 | 
				
			||||||
 | 
							Unread func(childComplexity int) int
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CreateTaskCommentPayload struct {
 | 
						CreateTaskCommentPayload struct {
 | 
				
			||||||
		Comment func(childComplexity int) int
 | 
							Comment func(childComplexity int) int
 | 
				
			||||||
		TaskID  func(childComplexity int) int
 | 
							TaskID  func(childComplexity int) int
 | 
				
			||||||
@@ -419,6 +424,7 @@ type ComplexityRoot struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	TaskBadges struct {
 | 
						TaskBadges struct {
 | 
				
			||||||
		Checklist func(childComplexity int) int
 | 
							Checklist func(childComplexity int) int
 | 
				
			||||||
 | 
							Comments  func(childComplexity int) int
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TaskChecklist struct {
 | 
						TaskChecklist struct {
 | 
				
			||||||
@@ -768,6 +774,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return e.complexity.ChecklistBadge.Total(childComplexity), true
 | 
							return e.complexity.ChecklistBadge.Total(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case "CommentsBadge.total":
 | 
				
			||||||
 | 
							if e.complexity.CommentsBadge.Total == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return e.complexity.CommentsBadge.Total(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case "CommentsBadge.unread":
 | 
				
			||||||
 | 
							if e.complexity.CommentsBadge.Unread == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return e.complexity.CommentsBadge.Unread(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case "CreateTaskCommentPayload.comment":
 | 
						case "CreateTaskCommentPayload.comment":
 | 
				
			||||||
		if e.complexity.CreateTaskCommentPayload.Comment == nil {
 | 
							if e.complexity.CreateTaskCommentPayload.Comment == nil {
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
@@ -2553,6 +2573,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return e.complexity.TaskBadges.Checklist(childComplexity), true
 | 
							return e.complexity.TaskBadges.Checklist(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case "TaskBadges.comments":
 | 
				
			||||||
 | 
							if e.complexity.TaskBadges.Comments == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return e.complexity.TaskBadges.Comments(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case "TaskChecklist.id":
 | 
						case "TaskChecklist.id":
 | 
				
			||||||
		if e.complexity.TaskChecklist.ID == nil {
 | 
							if e.complexity.TaskChecklist.ID == nil {
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
@@ -3212,8 +3239,14 @@ type ChecklistBadge {
 | 
				
			|||||||
  total: Int!
 | 
					  total: Int!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CommentsBadge {
 | 
				
			||||||
 | 
					  total: Int!
 | 
				
			||||||
 | 
					  unread: Boolean!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TaskBadges {
 | 
					type TaskBadges {
 | 
				
			||||||
  checklist: ChecklistBadge
 | 
					  checklist: ChecklistBadge
 | 
				
			||||||
 | 
					  comments: CommentsBadge
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CausedBy {
 | 
					type CausedBy {
 | 
				
			||||||
@@ -5280,6 +5313,76 @@ func (ec *executionContext) _ChecklistBadge_total(ctx context.Context, field gra
 | 
				
			|||||||
	return ec.marshalNInt2int(ctx, field.Selections, res)
 | 
						return ec.marshalNInt2int(ctx, field.Selections, res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _CommentsBadge_total(ctx context.Context, field graphql.CollectedField, obj *CommentsBadge) (ret graphql.Marshaler) {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
 | 
								ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
								ret = graphql.Null
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						fc := &graphql.FieldContext{
 | 
				
			||||||
 | 
							Object:     "CommentsBadge",
 | 
				
			||||||
 | 
							Field:      field,
 | 
				
			||||||
 | 
							Args:       nil,
 | 
				
			||||||
 | 
							IsMethod:   false,
 | 
				
			||||||
 | 
							IsResolver: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx = graphql.WithFieldContext(ctx, fc)
 | 
				
			||||||
 | 
						resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 | 
				
			||||||
 | 
							ctx = rctx // use context from middleware stack in children
 | 
				
			||||||
 | 
							return obj.Total, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ec.Error(ctx, err)
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resTmp == nil {
 | 
				
			||||||
 | 
							if !graphql.HasFieldError(ctx, fc) {
 | 
				
			||||||
 | 
								ec.Errorf(ctx, "must not be null")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res := resTmp.(int)
 | 
				
			||||||
 | 
						fc.Result = res
 | 
				
			||||||
 | 
						return ec.marshalNInt2int(ctx, field.Selections, res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _CommentsBadge_unread(ctx context.Context, field graphql.CollectedField, obj *CommentsBadge) (ret graphql.Marshaler) {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
 | 
								ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
								ret = graphql.Null
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						fc := &graphql.FieldContext{
 | 
				
			||||||
 | 
							Object:     "CommentsBadge",
 | 
				
			||||||
 | 
							Field:      field,
 | 
				
			||||||
 | 
							Args:       nil,
 | 
				
			||||||
 | 
							IsMethod:   false,
 | 
				
			||||||
 | 
							IsResolver: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx = graphql.WithFieldContext(ctx, fc)
 | 
				
			||||||
 | 
						resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 | 
				
			||||||
 | 
							ctx = rctx // use context from middleware stack in children
 | 
				
			||||||
 | 
							return obj.Unread, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ec.Error(ctx, err)
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resTmp == nil {
 | 
				
			||||||
 | 
							if !graphql.HasFieldError(ctx, fc) {
 | 
				
			||||||
 | 
								ec.Errorf(ctx, "must not be null")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res := resTmp.(bool)
 | 
				
			||||||
 | 
						fc.Result = res
 | 
				
			||||||
 | 
						return ec.marshalNBoolean2bool(ctx, field.Selections, res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) _CreateTaskCommentPayload_taskID(ctx context.Context, field graphql.CollectedField, obj *CreateTaskCommentPayload) (ret graphql.Marshaler) {
 | 
					func (ec *executionContext) _CreateTaskCommentPayload_taskID(ctx context.Context, field graphql.CollectedField, obj *CreateTaskCommentPayload) (ret graphql.Marshaler) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if r := recover(); r != nil {
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
@@ -14831,6 +14934,38 @@ func (ec *executionContext) _TaskBadges_checklist(ctx context.Context, field gra
 | 
				
			|||||||
	return ec.marshalOChecklistBadge2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐChecklistBadge(ctx, field.Selections, res)
 | 
						return ec.marshalOChecklistBadge2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐChecklistBadge(ctx, field.Selections, res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _TaskBadges_comments(ctx context.Context, field graphql.CollectedField, obj *TaskBadges) (ret graphql.Marshaler) {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
 | 
								ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
								ret = graphql.Null
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						fc := &graphql.FieldContext{
 | 
				
			||||||
 | 
							Object:     "TaskBadges",
 | 
				
			||||||
 | 
							Field:      field,
 | 
				
			||||||
 | 
							Args:       nil,
 | 
				
			||||||
 | 
							IsMethod:   false,
 | 
				
			||||||
 | 
							IsResolver: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx = graphql.WithFieldContext(ctx, fc)
 | 
				
			||||||
 | 
						resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 | 
				
			||||||
 | 
							ctx = rctx // use context from middleware stack in children
 | 
				
			||||||
 | 
							return obj.Comments, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ec.Error(ctx, err)
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resTmp == nil {
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res := resTmp.(*CommentsBadge)
 | 
				
			||||||
 | 
						fc.Result = res
 | 
				
			||||||
 | 
						return ec.marshalOCommentsBadge2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCommentsBadge(ctx, field.Selections, res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) _TaskChecklist_id(ctx context.Context, field graphql.CollectedField, obj *db.TaskChecklist) (ret graphql.Marshaler) {
 | 
					func (ec *executionContext) _TaskChecklist_id(ctx context.Context, field graphql.CollectedField, obj *db.TaskChecklist) (ret graphql.Marshaler) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if r := recover(); r != nil {
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
@@ -20124,6 +20259,38 @@ func (ec *executionContext) _ChecklistBadge(ctx context.Context, sel ast.Selecti
 | 
				
			|||||||
	return out
 | 
						return out
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var commentsBadgeImplementors = []string{"CommentsBadge"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _CommentsBadge(ctx context.Context, sel ast.SelectionSet, obj *CommentsBadge) graphql.Marshaler {
 | 
				
			||||||
 | 
						fields := graphql.CollectFields(ec.OperationContext, sel, commentsBadgeImplementors)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out := graphql.NewFieldSet(fields)
 | 
				
			||||||
 | 
						var invalids uint32
 | 
				
			||||||
 | 
						for i, field := range fields {
 | 
				
			||||||
 | 
							switch field.Name {
 | 
				
			||||||
 | 
							case "__typename":
 | 
				
			||||||
 | 
								out.Values[i] = graphql.MarshalString("CommentsBadge")
 | 
				
			||||||
 | 
							case "total":
 | 
				
			||||||
 | 
								out.Values[i] = ec._CommentsBadge_total(ctx, field, obj)
 | 
				
			||||||
 | 
								if out.Values[i] == graphql.Null {
 | 
				
			||||||
 | 
									invalids++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case "unread":
 | 
				
			||||||
 | 
								out.Values[i] = ec._CommentsBadge_unread(ctx, field, obj)
 | 
				
			||||||
 | 
								if out.Values[i] == graphql.Null {
 | 
				
			||||||
 | 
									invalids++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								panic("unknown field " + strconv.Quote(field.Name))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out.Dispatch()
 | 
				
			||||||
 | 
						if invalids > 0 {
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var createTaskCommentPayloadImplementors = []string{"CreateTaskCommentPayload"}
 | 
					var createTaskCommentPayloadImplementors = []string{"CreateTaskCommentPayload"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) _CreateTaskCommentPayload(ctx context.Context, sel ast.SelectionSet, obj *CreateTaskCommentPayload) graphql.Marshaler {
 | 
					func (ec *executionContext) _CreateTaskCommentPayload(ctx context.Context, sel ast.SelectionSet, obj *CreateTaskCommentPayload) graphql.Marshaler {
 | 
				
			||||||
@@ -22580,6 +22747,8 @@ func (ec *executionContext) _TaskBadges(ctx context.Context, sel ast.SelectionSe
 | 
				
			|||||||
			out.Values[i] = graphql.MarshalString("TaskBadges")
 | 
								out.Values[i] = graphql.MarshalString("TaskBadges")
 | 
				
			||||||
		case "checklist":
 | 
							case "checklist":
 | 
				
			||||||
			out.Values[i] = ec._TaskBadges_checklist(ctx, field, obj)
 | 
								out.Values[i] = ec._TaskBadges_checklist(ctx, field, obj)
 | 
				
			||||||
 | 
							case "comments":
 | 
				
			||||||
 | 
								out.Values[i] = ec._TaskBadges_comments(ctx, field, obj)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			panic("unknown field " + strconv.Quote(field.Name))
 | 
								panic("unknown field " + strconv.Quote(field.Name))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -26306,6 +26475,13 @@ func (ec *executionContext) marshalOChecklistBadge2ᚖgithubᚗcomᚋjordanknott
 | 
				
			|||||||
	return ec._ChecklistBadge(ctx, sel, v)
 | 
						return ec._ChecklistBadge(ctx, sel, v)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) marshalOCommentsBadge2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCommentsBadge(ctx context.Context, sel ast.SelectionSet, v *CommentsBadge) graphql.Marshaler {
 | 
				
			||||||
 | 
						if v == nil {
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ec._CommentsBadge(ctx, sel, v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) unmarshalOCreateTaskComment2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateTaskComment(ctx context.Context, v interface{}) (*CreateTaskComment, error) {
 | 
					func (ec *executionContext) unmarshalOCreateTaskComment2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateTaskComment(ctx context.Context, v interface{}) (*CreateTaskComment, error) {
 | 
				
			||||||
	if v == nil {
 | 
						if v == nil {
 | 
				
			||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,11 @@ type ChecklistBadge struct {
 | 
				
			|||||||
	Total    int `json:"total"`
 | 
						Total    int `json:"total"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CommentsBadge struct {
 | 
				
			||||||
 | 
						Total  int  `json:"total"`
 | 
				
			||||||
 | 
						Unread bool `json:"unread"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CreateTaskChecklist struct {
 | 
					type CreateTaskChecklist struct {
 | 
				
			||||||
	TaskID   uuid.UUID `json:"taskID"`
 | 
						TaskID   uuid.UUID `json:"taskID"`
 | 
				
			||||||
	Name     string    `json:"name"`
 | 
						Name     string    `json:"name"`
 | 
				
			||||||
@@ -431,6 +436,7 @@ type TaskActivityData struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type TaskBadges struct {
 | 
					type TaskBadges struct {
 | 
				
			||||||
	Checklist *ChecklistBadge `json:"checklist"`
 | 
						Checklist *ChecklistBadge `json:"checklist"`
 | 
				
			||||||
 | 
						Comments  *CommentsBadge  `json:"comments"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TaskPositionUpdate struct {
 | 
					type TaskPositionUpdate struct {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,8 +138,14 @@ type ChecklistBadge {
 | 
				
			|||||||
  total: Int!
 | 
					  total: Int!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CommentsBadge {
 | 
				
			||||||
 | 
					  total: Int!
 | 
				
			||||||
 | 
					  unread: Boolean!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TaskBadges {
 | 
					type TaskBadges {
 | 
				
			||||||
  checklist: ChecklistBadge
 | 
					  checklist: ChecklistBadge
 | 
				
			||||||
 | 
					  comments: CommentsBadge
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CausedBy {
 | 
					type CausedBy {
 | 
				
			||||||
@@ -994,4 +1000,3 @@ type DeleteUserAccountPayload {
 | 
				
			|||||||
  ok: Boolean!
 | 
					  ok: Boolean!
 | 
				
			||||||
  userAccount: UserAccount!
 | 
					  userAccount: UserAccount!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1721,8 +1721,9 @@ func (r *taskResolver) Badges(ctx context.Context, obj *db.Task) (*TaskBadges, e
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return &TaskBadges{}, err
 | 
							return &TaskBadges{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(checklists) == 0 {
 | 
						comments, err := r.Repository.GetCommentCountForTask(ctx, obj.TaskID)
 | 
				
			||||||
		return &TaskBadges{Checklist: nil}, err
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return &TaskBadges{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	complete := 0
 | 
						complete := 0
 | 
				
			||||||
	total := 0
 | 
						total := 0
 | 
				
			||||||
@@ -1738,10 +1739,15 @@ func (r *taskResolver) Badges(ctx context.Context, obj *db.Task) (*TaskBadges, e
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if complete == 0 && total == 0 {
 | 
						var taskChecklist *ChecklistBadge
 | 
				
			||||||
		return &TaskBadges{Checklist: nil}, nil
 | 
						if total != 0 {
 | 
				
			||||||
 | 
							taskChecklist = &ChecklistBadge{Total: total, Complete: complete}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &TaskBadges{Checklist: &ChecklistBadge{Total: total, Complete: complete}}, nil
 | 
						var taskComments *CommentsBadge
 | 
				
			||||||
 | 
						if comments != 0 {
 | 
				
			||||||
 | 
							taskComments = &CommentsBadge{Total: int(comments), Unread: false}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &TaskBadges{Checklist: taskChecklist, Comments: taskComments}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *taskResolver) Activity(ctx context.Context, obj *db.Task) ([]db.TaskActivity, error) {
 | 
					func (r *taskResolver) Activity(ctx context.Context, obj *db.Task) ([]db.TaskActivity, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,8 +138,14 @@ type ChecklistBadge {
 | 
				
			|||||||
  total: Int!
 | 
					  total: Int!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CommentsBadge {
 | 
				
			||||||
 | 
					  total: Int!
 | 
				
			||||||
 | 
					  unread: Boolean!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TaskBadges {
 | 
					type TaskBadges {
 | 
				
			||||||
  checklist: ChecklistBadge
 | 
					  checklist: ChecklistBadge
 | 
				
			||||||
 | 
					  comments: CommentsBadge
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CausedBy {
 | 
					type CausedBy {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user