Skip to content
Merged
37 changes: 30 additions & 7 deletions src/app/dashboard/[id]/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Image from 'next/image'

import type { Card as CardType } from '@/app/api/useCards'
import { Avatar } from '@/app/shared/components/common/Avatar'

import { useDragStore } from '../store/useDragStore'
import Tags from './Tags'
Expand All @@ -16,26 +17,48 @@ export default function Card({
const { setDraggingCard } = useDragStore()
return (
<div
data-card-id={card.id}
data-card-data={JSON.stringify(card)}
draggable="true"
onDragStart={() => setDraggingCard({ cardId: id, columnId: columnId })}
className="BG-white Border-section relative w-314 rounded-6 border-solid px-20 py-16"
onDragStart={() => setDraggingCard({ cardData: card })}
onContextMenu={(e: React.MouseEvent) => e.preventDefault()}
className="BG-white Border-section relative rounded-6 border-solid px-20 py-16"
>
Todo Card
{imageUrl && (
<Image
src={imageUrl}
alt="카드 이미지"
width={400}
height={600}
className="h-auto w-full rounded-6 object-contain"
className="mb-15 h-auto w-full rounded-6 object-contain"
priority
draggable="false"
/>
)}
<p>{title}</p>
<h3 className="Text-black mb-10 text-16 font-medium leading-relaxed">
{title}
</h3>
<Tags tags={tags} />
<p>{dueDate}</p>
<p>프로필</p>
<div className="mt-8 flex content-around items-center">
<div className="flex size-full items-center gap-6">
<Image
src={'/images/calendar.svg'}
alt="마감일"
width={18}
height={18}
/>
<div className="Text-gray mt-4 text-12 font-medium leading-none">
{dueDate}
</div>
</div>
<div className="shrink-0">
<Avatar
nickname={assignee.nickname}
imageUrl={assignee.profileImageUrl}
size={24}
/>
</div>
</div>
</div>
)
}
32 changes: 22 additions & 10 deletions src/app/dashboard/[id]/Card/Tags.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import { getColor } from '@/app/shared/lib/getColor'

export default function Tags({ tags }: { tags: string[] }) {
//태그 컬러 - 랜덤 배정
//카드 생성 시 - 동일 태그 입력 불가하도록
const bgColors = ['#F9EEE3', '#E7F7DB', '#F7DBF0', '#DBE6F7']
const textColors = ['#D58D49', '#86D549', '#D549B6', '#4981D5']

return (
<div className="flex gap-6">
{tags.map((tag) => (
<span
key={tag}
className="inline-block whitespace-nowrap rounded-4 px-9 pb-3 pt-5"
style={{ backgroundColor: '#F7DBF0', color: '#D549B6' }}
>
{tag}
</span>
))}
<div className="flex flex-wrap gap-6">
{tags.map((tag) => {
const colorIndex = getColor(tag, bgColors)

return (
<span
key={tag}
className="inline-block whitespace-nowrap rounded-4 px-9 pb-3 pt-5"
style={{
backgroundColor: bgColors[colorIndex],
color: textColors[colorIndex],
}}
>
{tag}
</span>
)
})}
</div>
)
}
27 changes: 19 additions & 8 deletions src/app/dashboard/[id]/Column/Column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export default function Column({ column }: { column: ColumnType }) {
const [isDraggingover, setDraggingover] = useState(false)
const { clearDraggingCard } = useDragStore()
const cardMutation = useCardMutation()
const [openCard, setOpenCard] = useState(false) //card.tsx
const [openCreateCard, setOpenCreateCard] = useState(false)
const [openCreateColumn, setOpenCreateColumn] = useState(false) //page.tsx
const [oepnConfigColumn, setConfigColumn] = useState(false)

if (isLoading) return <p>loading...</p>
if (error) return <p>error...{error.message}</p>
Expand All @@ -33,29 +37,32 @@ export default function Column({ column }: { column: ColumnType }) {
e.preventDefault()
if (isDraggingover) setDraggingover(false)
const draggingCard = useDragStore.getState().draggingCard

if (!draggingCard) {
console.log('no dragging card') //TODO - toast 처리 🍞
return
}
// 동일 컬럼이면 무시
if (draggingCard.columnId === id) {

if (draggingCard.cardData.columnId === id) {
clearDraggingCard()
return
}
cardMutation.mutate({ cardId: draggingCard.cardId, columnId: id })
cardMutation.mutate({
columnId: id,
cardData: draggingCard.cardData,
})
}}
data-column-id={id}
className={cn(
'BG-gray Border-column flex w-354 shrink-0 flex-col gap-16 p-20',
'BG-gray Border-column tablet:w-584 flex w-354 shrink-0 flex-col gap-16 p-20',
{
'!border-blue-500': isDraggingover,
},
)}
>
<div className="mb-24 flex items-center justify-between">
<div className="flex items-center">
<div className="mb-7 mr-8 size-8 rounded-25 bg-blue-500"></div>
<h2 className="mr-12 text-18 font-bold leading-none">{title}</h2>
<div className="BG-blue mb-7 mr-8 size-8 rounded-25"></div>
<h2 className="mb-3 mr-12 h-21 text-18 font-bold">{title}</h2>
<span className="Text-gray block size-20 rounded-4 bg-[#EEEEEE] pt-3 text-center text-12 font-medium leading-tight dark:bg-[#2E2E2E]">
{data?.totalCount}
</span>
Expand All @@ -65,9 +72,13 @@ export default function Column({ column }: { column: ColumnType }) {
alt="컬럼 설정"
width={20}
height={20}
onClick={() => setConfigColumn(true)}
/>
</div>
<button className="BG-white Border-btn rounded-6 px-146 py-9">
<button
className="BG-white Border-btn flex justify-center rounded-6 py-9"
onClick={() => setOpenCreateCard(true)}
>
<div className="flex h-22 w-22 items-center justify-center rounded-4 bg-blue-100">
<Image
src={'/images/plus.svg'}
Expand Down
50 changes: 25 additions & 25 deletions src/app/dashboard/[id]/api/useCardMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,22 @@ export const useCardMutation = () => {
return useMutation({
// 1. 서버 API 호출
// cardId: 드래그한 카드 아이디, columnId: dragOver된 타겟 컬럼 아이디
mutationFn: ({ cardId, columnId }: { cardId: number; columnId: number }) =>
updateCardColumn(cardId, columnId),
mutationFn: ({
columnId,
cardData,
}: {
columnId: number
cardData: Card
}) => updateCardColumn(cardData.id, columnId),

// 2. 낙관적 UI 처리 (서버 요청 전에 실행됨)
onMutate: async ({ cardId, columnId }) => {
onMutate: async ({ columnId, cardData }) => {
const currentCard = useDragStore.getState().draggingCard

await Promise.all([
queryClient.cancelQueries({ queryKey: ['columnId', columnId] }),
queryClient.cancelQueries({
queryKey: ['columnId', currentCard?.columnId],
queryKey: ['columnId', currentCard?.cardData.columnId],
}),
])

Expand All @@ -35,47 +40,42 @@ export const useCardMutation = () => {
// Guard return
if (
!currentCard ||
currentCard.cardId !== cardId ||
currentCard.columnId === columnId
currentCard.cardData.id !== cardData.id ||
currentCard.cardData.columnId === columnId
) {
console.log('no dragging card || is not a dragging card || same column')
clearDraggingCard()
return
}

let extractedCard: Card | undefined // B. 에서 사용할 예정(추가할 카드 데이터는 Card여야 해서)
// A. 이전 컬럼에서 카드 제거 & 카드 추출
// setQueryData의 콜백함수의 리턴값이 쿼리키 캐시에 저장됨(캐시 업데이트)
queryClient.setQueryData<CardResponse>(
['columnId', currentCard.columnId],
['columnId', currentCard.cardData.columnId],
(oldData) => {
if (!oldData) return

const filtered = oldData.cards.filter((card) => {
if (card.id === cardId) extractedCard = card
return card.id !== cardId
return card.id !== cardData.id
})

return { ...oldData, cards: filtered }
},
)
// B. 새 컬럼에 카드 추가
if (extractedCard) {
queryClient.setQueryData<CardResponse>(
['columnId', columnId],
(oldData) => {
if (!oldData) return
queryClient.setQueryData<CardResponse>(
['columnId', columnId],
(oldData) => {
if (!oldData) return

const movedCard = { ...extractedCard!, columnId }
return {
...oldData,
cards: [...oldData.cards, movedCard],
}
},
)
} else {
console.log('카드가 제거 중에 undefined가 됨')
}
const movedCard = { ...cardData, columnId: columnId }
console.log('Cardcolumn changed', { movedCard })
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

프로덕션 코드에서 console.log 제거 필요

-          console.log('Cardcolumn changed', { movedCard })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log('Cardcolumn changed', { movedCard })
🤖 Prompt for AI Agents
In src/app/dashboard/[id]/api/useCardMutation.ts at line 72, remove the
console.log statement used for debugging to ensure no debug logs remain in the
production code.

return {
...oldData,
cards: [...oldData.cards, movedCard],
}
},
)

clearDraggingCard()
return { previousData }
Expand Down
Loading