import type { WorkflowCommentList } from '@/service/workflow-comment' import { RiCheckboxCircleFill, RiCheckboxCircleLine, RiCheckLine, RiCloseLine, RiFilter3Line } from '@remixicon/react' import { useParams } from 'next/navigation' import { memo, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import Switch from '@/app/components/base/switch' import { UserAvatarList } from '@/app/components/base/user-avatar-list' import { collaborationManager } from '@/app/components/workflow/collaboration' 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 { useAppContext } from '@/context/app-context' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' import { resolveWorkflowComment } from '@/service/workflow-comment' import { cn } from '@/utils/classnames' 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, loadComments, handleCommentIconClick } = useWorkflowComment() const params = useParams() const appId = params.appId as string const { formatTimeFromNow } = useFormatTimeFromNow() const [showOnlyMine, setShowOnlyMine] = useState(false) const [showFilter, setShowFilter] = useState(false) const handleSelect = useCallback((comment: WorkflowCommentList) => { handleCommentIconClick(comment) }, [handleCommentIconClick]) const { userProfile } = useAppContext() const filteredSorted = useMemo(() => { let data = comments if (!showResolvedComments) data = data.filter(c => !c.resolved) if (showOnlyMine) data = data.filter(c => c.created_by === userProfile?.id) return data }, [comments, showOnlyMine, showResolvedComments, userProfile?.id]) const handleResolve = useCallback(async (comment: WorkflowCommentList) => { if (comment.resolved) return if (!appId) return try { await resolveWorkflowComment(appId, comment.id) collaborationManager.emitCommentsUpdate(appId) await loadComments() setActiveCommentId(comment.id) } catch (e) { console.error('Resolve comment failed', e) } }, [appId, loadComments, setActiveCommentId]) const hasActiveFilter = showOnlyMine || !showResolvedComments return (
{t('comments.panelTitle', { ns: 'workflow' })}
{showFilter && (
{ e.stopPropagation() }} > Show resolved { 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 * 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)