Item.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. 'use client';
  2. import '../style.scss';
  3. import Image from 'next/image';
  4. import { useState, useEffect } from 'react';
  5. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
  6. import { faThumbsUp as nThumbsUp, faThumbsDown as nThumbsDown, faFlag as nFlag, faPenToSquare, faTrashCan } from '@fortawesome/free-regular-svg-icons';
  7. import { faThumbsUp as yThumbsUp, faFlag as yFlag, faArrowRotateRight, faEllipsisVertical } from '@fortawesome/free-solid-svg-icons';
  8. import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
  9. import { throwError, loginCheck, formatDate } from '@/lib/utils/client';
  10. import { type CommentItem } from '@/types/forum/comment';
  11. import BoardResponse from '@/dtos/response/forum/board/boardResponse';
  12. import PostResponse from '@/dtos/response/forum/post/postResponse';
  13. import CommentDeleteRequest from '@/dtos/request/forum/comment/commentDeleteRequest';
  14. import EditForm from './EditForm';
  15. import { fetchCommentDelete } from '@/lib/api/forum/comment';
  16. type Props = {
  17. board: BoardResponse; // 게시판 정보
  18. post: PostResponse; // 게시글 정보
  19. comment: CommentItem; // 댓글 정보
  20. isReplying?: boolean; // 답글 버튼 클릭 여부
  21. onReply: () => void; // 답글 버튼 클릭 시
  22. onDelete: () => void; // 삭제 후
  23. onSuccess: () => void; // 수정/삭제 후
  24. }
  25. export default function Item({ comment, isReplying, board, post, onReply, onSuccess, onDelete } : Props)
  26. {
  27. const [error, setError] = useState<string|null>(null);
  28. const [loading, setLoading] = useState<boolean>(false);
  29. const [isEditing, setIsEditing] = useState<boolean>(false);
  30. useEffect(() => {
  31. if (!comment) {
  32. setIsEditing(false);
  33. }
  34. }, [comment]);
  35. const handleStartEdit = () => {
  36. if (!loginCheck()) {
  37. return;
  38. }
  39. setIsEditing(true);
  40. };
  41. const handleCancelEdit = () => {
  42. setIsEditing(false);
  43. };
  44. const handleEditSuccess = () => {
  45. setIsEditing(false);
  46. if (typeof onSuccess === 'function') {
  47. onSuccess();
  48. }
  49. };
  50. const handleDelete = () => {
  51. setIsEditing(false);
  52. if (confirm("댓글을 삭제하시겠습니까?")) {
  53. setLoading(true);
  54. // 댓글 삭제 호출
  55. fetchCommentDelete({ commentID: comment.id } as CommentDeleteRequest).then((res) => {
  56. throwError(res);
  57. // 삭제 성공 시 해당 댓글 영역 삭제
  58. onDelete();
  59. }).catch(err => {
  60. setError(err.message);
  61. }).finally(() => {
  62. setLoading(false);
  63. });
  64. }
  65. };
  66. const writerThumb = (comment.writer.thumbnail ?? '/resources/thumb.gif');
  67. const writerName = (comment.writer.name || comment.writer.sid);
  68. const createdAt = formatDate(comment.createdAt);
  69. return (
  70. <li className={comment.isSecret ? 'is-secret' : ''}>
  71. <div>
  72. <Image src={writerThumb} alt={writerName} width={72} height={0} />
  73. </div>
  74. <div>
  75. <ul>
  76. <li>{writerName}</li>
  77. <li>{createdAt}</li>
  78. </ul>
  79. </div>
  80. <div>
  81. <DropdownMenu>
  82. <DropdownMenuTrigger>
  83. <FontAwesomeIcon icon={faEllipsisVertical}/>
  84. </DropdownMenuTrigger>
  85. <DropdownMenuContent>
  86. {/*
  87. <DropdownMenuItem><FontAwesomeIcon icon={nFlag} className="mr-2"/> 신고</DropdownMenuItem>
  88. */}
  89. <DropdownMenuItem onClick={handleStartEdit}>
  90. <FontAwesomeIcon icon={faPenToSquare} className="mr-2"/> 수정
  91. </DropdownMenuItem>
  92. <DropdownMenuItem onClick={handleDelete}>
  93. <FontAwesomeIcon icon={faTrashCan} className="mr-2"/> 삭제
  94. </DropdownMenuItem>
  95. </DropdownMenuContent>
  96. </DropdownMenu>
  97. </div>
  98. <div>
  99. <ul>
  100. <li>
  101. {isEditing ? (
  102. <EditForm
  103. board={board}
  104. post={post}
  105. comment={comment}
  106. onSuccess={handleEditSuccess}
  107. onCancel={handleCancelEdit}
  108. />
  109. ) : (
  110. <ul>
  111. <li>
  112. {comment.mention && (
  113. <span className="mention">@{comment.mention.rawHandle} </span>
  114. )}
  115. {comment.content}
  116. </li>
  117. </ul>
  118. )}
  119. </li>
  120. </ul>
  121. </div>
  122. {!isEditing && (
  123. <div>
  124. {/*
  125. <button type="button" title="좋아요"><FontAwesomeIcon icon={nThumbsUp} /></button>
  126. <button type="button" title="싫어요"><FontAwesomeIcon icon={nThumbsDown} /></button>
  127. */}
  128. <button
  129. type="button"
  130. title="답글"
  131. onClick={onReply}
  132. >
  133. {isReplying ? '답글 접기' : '답글'}
  134. </button>
  135. </div>
  136. )}
  137. </li>
  138. );
  139. }