From 7239a6cc47e16eea5e790a65ca96e08c83bdc0aa Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 21 Jun 2025 04:47:16 +0900 Subject: [PATCH 01/16] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EB=AA=A8=EB=8B=AC=20=EC=83=81=EB=8B=A8=20?= =?UTF-8?q?UI=20=EB=B0=8F=20=EB=B0=98=EC=9D=91=ED=98=95=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ“ TODO: - ๋‹ด๋‹น์ž ์˜† ์•„๋ฐ”ํƒ€์— ๋ฐ˜์‘ํ˜• ์‚ฌ์ด์ฆˆ ์ ์šฉ ํ•„์š” - ์นด๋“œ ์ˆ˜์ • ๋ชจ๋‹ฌ์˜ ์ปฌ๋Ÿผ ์„ ํƒ ๊ฐ’์„ form ๋ฐ์ดํ„ฐ์™€ ์—ฐ๊ฒฐ ํ•„์š” --- src/app/features/dashboard_Id/Card/Card.tsx | 18 +++ .../dashboard_Id/Card/ColumnTitle.tsx | 2 +- .../Card/cardFormModals/ModifyCardForm.tsx | 7 +- .../Card/cardModal/CardContent.tsx | 142 +++++++++++++----- .../Card/cardModal/CardContentCopy.tsx | 110 ++++++++++++++ .../dashboard_Id/Card/cardModal/CardModal.tsx | 2 +- .../dashboard_Id/api/usePutCardMutation.ts | 2 +- 7 files changed, 240 insertions(+), 43 deletions(-) create mode 100644 src/app/features/dashboard_Id/Card/cardModal/CardContentCopy.tsx diff --git a/src/app/features/dashboard_Id/Card/Card.tsx b/src/app/features/dashboard_Id/Card/Card.tsx index 24218ae..948bbb8 100644 --- a/src/app/features/dashboard_Id/Card/Card.tsx +++ b/src/app/features/dashboard_Id/Card/Card.tsx @@ -6,6 +6,8 @@ import { Avatar } from '@/app/shared/components/common/Avatar' import { useDragStore } from '../store/useDragStore' import type { Card as CardType } from '../type/Card.type' import type { Column as ColumnType } from '../type/Column.type' +import CreateCardModal from './cardFormModals/CreateCardModal' +import ModifyCardForm from './cardFormModals/ModifyCardForm' import CardContent from './cardModal/CardContent' import CardModal from './cardModal/CardModal' import Tags from './Tags' @@ -20,6 +22,9 @@ export default function Card({ const { id, imageUrl, title, tags, dueDate, assignee } = card const { setDraggingCard } = useDragStore() const [openCard, setOpenCard] = useState(false) //card.tsx + const [openModifyCard, setOpenModifyCard] = useState(false) + const { title: columnTitle, id: columnId } = column + const currentColumn = { columnTitle, columnId } return (
setOpenCard(false)} + openModifyModal={() => setOpenModifyCard(true)} card={card} column={column} /> )} + + {/* ์นด๋“œ ์ˆ˜์ • ๋ชจ๋‹ฌ */} + {openModifyCard && ( + + setOpenModifyCard(false)} + // columnId={column.id} + currentColumn={currentColumn} + card={card} + /> + + )}
) } diff --git a/src/app/features/dashboard_Id/Card/ColumnTitle.tsx b/src/app/features/dashboard_Id/Card/ColumnTitle.tsx index 9311ac5..810fdbd 100644 --- a/src/app/features/dashboard_Id/Card/ColumnTitle.tsx +++ b/src/app/features/dashboard_Id/Card/ColumnTitle.tsx @@ -2,7 +2,7 @@ export default function ColumnTitle({ title }: { title: string }) { return (
-
+
{title}
diff --git a/src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx b/src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx index 61ca09d..cc486dd 100644 --- a/src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx +++ b/src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx @@ -78,7 +78,6 @@ export default function ModifyCardForm({ // React Hook Form ๊ณผ tags ๊ฐ’ ์—ฐ๊ฒฐ useEffect(() => { setValue('tags', tags) - console.log(tags) }, [tags, tags.length, setValue]) // ์ƒํƒœ(์ปฌ๋Ÿผ) ์„ ํƒ ์‹œ / assignee ์„ ํƒ ์‹œ ๋“œ๋กญ๋‹ค์šด ๋‹ซ๊ธฐ @@ -125,7 +124,11 @@ export default function ModifyCardForm({ // โœ… JSX return ( -
+ e.stopPropagation()} + >

ํ•  ์ผ ์ˆ˜์ •

diff --git a/src/app/features/dashboard_Id/Card/cardModal/CardContent.tsx b/src/app/features/dashboard_Id/Card/cardModal/CardContent.tsx index ef9b563..5ab566a 100644 --- a/src/app/features/dashboard_Id/Card/cardModal/CardContent.tsx +++ b/src/app/features/dashboard_Id/Card/cardModal/CardContent.tsx @@ -1,32 +1,41 @@ import Image from 'next/image' import { useState } from 'react' +import { Avatar } from '@/app/shared/components/common/Avatar' import Dropdown from '@/app/shared/components/common/Dropdown/Dropdown' import { Card } from '../../type/Card.type' import { Column } from '../../type/Column.type' import CreateCardModal from '../cardFormModals/CreateCardModal' import ModifyCardForm from '../cardFormModals/ModifyCardForm' +import ColumnTitle from '../ColumnTitle' import Tags from '../Tags' -interface CardContentProps { - onClose: () => void - card: Card - column: Column -} + export default function CardContent({ onClose, + openModifyModal, card, column, -}: CardContentProps) { - const { id, imageUrl, title, tags, dueDate, assignee } = card +}: { + onClose: () => void + openModifyModal: () => void + card: Card + column: Column +}) { + // const { id, imageUrl, title, tags, dueDate, assignee, description } = card const [openModifyCard, setOpenModifyCard] = useState(false) - const { title: columnTitle, id: columnId } = column - const currentColumn = { columnTitle, columnId } + // const { title: columnTitle, id: columnId } = column + // const currentColumn = { columnTitle, columnId } return ( - <> -

{title}

-
+ //
+
+

+ {card.title} +

+ + {/* ์นด๋“œ ์šฐ์ธก ์ƒ๋‹จ์˜ ์ผ€๋ฐฅ ๋ฒ„ํŠผ, X ๋ฒ„ํŠผ */} +
} > - {/* ๋“œ๋กญ๋‹ค์šด ๋‚ด๋ถ€ ๋ฉ”๋‰ด ์•„์ดํ…œ */} -
setOpenModifyCard(true)} + {/* ํŠธ๋ฆฌ๊ฑฐ์˜(์ผ€๋ฐฅ) ๋“œ๋กญ๋‹ค์šด - ์ˆ˜์ •ํ•˜๊ธฐ, ์‚ญ์ œํ•˜๊ธฐ */} +
-
+ +
+
- ์นด๋“œ ๋‹ซ๊ธฐ { + {/* X ๋ฒ„ํŠผ (์นด๋“œ ๋‹ซ๊ธฐ)*/} +
+ {/* ์นด๋“œ ์ƒ๋‹จ */}
- ๊ทธ๋ฆฌ๋“œ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ / ์ปฌ๋Ÿผ๋ช…์€ ๋”ฐ๋กœ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค์–ด ๋นผ๊ธฐ -
{column.title}
- -

์„ค๋ช…~~~~~~~~

-
-
{assignee.nickname}
-
{dueDate}
+
+ {/* ์ปฌ๋Ÿผ๋ช… / ํƒœ๊ทธ // ์„ค๋ช… // ์ด๋ฏธ์ง€ */} +
+
+ +
+ +
+

+ {card.description} +

+ ์นด๋“œ ์ด๋ฏธ์ง€ +
+ {/* ๋‹ด๋‹น์ž / ๋งˆ๊ฐ์ผ ๋ฐ•์Šค */} +
+
+
+ + ๋‹ด๋‹น์ž + +
+ + + {card.assignee.nickname} + +
+
+
+ + ๋งˆ๊ฐ์ผ + + + {card.dueDate} + +
+
+
+
-
์ด๋ฏธ์ง€ ์žˆ์œผ๋ฉด ์ด๋ฏธ์ง€
+
๋Œ“๊ธ€ ์ปดํฌ๋„ŒํŠธ
- {/* ์นด๋“œ ์ˆ˜์ • ๋ชจ๋‹ฌ */} + {/* ์นด๋“œ ์ˆ˜์ • ๋ชจ๋‹ฌ {openModifyCard && ( - )} - + )} */} +
) } diff --git a/src/app/features/dashboard_Id/Card/cardModal/CardContentCopy.tsx b/src/app/features/dashboard_Id/Card/cardModal/CardContentCopy.tsx new file mode 100644 index 0000000..933b48c --- /dev/null +++ b/src/app/features/dashboard_Id/Card/cardModal/CardContentCopy.tsx @@ -0,0 +1,110 @@ +// import { useModalStore } from '@store/useModalStore' +// import Image from 'next/image' +// import { useState } from 'react' + +// import Dropdown from '@/app/shared/components/common/Dropdown/Dropdown' + +// import { Card } from '../../type/Card.type' +// import { Column } from '../../type/Column.type' +// import CreateCardModal from '../cardFormModals/CreateCardModal' +// import ModifyCardForm from '../cardFormModals/ModifyCardForm' +// import Tags from '../Tags' +// interface CardContentProps { +// onClose: () => void +// card: Card +// column: Column +// } +// export default function CardContent({ +// onClose, +// card, +// column, +// }: CardContentProps) { +// const { id, imageUrl, title, tags, dueDate, assignee } = card +// const [openModifyCard, setOpenModifyCard] = useState(false) +// const { title: columnTitle, id: columnId } = column +// const currentColumn = { columnTitle, columnId } +// const { modalType, closeModal } = useModalStore() + +// const handleBackdropClick = (e: React.MouseEvent) => { +// if (e.target === e.currentTarget) { +// closeModal() +// } +// } + +// if (!modalType) return null + +// return ( +// <> +//
+//
+//

์ดˆ๋Œ€ํ•˜๊ธฐ

+ +//

{title}

+//
+// +// } +// > +// {/* ๋“œ๋กญ๋‹ค์šด ๋‚ด๋ถ€ ๋ฉ”๋‰ด ์•„์ดํ…œ */} +//
setOpenModifyCard(true)} +// > +// ์ˆ˜์ •ํ•˜๊ธฐ +//
+//
+// ์‚ญ์ œํ•˜๊ธฐ +//
+//
+// ์นด๋“œ ๋‹ซ๊ธฐ { +// onClose() +// console.log('sdfadfdsgreggsfds') +// }} +// /> +//
+ +//
+// ๊ทธ๋ฆฌ๋“œ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ / ์ปฌ๋Ÿผ๋ช…์€ ๋”ฐ๋กœ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค์–ด ๋นผ๊ธฐ +//
{column.title}
+// +//

์„ค๋ช…~~~~~~~~

+//
+//
{assignee.nickname}
+//
{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..bef9e45 100644 --- a/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx +++ b/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx @@ -9,7 +9,7 @@ export default function CardModal({ children }: ModalProps) { return createPortal(
-
+
{children}
, diff --git a/src/app/features/dashboard_Id/api/usePutCardMutation.ts b/src/app/features/dashboard_Id/api/usePutCardMutation.ts index 0eb536a..cc8fc10 100644 --- a/src/app/features/dashboard_Id/api/usePutCardMutation.ts +++ b/src/app/features/dashboard_Id/api/usePutCardMutation.ts @@ -18,7 +18,7 @@ export function usePutCardMutation() { payload: CardModifyFormData }) => putCard(payload, cardId), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['cardId'] }) //'columnId' ์ฟผ๋ฆฌ invalidate - ์นด๋“œ๊ฐ€ stale ์ƒํƒœ์ž„์„ ์•Œ๋ฆฌ๊ณ  ๋‹ค์‹œ fetch ํ•˜๋„๋ก ์œ ๋„ํ•จ + queryClient.invalidateQueries({ queryKey: ['columnId'] }) //'columnId' ์ฟผ๋ฆฌ invalidate - ์นด๋“œ๊ฐ€ stale ์ƒํƒœ์ž„์„ ์•Œ๋ฆฌ๊ณ  ๋‹ค์‹œ fetch ํ•˜๋„๋ก ์œ ๋„ํ•จ }, onError: (error) => { if (axios.isAxiosError(error)) { From 14d0c65861a66b104aa3515f965726440d6084ff Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 21 Jun 2025 18:48:24 +0900 Subject: [PATCH 02/16] =?UTF-8?q?=E2=9C=A8=20feat:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1,=EC=88=98=EC=A0=95=20=EC=99=84=EC=84=B1=20/?= =?UTF-8?q?=20useIsMobile=EA=B3=B5=ED=86=B5=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Card/cardFormModals/ModifyCardForm.tsx | 16 +-- .../Card/cardModal/CardContent.tsx | 108 +++++++++++------- .../dashboard_Id/Card/cardModal/Comment.tsx | 50 ++++++++ .../Card/cardModal/CommentForm.tsx | 64 +++++++++++ .../Card/cardModal/CommentModifyForm.tsx | 62 ++++++++++ .../dashboard_Id/Card/cardModal/Comments.tsx | 24 ++++ .../dashboard_Id/api/fetchComments.ts | 16 +++ .../features/dashboard_Id/api/postComment.ts | 14 +++ .../features/dashboard_Id/api/putComment.ts | 15 +++ .../dashboard_Id/api/useCommentsQuery.ts | 11 ++ .../api/usePostCommentMutation.ts | 24 ++++ .../dashboard_Id/api/usePutCardMutation.ts | 4 +- .../api/usePutCommentsMutation.ts | 30 +++++ .../dashboard_Id/type/Comment.type.ts | 17 +++ .../dashboard_Id/type/CommentFormData.type.ts | 20 ++++ src/app/globals.css | 2 +- src/app/shared/hooks/useIsmobile.ts | 17 +++ 17 files changed, 440 insertions(+), 54 deletions(-) create mode 100644 src/app/features/dashboard_Id/Card/cardModal/Comment.tsx create mode 100644 src/app/features/dashboard_Id/Card/cardModal/CommentForm.tsx create mode 100644 src/app/features/dashboard_Id/Card/cardModal/CommentModifyForm.tsx create mode 100644 src/app/features/dashboard_Id/Card/cardModal/Comments.tsx create mode 100644 src/app/features/dashboard_Id/api/fetchComments.ts create mode 100644 src/app/features/dashboard_Id/api/postComment.ts create mode 100644 src/app/features/dashboard_Id/api/putComment.ts create mode 100644 src/app/features/dashboard_Id/api/useCommentsQuery.ts create mode 100644 src/app/features/dashboard_Id/api/usePostCommentMutation.ts create mode 100644 src/app/features/dashboard_Id/api/usePutCommentsMutation.ts create mode 100644 src/app/features/dashboard_Id/type/Comment.type.ts create mode 100644 src/app/features/dashboard_Id/type/CommentFormData.type.ts create mode 100644 src/app/shared/hooks/useIsmobile.ts diff --git a/src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx b/src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx index cc486dd..14dbcd3 100644 --- a/src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx +++ b/src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx @@ -63,14 +63,14 @@ export default function ModifyCardForm({ formState: { errors, isValid, isSubmitting, isDirty }, } = useForm({ defaultValues: { - assigneeUserId: card.assignee.id, + assigneeUserId: card.assignee?.id, dashboardId: card.dashboardId, columnId: card.columnId, title: card.title, description: card.description, - dueDate: card.dueDate, - tags: card.tags, - imageUrl: card.imageUrl, + dueDate: card.dueDate ? card.dueDate : '', + tags: card.tags ? card.tags : [], + imageUrl: card.imageUrl ? card.imageUrl : '', }, mode: 'onChange', // isValid์™€ isDirty๊ฐ€ ์ž…๋ ฅ ์ฆ‰์‹œ ๋ฐ˜์˜๋˜๋„๋ก }) @@ -191,9 +191,11 @@ export default function ModifyCardForm({ id="assigneeUserId" type="text" /> -
- -
+ {selectedAssignee && ( +
+ +
+ )} ํ™”์‚ดํ‘œ @@ -88,50 +92,73 @@ export default function CardContent({
-
- +
+ {card.tags && }
-

- {card.description} -

- ์นด๋“œ ์ด๋ฏธ์ง€ + {card.description && ( +

+ {card.description} +

+ )} + {card.imageUrl && ( + ์นด๋“œ ์ด๋ฏธ์ง€ + )}
{/* ๋‹ด๋‹น์ž / ๋งˆ๊ฐ์ผ ๋ฐ•์Šค */}
-
+
๋‹ด๋‹น์ž -
- - - {card.assignee.nickname} - -
+ {card.assignee && ( +
+ {isMobile ? ( + <> + + + {card.assignee.nickname} + + + ) : ( + <> + + + {card.assignee.nickname} + + + )} +
+ )}
๋งˆ๊ฐ์ผ - - {card.dueDate} - + {card.dueDate && ( + + {card.dueDate} + + )}
@@ -139,19 +166,12 @@ export default function CardContent({
-
๋Œ“๊ธ€ ์ปดํฌ๋„ŒํŠธ
- - {/* ์นด๋“œ ์ˆ˜์ • ๋ชจ๋‹ฌ - {openModifyCard && ( - - setOpenModifyCard(false)} - // columnId={column.id} - currentColumn={currentColumn} - card={card} - /> - - )} */} + +
) } 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..519f0db --- /dev/null +++ b/src/app/features/dashboard_Id/Card/cardModal/Comment.tsx @@ -0,0 +1,50 @@ +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 { 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) + + return ( +
+ {isMobile ? ( + + ) : ( + + )} +
+
+ + {comment.author.nickname} + + + {format(new Date(comment.createdAt), 'yyyy.MM.dd HH:mm')} + +
+ {modifyComment ? ( + + ) : ( +

{comment.content}

+ )} +
+ + +
+
+
+ ) +} 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..bae8193 --- /dev/null +++ b/src/app/features/dashboard_Id/Card/cardModal/CommentForm.tsx @@ -0,0 +1,64 @@ +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, + 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) + } + + return ( + + +
+