Files
gitmost/apps/client/src/features/comment/components/resolve-comment.tsx
T
vvzvlad c758a36dd2 feat(comments): implement comment resolution for the community build
Add comment resolve/re-open as a community feature, written from scratch on top
of the infrastructure already present in the community codebase: the
resolved_at/resolved_by_id columns, the COMMENT_RESOLVED notification job, the
resolveCommentMark collaboration handler, the commentResolved websocket event,
the comment service/types and the Open/Resolved tabs. No Enterprise-Edition code
is reused and there is no EE feature gating — resolving is available to anyone
who can comment.

Backend:
- add POST /comments/resolve (ResolveCommentDto) guarded by validateCanComment;
  reject resolving replies
- add CommentService.resolveComment: set/clear resolvedAt/resolvedById, sync the
  inline comment mark via collaboration handleYjsEvent, queue
  COMMENT_RESOLVED_NOTIFICATION (only when another user resolves), emit the
  commentResolved websocket event and write a resolve/reopen audit log

Frontend:
- add useResolveCommentMutation with optimistic update + rollback
- add ResolveComment toggle button
- wire the resolve button and menu item into comment-list-item / comment-menu,
  gated on canComment for parent comments

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 23:38:15 +03:00

65 lines
1.6 KiB
TypeScript

import { ActionIcon, Tooltip } from "@mantine/core";
import { IconCircleCheck, IconCircleCheckFilled } from "@tabler/icons-react";
import { useResolveCommentMutation } from "@/features/comment/queries/comment-query";
import { useTranslation } from "react-i18next";
import { Editor } from "@tiptap/react";
interface ResolveCommentProps {
editor: Editor | null;
commentId: string;
pageId: string;
resolvedAt?: Date;
}
function ResolveComment({
editor,
commentId,
pageId,
resolvedAt,
}: ResolveCommentProps) {
const { t } = useTranslation();
const resolveCommentMutation = useResolveCommentMutation();
const isResolved = resolvedAt != null;
const handleResolveToggle = async () => {
try {
await resolveCommentMutation.mutateAsync({
commentId,
pageId,
resolved: !isResolved,
});
if (editor) {
editor.commands.setCommentResolved(commentId, !isResolved);
}
} catch (error) {
console.error("Failed to toggle resolved state:", error);
}
};
return (
<Tooltip
label={isResolved ? t("Re-open comment") : t("Resolve comment")}
position="top"
>
<ActionIcon
onClick={handleResolveToggle}
variant="subtle"
color={isResolved ? "green" : "gray"}
size="sm"
loading={resolveCommentMutation.isPending}
disabled={resolveCommentMutation.isPending}
>
{isResolved ? (
<IconCircleCheckFilled size={18} />
) : (
<IconCircleCheck size={18} />
)}
</ActionIcon>
</Tooltip>
);
}
export default ResolveComment;