diff --git a/package-lock.json b/package-lock.json index ca8816bbd..53b9fb084 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9260,8 +9260,9 @@ }, "node_modules/eslint-config-next": { "version": "13.2.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.2.4.tgz", + "integrity": "sha512-lunIBhsoeqw6/Lfkd6zPt25w1bn0znLA/JCL+au1HoEpSb4/PpsOYsYtgV/q+YPsoKIOzFyU5xnb04iZnXjUvg==", "dev": true, - "license": "MIT", "dependencies": { "@next/eslint-plugin-next": "13.2.4", "@rushstack/eslint-patch": "^1.1.3", @@ -24814,6 +24815,8 @@ }, "eslint-config-next": { "version": "13.2.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.2.4.tgz", + "integrity": "sha512-lunIBhsoeqw6/Lfkd6zPt25w1bn0znLA/JCL+au1HoEpSb4/PpsOYsYtgV/q+YPsoKIOzFyU5xnb04iZnXjUvg==", "dev": true, "requires": { "@next/eslint-plugin-next": "13.2.4", diff --git a/src/components/CommentView/CommentView.tsx b/src/components/CommentView/CommentView.tsx index 90de138c9..e9f4e6feb 100644 --- a/src/components/CommentView/CommentView.tsx +++ b/src/components/CommentView/CommentView.tsx @@ -1,7 +1,7 @@ -import React, { FC, useCallback, useState } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; import styled from 'styled-components'; import dynamic from 'next/dynamic'; -import { brandColor, danger0, gapM, gapS, gray4, gray9 } from '@taskany/colors'; +import { brandColor, danger0, gapM, gapS, gray4, gray9, backgroundColor } from '@taskany/colors'; import { BinIcon, Card, @@ -16,6 +16,7 @@ import { Text, UserPic, nullable, + PinAltIcon, } from '@taskany/bricks'; import { Reaction, State, User } from '@prisma/client'; import colorLayer from 'color-layer'; @@ -23,11 +24,12 @@ import colorLayer from 'color-layer'; import { useReactionsResource } from '../../hooks/useReactionsResource'; import { useCommentResource } from '../../hooks/useCommentResource'; import { usePageContext } from '../../hooks/usePageContext'; +import { useLocale } from '../../hooks/useLocale'; import { createLocaleDate } from '../../utils/dateTime'; import { Reactions } from '../Reactions'; import { ActivityFeedItem } from '../ActivityFeed'; -import { useLocale } from '../../hooks/useLocale'; import { RelativeTime } from '../RelativeTime/RelativeTime'; +import { Circle, CircledIcon } from '../Circle'; import { tr } from './CommentView.i18n'; @@ -45,6 +47,7 @@ interface CommentViewProps { isNew?: boolean; isEditable?: boolean; state?: State | null; + isPinned?: boolean; onReactionToggle?: React.ComponentProps['onClick']; onDelete?: (id: string) => void; @@ -104,25 +107,48 @@ const StyledCommentCard = styled(Card)<{ isNew?: boolean }>` `; const StyledCardInfo = styled(CardInfo)` - display: grid; - grid-template-columns: 6fr 6fr; -`; - -const StyledReactions = styled.div` - padding-top: ${gapM}; + display: flex; + justify-content: space-between; `; -const StyledStateDot = styled(StateDot)` - margin-right: ${gapS}; +const StyledCardComment = styled(CardComment)` + display: flex; + flex-direction: column; + gap: ${gapM}; `; const StyledTimestamp = styled.div` display: flex; align-items: center; - - padding-bottom: ${gapM}; + gap: ${gapS}; `; +const renderTriggerHelper = ({ ref, onClick }: { ref: React.RefObject; onClick: () => void }) => ( + +); + +const renderItemHelper = ({ item, cursor, index }: { item: any; cursor: number; index: number }) => ( + + {item.label} + +); + +const iconRenderCondition = (isPinned: boolean, image?: User['image'], email?: User['email']) => + isPinned ? ( + + + + ) : ( + + ); + export const CommentView: FC = ({ id, author, @@ -134,6 +160,7 @@ export const CommentView: FC = ({ state, onDelete, onReactionToggle, + isPinned = false, }) => { const { themeId } = usePageContext(); const locale = useLocale(); @@ -181,9 +208,26 @@ export const CommentView: FC = ({ })({ id }); }, [id, onDelete, remove]); + const dropdownItems = useMemo( + () => [ + { + label: tr('Edit'), + icon: , + onClick: onEditClick, + }, + { + label: tr('Delete'), + color: danger0, + icon: , + onClick: onDeleteClick, + }, + ], + [onDeleteClick, onEditClick], + ); + return ( - - + + {iconRenderCondition(isPinned, author?.image, author?.email)} {editMode ? ( = ({ ))} {nullable(isEditable, () => ( - - , - onClick: onEditClick, - }, - { - label: tr('Delete'), - color: danger0, - icon: , - onClick: onDeleteClick, - }, - ]} - renderTrigger={({ ref, onClick }) => ( - - )} - renderItem={({ item, cursor, index }) => ( - - {item.label} - - )} - /> - + ))} - + {nullable(state, (s) => ( - + {createLocaleDate(createdAt, { locale })} @@ -256,11 +273,9 @@ export const CommentView: FC = ({ {commentDescription} {nullable(reactions?.length, () => ( - - - + ))} - + )} diff --git a/src/components/GoalPreview/GoalPreview.tsx b/src/components/GoalPreview/GoalPreview.tsx index 00f7498a5..94bd5dace 100644 --- a/src/components/GoalPreview/GoalPreview.tsx +++ b/src/components/GoalPreview/GoalPreview.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import dynamic from 'next/dynamic'; import styled from 'styled-components'; import { danger0, gapM, gapS, gray7 } from '@taskany/colors'; @@ -43,6 +43,7 @@ import { State } from '../State'; import { useGoalDependencyResource } from '../../hooks/useGoalDependencyResource'; import { GoalDependencyAddForm } from '../GoalDependencyForm/GoalDependencyForm'; import { GoalDependencyListByKind } from '../GoalDependencyList/GoalDependencyList'; +import { CommentView } from '../CommentView/CommentView'; import { tr } from './GoalPreview.i18n'; @@ -95,6 +96,8 @@ const StyledModalContent = styled(ModalContent)` const StyledCard = styled(Card)` min-height: 60px; + + margin-bottom: ${gapS}; `; const GoalPreview: React.FC = ({ preview, onClose, onDelete }) => { @@ -188,6 +191,7 @@ const GoalPreview: React.FC = ({ preview, onClose, onDelete }) const commentsRef = useRef(null); const contentRef = useRef(null); const headerRef = useRef(null); + const onCommentsClick = useCallback(() => { commentsRef.current && contentRef.current && @@ -200,6 +204,16 @@ const GoalPreview: React.FC = ({ preview, onClose, onDelete }) const { title, description, updatedAt } = goal || preview; + const lastChangedStatusComment = useMemo(() => { + if (!goal || goal.comments?.length <= 1) { + return null; + } + + const foundResult = goal.comments.findLast((comment) => comment.stateId); + return foundResult?.stateId === goal.stateId ? foundResult : null; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [goal?.comments, goal?.stateId]); + return ( <> @@ -305,6 +319,21 @@ const GoalPreview: React.FC = ({ preview, onClose, onDelete }) + {nullable(lastChangedStatusComment, (value) => ( + + ))} + {nullable(goal, ({ activityFeed, id, goalAchiveCriteria, relations, project, _isEditable }) => (