view.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. 'use client';
  2. import './style.scss';
  3. import { useState, useEffect } from 'react';
  4. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
  5. import { faArrowRotateRight } from '@fortawesome/free-solid-svg-icons';
  6. import BoardResponse from '@/dtos/response/forum/board/boardResponse';
  7. import PostResponse from '@/dtos/response/forum/post/postResponse';
  8. import CommentListRequest from '@/dtos/request/forum/comment/commentListRequest';
  9. import CommentListResponse from '@/dtos/response/forum/comment/commentListResponse';
  10. import { fetchCommentList } from '@/lib/api/forum/comment';
  11. import { throwError, loginCheck } from '@/lib/utils/client';
  12. import WriteForm from './_component/WriteForm';
  13. import List from './_component/List';
  14. import { type CommentSort, CommentConst } from '@/constants/forum';
  15. import Pagination from '@/app/component/Pagination';
  16. type Props = {
  17. board: BoardResponse;
  18. post: PostResponse;
  19. }
  20. export default function View({ board, post } : Props)
  21. {
  22. const [error, setError] = useState<string|null>(null);
  23. const [loading, setLoading] = useState<boolean>(false);
  24. const [page, setPage] = useState<number>(1);
  25. const [sort, setSort] = useState<CommentSort>(CommentConst.Sort.CreatedAt);
  26. const [data, setData] = useState<CommentListResponse>({ total: 0, list: [] });
  27. const [replyTargetID, setReplyTargetID] = useState<number|null>(null);
  28. useEffect(() => {
  29. if (error) {
  30. alert(error);
  31. setError(null);
  32. }
  33. }, [error]);
  34. useEffect(() => {
  35. loadComments();
  36. }, [page, sort]);
  37. const loadComments = async () => {
  38. setLoading(true);
  39. // 댓글 목록 호출
  40. fetchCommentList({
  41. postID: post.id,
  42. page: page,
  43. sort: sort,
  44. perPage: board.boardMeta.comment?.perPage ?? 20
  45. } as CommentListRequest).then((res) => {
  46. throwError(res);
  47. if (res.data != null) {
  48. setData(res.data);
  49. }
  50. }).catch(err => {
  51. setError(err.message);
  52. }).finally(() => {
  53. setLoading(false);
  54. });
  55. };
  56. const handleReply = (commentID: number) => {
  57. if (!loginCheck()) {
  58. return;
  59. }
  60. setReplyTargetID((prev) => (prev === commentID ? null : commentID)); // toggle
  61. };
  62. const handleSuccess = () => {
  63. setReplyTargetID(null);
  64. loadComments();
  65. };
  66. const handleDelete = () => {
  67. setReplyTargetID(null);
  68. loadComments();
  69. };
  70. return (
  71. <>
  72. {/* 댓글, 답글 */}
  73. <section id="comments">
  74. <div className='comment-header'>
  75. <article>댓글 <em>{data.total}개</em></article>
  76. <article>
  77. <select name="sort" title="정렬 기준" onChange={e => setSort(Number(e.target.value) as CommentSort)} value={sort}>
  78. <option value="0">최신순</option>
  79. <option value="1">인기순</option>
  80. </select>
  81. </article>
  82. <article>
  83. <button className="btn btn-default" title="새로고침" onClick={loadComments} disabled={loading}>
  84. <FontAwesomeIcon icon={faArrowRotateRight}/>
  85. </button>
  86. </article>
  87. </div>
  88. {/* 댓글 작성란 */}
  89. <WriteForm board={board} post={post} onSuccess={handleSuccess} />
  90. <hr />
  91. {/* 댓글 목록 */}
  92. <List board={board} post={post} data={data} loading={loading} replyTargetID={replyTargetID} onReply={handleReply} onSuccess={handleSuccess} onDelete={handleDelete} />
  93. {/* 페이징 */}
  94. <Pagination total={data.total} page={page} onChange={setPage} />
  95. </section>
  96. </>
  97. );
  98. }