diff --git a/web/app/components/workflow/comment/comment-icon.tsx b/web/app/components/workflow/comment/comment-icon.tsx index 33237c1766..27db952f26 100644 --- a/web/app/components/workflow/comment/comment-icon.tsx +++ b/web/app/components/workflow/comment/comment-icon.tsx @@ -1,19 +1,27 @@ 'use client' import type { FC } from 'react' -import { memo, useMemo } from 'react' +import { memo, useMemo, useState } from 'react' import { useReactFlow, useViewport } from 'reactflow' import { UserAvatarList } from '@/app/components/base/user-avatar-list' +import CommentPreview from './comment-preview' import type { WorkflowCommentList } from '@/service/workflow-comment' type CommentIconProps = { comment: WorkflowCommentList onClick: () => void + isActive?: boolean } -export const CommentIcon: FC = memo(({ comment, onClick }) => { +export const CommentIcon: FC = memo(({ comment, onClick, isActive = false }) => { const { flowToScreenPosition } = useReactFlow() const viewport = useViewport() + const [showPreview, setShowPreview] = useState(false) + + const handlePreviewClick = () => { + setShowPreview(false) + onClick() + } const screenPosition = useMemo(() => { return flowToScreenPosition({ @@ -35,30 +43,58 @@ export const CommentIcon: FC = memo(({ comment, onClick }) => ) return ( -
+ <>
-
-
- +
setShowPreview(true)} + onMouseLeave={isActive ? undefined : () => setShowPreview(false)} + > +
+
+
+ +
+
-
+ + {/* Preview panel */} + {showPreview && !isActive && ( +
setShowPreview(true)} + onMouseLeave={() => setShowPreview(false)} + > + +
+ )} + ) }, (prevProps, nextProps) => { return ( @@ -66,6 +102,7 @@ export const CommentIcon: FC = memo(({ comment, onClick }) => && prevProps.comment.position_x === nextProps.comment.position_x && prevProps.comment.position_y === nextProps.comment.position_y && prevProps.onClick === nextProps.onClick + && prevProps.isActive === nextProps.isActive ) }) diff --git a/web/app/components/workflow/comment/comment-preview.tsx b/web/app/components/workflow/comment/comment-preview.tsx new file mode 100644 index 0000000000..5ed78aaab1 --- /dev/null +++ b/web/app/components/workflow/comment/comment-preview.tsx @@ -0,0 +1,44 @@ +'use client' + +import type { FC } from 'react' +import { memo } from 'react' +import { UserAvatarList } from '@/app/components/base/user-avatar-list' +import type { WorkflowCommentList } from '@/service/workflow-comment' +import { useFormatTimeFromNow } from '@/app/components/workflow/hooks' + +type CommentPreviewProps = { + comment: WorkflowCommentList + onClick?: () => void +} + +const CommentPreview: FC = ({ comment, onClick }) => { + const { formatTimeFromNow } = useFormatTimeFromNow() + + return ( +
+
+ +
+ +
+
+
{comment.created_by_account.name}
+
+ {formatTimeFromNow(comment.updated_at * 1000)} +
+
+
+ +
{comment.content}
+
+ ) +} + +export default memo(CommentPreview) diff --git a/web/app/components/workflow/comment/thread.tsx b/web/app/components/workflow/comment/thread.tsx index 727bb60712..2295eecac9 100644 --- a/web/app/components/workflow/comment/thread.tsx +++ b/web/app/components/workflow/comment/thread.tsx @@ -216,9 +216,9 @@ export const CommentThread: FC = memo(({
diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index 932a7a83e5..3276ecf330 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -454,21 +454,29 @@ export const Workflow: FC = memo(({ const canGoPrev = index > 0 const canGoNext = index < comments.length - 1 return ( - handleCommentResolve(comment.id)} - onDelete={() => setPendingDeleteCommentId(comment.id)} - onPrev={canGoPrev ? () => handleCommentNavigate('prev') : undefined} - onNext={canGoNext ? () => handleCommentNavigate('next') : undefined} - onReply={(content, ids) => handleCommentReply(comment.id, content, ids ?? [])} - onReplyEdit={(replyId, content, ids) => handleCommentReplyUpdate(comment.id, replyId, content, ids ?? [])} - onReplyDelete={replyId => setPendingDeleteReply({ commentId: comment.id, replyId })} - canGoPrev={canGoPrev} - canGoNext={canGoNext} - /> + <> + handleCommentIconClick(comment)} + isActive={true} + /> + handleCommentResolve(comment.id)} + onDelete={() => setPendingDeleteCommentId(comment.id)} + onPrev={canGoPrev ? () => handleCommentNavigate('prev') : undefined} + onNext={canGoNext ? () => handleCommentNavigate('next') : undefined} + onReply={(content, ids) => handleCommentReply(comment.id, content, ids ?? [])} + onReplyEdit={(replyId, content, ids) => handleCommentReplyUpdate(comment.id, replyId, content, ids ?? [])} + onReplyDelete={replyId => setPendingDeleteReply({ commentId: comment.id, replyId })} + canGoPrev={canGoPrev} + canGoNext={canGoNext} + /> + ) }