view.tsx 3.3 KB

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