import type { WorkflowCommentList } from '@/app/components/workflow/comment/types' import { cn } from '@langgenius/dify-ui/cn' import { Switch } from '@langgenius/dify-ui/switch' import { RiCheckboxCircleFill, RiCheckboxCircleLine, RiCheckLine, RiCloseLine, RiFilter3Line, } from '@remixicon/react' import { useAtomValue } from 'jotai' import { memo, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import { UserAvatarList } from '@/app/components/base/user-avatar-list' import { useWorkflowComment } from '@/app/components/workflow/hooks/use-workflow-comment' import { useStore } from '@/app/components/workflow/store' import { ControlMode } from '@/app/components/workflow/types' import { userProfileIdAtom } from '@/context/account-state' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' const CommentsPanel = () => { const { t } = useTranslation() const activeCommentId = useStore((s) => s.activeCommentId) const setActiveCommentId = useStore((s) => s.setActiveCommentId) const setControlMode = useStore((s) => s.setControlMode) const showResolvedComments = useStore((s) => s.showResolvedComments) const setShowResolvedComments = useStore((s) => s.setShowResolvedComments) const { comments, loading, handleCommentIconClick, handleCommentResolve } = useWorkflowComment() const { formatTimeFromNow } = useFormatTimeFromNow() const [showOnlyMine, setShowOnlyMine] = useState(false) const [showFilter, setShowFilter] = useState(false) const handleSelect = useCallback( (comment: WorkflowCommentList) => { handleCommentIconClick(comment) }, [handleCommentIconClick], ) const currentUserId = useAtomValue(userProfileIdAtom) const filteredSorted = useMemo(() => { let data = comments if (!showResolvedComments) data = data.filter((c) => !c.resolved) if (showOnlyMine) data = data.filter((c) => c.created_by === currentUserId) return data }, [comments, currentUserId, showOnlyMine, showResolvedComments]) const handleResolve = useCallback( async (comment: WorkflowCommentList) => { if (comment.resolved) return try { await handleCommentResolve(comment.id) setActiveCommentId(comment.id) } catch (e) { console.error('Resolve comment failed', e) } }, [handleCommentResolve, setActiveCommentId], ) const hasActiveFilter = showOnlyMine || !showResolvedComments return (
{t(($) => $['comments.panelTitle'], { ns: 'workflow' })}
{showFilter && (
{ e.stopPropagation() }} > {t(($) => $['comments.filter.showResolved'], { ns: 'workflow' })} { setShowResolvedComments(checked) }} />
)}
{ setControlMode(ControlMode.Pointer) setActiveCommentId(null) }} >
{filteredSorted.map((c) => { const isActive = activeCommentId === c.id return (
handleSelect(c)} >
{c.resolved ? ( ) : ( { e.preventDefault() e.stopPropagation() handleResolve(c) }} /> )}
{/* Header row: creator + time */}
{c.created_by_account?.name ?? ''}
{formatTimeFromNow((c.updated_at ?? c.created_at ?? 0) * 1000)}
{/* Content */}
{c.content}
{/* Footer */} {c.reply_count > 0 && (
{c.reply_count} {t(($) => $['comments.reply'], { ns: 'workflow' })}
)}
) })} {!loading && filteredSorted.length === 0 && (
{t(($) => $['comments.noComments'], { ns: 'workflow' })}
)}
) } export default memo(CommentsPanel)