'use client'; import './style.scss'; import { useState, useEffect, useCallback } from 'react'; import useErrorAlert from '@/hooks/useErrorAlert'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faArrowRotateRight } from '@fortawesome/free-solid-svg-icons'; import { BoardResponse } from '@/types/response/forum/board'; import { PostResponse } from '@/types/response/forum/post'; import { CommentListResponse } from '@/types/response/forum/comment'; import { type CommentItem } from '@/types/forum/comment'; import { fetchApi } from '@/lib/utils/client'; import { checkPermission } from '@/lib/utils/permission'; import useAuth from '@/hooks/useAuth'; import WriteForm from './_component/WriteForm'; import List from './_component/List'; import { type CommentSort, CommentConst, BoardLayout } from '@/constants/forum'; import Pagination from '@/app/component/Pagination'; type Props = { _board: BoardResponse; _post: PostResponse; } export default function View({ _board, _post } : Props) { const { member, loginCheck } = useAuth(); const { setError } = useErrorAlert(); // 댓글 권한 체크 const { canViewComment, canWriteComment, canWriteReply } = checkPermission(_board.boardMeta, _board.boardManager, member); const [loading, setLoading] = useState(false); const [page, setPage] = useState(1); const [sort, setSort] = useState(CommentConst.Sort.CreatedAt); const [data, setData] = useState({ total: 0, totalRoots: 0, list: [] }); const [replyTargetID, setReplyTargetID] = useState(null); const loadComments = useCallback(() => { setLoading(true); const queryParams = new URLSearchParams(); queryParams.set('page', String(page)); queryParams.set('perPage', String(_board.boardMeta.comment?.perPage ?? 20)); if (sort !== undefined) { queryParams.set('sort', String(sort)); } // 댓글 목록 호출 fetchApi(`/api/forum/posts/${_post.id}/comments?${queryParams.toString()}`).then((res) => { if (res.data != null) { setData(res.data); } }).catch(err => { setError(err.message); }).finally(() => { setLoading(false); }); }, [page, sort, _board.boardMeta.comment?.perPage, _post.id, setError]); useEffect(() => { loadComments(); }, [loadComments]); const handleReply = useCallback((commentID: number) => { if (!loginCheck()) { return; } setReplyTargetID((prev) => (prev === commentID ? null : commentID)); // toggle }, [loginCheck]); const handleSuccess = useCallback((comment?: CommentItem) => { setReplyTargetID(null); if (!comment) { loadComments(); return; } setData(prev => { // 수정: 기존 데이터에 ID가 있으면 교체 const isTopEdit = prev.list.some(c => c.id === comment.id); const isChildEdit = prev.list.some(c => c.children.some(ch => ch.id === comment.id)); if (isTopEdit) { return { ...prev, list: prev.list.map(c => c.id === comment.id ? { ...comment, children: c.children } : c )}; } if (isChildEdit) { return { ...prev, list: prev.list.map(c => ({ ...c, children: c.children.map(ch => ch.id === comment.id ? comment : ch) }))}; } // 새 답글 if (comment.parentID) { return { ...prev, list: prev.list.map(c => c.id === comment.parentID ? { ...c, children: [...c.children, comment], replies: c.replies + 1 } : c )}; } // 새 최상위 댓글 (맨 앞 추가) return { total: prev.total + 1, totalRoots: prev.totalRoots + 1, list: [comment, ...prev.list] }; }); }, [loadComments]); const handleDelete = useCallback((commentID: number, parentID?: number) => { setReplyTargetID(null); setData(prev => { if (parentID) { return { ...prev, list: prev.list.map(c => c.id === parentID ? { ...c, children: c.children.filter(ch => ch.id !== commentID), replies: Math.max(0, c.replies - 1) } : c )}; } return { total: Math.max(0, prev.total - 1), totalRoots: Math.max(0, prev.totalRoots - 1), list: prev.list.filter(c => c.id !== commentID) }; }); }, []); const handleSortChange = useCallback((e: React.ChangeEvent) => { setSort(Number(e.target.value) as CommentSort); }, []); if (!_board.boardMeta.comment.enableComment) { return null; } return ( <> {/* 댓글, 답글 */}
댓글 {data.total}개
{data.total > 0 && ( <>
)}
{canViewComment ? ( <> {/* 댓글 작성란 */} {canWriteComment && ( )} {/* QnA 답변 대기 안내 */} {_board.boardMeta.list.layout === BoardLayout.QnA && !_post.isReply && data.total === 0 ? (

문의 내용 확인 중입니다.

담당자 확인 후 답변 드리겠습니다.

) : ( <> {/* 댓글 목록 */} {/* 페이지네이션 */} )} ) : (

댓글을 볼 수 있는 권한이 없습니다.

)}
); }