+
+ {card.title}
+
+
+ {/* 카드 우측 상단의 케밥 버튼, X 버튼 */}
+
+ isDark ? (
+
+ ) : (
+
+ )
}
>
- {/* 드롭다운 내부 메뉴 아이템 */}
-
setOpenModifyCard(true)}
+ {/* 트리거의(케밥) 드롭다운 - 수정하기, 삭제하기 */}
+
-
+
+
+
-
{
+ {/* X 버튼 (카드 닫기)*/}
+
+ {/* 카드 상단 */}
- 그리드로 관리하기 / 컬럼명은 따로 컴포넌트로 만들어 빼기
-
{column.title}
-
-
설명~~~~~~~~
-
-
{assignee.nickname}
-
{dueDate}
+
+ {/* 컬럼명 / 태그 // 설명 // 이미지 */}
+
+
+ {card.description && (
+
+ {card.description}
+
+ )}
+ {card.imageUrl && (
+
+ )}
+
+ {/* 담당자 / 마감일 박스 */}
+
+
+
+
+ 담당자
+
+ {card.assignee && (
+
+ <>
+
+
+ {card.assignee.nickname}
+
+ >
+
+ )}
+
+
+
+ 마감일
+
+ {card.dueDate && (
+
+ {card.dueDate}
+
+ )}
+
+
+
+
-
이미지 있으면 이미지
-
댓글 컴포넌트
- {/* 카드 수정 모달 */}
- {openModifyCard && (
-
- setOpenModifyCard(false)}
- // columnId={column.id}
- currentColumn={currentColumn}
- card={card}
- />
-
- )}
- >
+
+
+
)
}
diff --git a/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx b/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx
index 9f8b32b..c494164 100644
--- a/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx
+++ b/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx
@@ -1,17 +1,20 @@
import { createPortal } from 'react-dom'
+import { useLockBodyScroll } from '@/app/shared/hooks/useLockBodyScroll'
+
interface ModalProps {
children: React.ReactNode
}
export default function CardModal({ children }: ModalProps) {
const modalRoot = document.getElementById('modal-root')
+
+ useLockBodyScroll()
+
if (!modalRoot) return null
return createPortal(
-
- {children}
-
+
{children}
,
modalRoot,
)
diff --git a/src/app/features/dashboard_Id/Card/cardModal/Comment.tsx b/src/app/features/dashboard_Id/Card/cardModal/Comment.tsx
new file mode 100644
index 0000000..a681885
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/Comment.tsx
@@ -0,0 +1,58 @@
+import { format } from 'date-fns'
+import { useState } from 'react'
+
+import { Avatar } from '@/app/shared/components/common/Avatar'
+import { useIsMobile } from '@/app/shared/hooks/useIsmobile'
+
+import { useDeleteCommentMutation } from '../../api/useDeleteCommentMutation'
+import { Comment as CommentType } from '../../type/Comment.type'
+import CommentModifyForm from './CommentModifyForm'
+
+export default function Comment({ comment }: { comment: CommentType }) {
+ const isMobile = useIsMobile()
+ const [modifyComment, setModifyComment] = useState(false)
+ const { mutate: deleteComment, isPending } = useDeleteCommentMutation()
+
+ return (
+
+ {isMobile ? (
+
+ ) : (
+
+ )}
+
+
+
+ {comment.author.nickname}
+
+
+ {format(new Date(comment.createdAt), 'yyyy.MM.dd HH:mm')}
+
+
+ {modifyComment ? (
+
setModifyComment(false)}
+ commentId={comment.id}
+ content={comment.content}
+ />
+ ) : (
+ {comment.content}
+ )}
+ {!modifyComment && (
+
+
+
+
+ )}
+
+
+ )
+}
diff --git a/src/app/features/dashboard_Id/Card/cardModal/CommentForm.tsx b/src/app/features/dashboard_Id/Card/cardModal/CommentForm.tsx
new file mode 100644
index 0000000..08cc66f
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/CommentForm.tsx
@@ -0,0 +1,69 @@
+import { useForm } from 'react-hook-form'
+
+import { usePostCommentMutation } from '../../api/usePostCommentMutation'
+import { CommentFormData } from '../../type/CommentFormData.type'
+
+export default function CommentForm({
+ cardId,
+ columnId,
+ dashboardId,
+}: {
+ cardId: number
+ columnId: number
+ dashboardId: number
+}) {
+ const {
+ register,
+ handleSubmit,
+ reset,
+ formState: { errors, isValid, isSubmitting },
+ } = useForm
({
+ mode: 'onChange',
+ })
+
+ // 폼 제출 핸들러 함수
+ const { mutate: createComment, isPending } = usePostCommentMutation()
+
+ function onSubmit(data: CommentFormData) {
+ const payload = {
+ ...data,
+ cardId,
+ columnId,
+ dashboardId,
+ }
+ console.log(payload)
+ createComment(payload)
+ reset()
+ }
+
+ return (
+
+ )
+}
diff --git a/src/app/features/dashboard_Id/Card/cardModal/CommentModifyForm.tsx b/src/app/features/dashboard_Id/Card/cardModal/CommentModifyForm.tsx
new file mode 100644
index 0000000..379012b
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/CommentModifyForm.tsx
@@ -0,0 +1,66 @@
+import { useForm } from 'react-hook-form'
+
+import { usePutCommentMutation } from '../../api/usePutCommentsMutation'
+import { PutCommentForm } from '../../type/CommentFormData.type'
+
+export default function CommentModifyForm({
+ onClose,
+ commentId,
+ content,
+}: {
+ onClose: () => void
+ commentId: number
+ content: string
+}) {
+ const {
+ register,
+ handleSubmit,
+
+ formState: { errors, isValid, isSubmitting },
+ } = useForm({
+ defaultValues: {
+ content: content,
+ },
+ })
+
+ // 폼 제출 핸들러 함수
+ const { mutate: createComment, isPending } = usePutCommentMutation()
+
+ function onSubmit(data: PutCommentForm) {
+ console.log(data)
+ console.log(commentId)
+ createComment({ payload: data, commentId }) //useMutation은 하나의 인자만 허용. 객체 하나로 묶어서 전달하는거 자꾸 까먹음..
+ onClose()
+ }
+
+ return (
+
+ )
+}
diff --git a/src/app/features/dashboard_Id/Card/cardModal/Comments.tsx b/src/app/features/dashboard_Id/Card/cardModal/Comments.tsx
new file mode 100644
index 0000000..ed8ae50
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/Comments.tsx
@@ -0,0 +1,46 @@
+import { toast } from 'sonner'
+
+import { useInfiniteComments } from '../../api/useInfiniteComments'
+import { useInfiniteScroll } from '../../hooks/useInfiniteScroll'
+import { Comment as CommentType } from '../../type/Comment.type'
+import Comment from './Comment'
+
+export default function Comments({
+ cardId,
+ scrollRef,
+}: {
+ cardId: number
+ scrollRef: React.RefObject
+}) {
+ const {
+ data,
+ fetchNextPage,
+ hasNextPage,
+ isFetchingNextPage,
+ isLoading,
+ isError,
+ } = useInfiniteComments(cardId)
+
+ useInfiniteScroll(fetchNextPage, hasNextPage, isFetchingNextPage, scrollRef)
+
+ if (isError) {
+ toast.error('댓글 불러오기 실패')
+ }
+ return (
+ <>
+
+ {data?.pages.map((page) =>
+ page?.comments.map((comment: CommentType) => (
+
+ )),
+ )}
+
+ {/* 무한 스크롤 관련 */}
+ {isFetchingNextPage && (
+
+ 댓글을 불러오는 중...
+
+ )}
+ >
+ )
+}
diff --git a/src/app/features/dashboard_Id/Column/Column.tsx b/src/app/features/dashboard_Id/Column/Column.tsx
index f55c4dc..1ddb116 100644
--- a/src/app/features/dashboard_Id/Column/Column.tsx
+++ b/src/app/features/dashboard_Id/Column/Column.tsx
@@ -1,13 +1,15 @@
import Image from 'next/image'
import { useState } from 'react'
+import { toast } from 'sonner'
import { cn } from '@/app/shared/lib/cn'
import { useCardMutation } from '../api/useCardMutation'
-import useCards from '../api/useCards'
+import { useInfiniteCards } from '../api/useInfiniteCards'
import Card from '../Card/Card'
import CreateCardForm from '../Card/cardFormModals/CreateCardForm'
import CreateCardModal from '../Card/cardFormModals/CreateCardModal'
+import { useInfiniteScroll } from '../hooks/useInfiniteScroll'
import { useColumnModalStore } from '../store/useColumnModalStore'
import { useDragStore } from '../store/useDragStore'
import type { Column as ColumnType } from '../type/Column.type'
@@ -20,8 +22,19 @@ export default function Column({
dashboardId: number
}) {
const { id, title }: { id: number; title: string } = column
- const { data, isLoading, error } = useCards(id)
const { openModal } = useColumnModalStore()
+
+ const {
+ data,
+ fetchNextPage,
+ hasNextPage,
+ isFetchingNextPage,
+ isLoading,
+ isError,
+ } = useInfiniteCards(id)
+
+ useInfiniteScroll(fetchNextPage, hasNextPage, isFetchingNextPage)
+
const [isDraggingover, setDraggingover] = useState(false)
const { draggingCard, clearDraggingCard } = useDragStore()
const cardMutation = useCardMutation()
@@ -38,8 +51,8 @@ export default function Column({
})
}
- if (isLoading) return loading...
- if (error) return error...{error.message}
+ if (isLoading) return loading...
// 스켈레톤 적용???⭐️
+ if (isError) return toast.error('할 일 불러오기 실패')
return (
{title}
- {data?.totalCount}
+ {data?.pages[0]?.totalCount ?? 0}