Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
10 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
)
})}
Comment on lines +6 to +26
Copy link

Choose a reason for hiding this comment

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

๐Ÿ› ๏ธ Refactor suggestion

๋ฐฐ๊ฒฝ/ํ…์ŠคํŠธ ์ƒ‰ ๋ฐฐ์—ด ๊ธธ์ด ๋ถˆ์ผ์น˜ ๊ฐ€๋Šฅ์„ฑ ๋Œ€๋น„

bgColors์™€ textColors ๋ฐฐ์—ด ๊ธธ์ด๊ฐ€ ๋‹ฌ๋ผ์ง€๋ฉด colorIndex๊ฐ€ textColors ๋ฒ”์œ„๋ฅผ ์ดˆ๊ณผํ•ด undefined๋ฅผ ๋ฐ˜ํ™˜, ์Šคํƒ€์ผ ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ๋ฐฐ์—ด ๊ธธ์ด๋ฅผ ๊ฒ€์ฆํ•˜๊ฑฐ๋‚˜ ํ•˜๋‚˜์˜ ๋ฐฐ์—ด์— ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

-const bgColors = ['#F9EEE3', '#E7F7DB', '#F7DBF0', '#DBE6F7']
-const textColors = ['#D58D49', '#86D549', '#D549B6', '#4981D5']
+const colorPairs = [
+  { bg: '#F9EEE3', text: '#D58D49' },
+  { bg: '#E7F7DB', text: '#86D549' },
+  { bg: '#F7DBF0', text: '#D549B6' },
+  { bg: '#DBE6F7', text: '#4981D5' },
+]
...
-const colorIndex = getColor(tag, bgColors)
+const colorIndex = getColor(tag, colorPairs)
 ...
-  backgroundColor: bgColors[colorIndex],
-  color: textColors[colorIndex],
+  backgroundColor: colorPairs[colorIndex].bg,
+  color: colorPairs[colorIndex].text,
๐Ÿ“ 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
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>
)
})}
// Replace separate bg/text arrays with one array of paired objects
const colorPairs = [
{ bg: '#F9EEE3', text: '#D58D49' },
{ bg: '#E7F7DB', text: '#86D549' },
{ bg: '#F7DBF0', text: '#D549B6' },
{ bg: '#DBE6F7', text: '#4981D5' },
]
return (
<div className="flex flex-wrap gap-6">
{tags.map((tag) => {
// getColor just needs the array length to pick an index
const colorIndex = getColor(tag, colorPairs)
return (
<span
key={tag}
className="inline-block whitespace-nowrap rounded-4 px-9 pb-3 pt-5"
style={{
backgroundColor: colorPairs[colorIndex].bg,
color: colorPairs[colorIndex].text,
}}
>
{tag}
</span>
)
})}
๐Ÿค– Prompt for AI Agents
In src/app/dashboard/[id]/Card/Tags.tsx around lines 6 to 26, the bgColors and
textColors arrays have different lengths, which can cause colorIndex to exceed
the bounds of textColors and result in undefined color values and style
warnings. To fix this, either validate that both arrays have the same length
before using colorIndex or refactor the code to use a single array of objects
where each object contains both background and text color properties, ensuring
consistent indexing and preventing out-of-bounds access.

</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)
Copy link

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue

์ƒํƒœ ๋ณ€์ˆ˜๋ช… ํƒ€์ดํฌ ์ˆ˜์ • ํ•„์š”

oepnConfigColumn์€ openConfigColumn์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ํƒ€์ดํฌ๋กœ ์ธํ•ด ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์„ธ์š”:

-const [oepnConfigColumn, setConfigColumn] = useState(false)
+const [openConfigColumn, setOpenConfigColumn] = useState(false)

๊ทธ๋ฆฌ๊ณ  75๋ฒˆ ์ค„๋„ ํ•จ๊ป˜ ์ˆ˜์ •:

-onClick={() => setConfigColumn(true)}
+onClick={() => setOpenConfigColumn(true)}

Also applies to: 75-75

๐Ÿค– Prompt for AI Agents
In src/app/dashboard/[id]/Column/Column.tsx at lines 21 and 75, the state
variable name `oepnConfigColumn` is a typo and should be corrected to
`openConfigColumn`. Rename all instances of `oepnConfigColumn` to
`openConfigColumn` to ensure the state updates correctly and consistently.


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 }
},
)
Comment on lines 53 to 64
Copy link

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue

oldData๊ฐ€ ์—†์„ ๋•Œ undefined๋ฅผ ์บ์‹œ์— ๋ฎ์–ด์“ฐ๋Š” ๋ฒ„๊ทธ

setQueryData ์ฝœ๋ฐฑ์—์„œ oldData๊ฐ€ falsy์ด๋ฉด return๋งŒ ์ˆ˜ํ–‰ํ•ด undefined๊ฐ€ ์บ์‹œ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ์บ์‹œ๋ฅผ ๋‚ ๋ ค ๋ฒ„๋ฆฌ๋Š” ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

-          if (!oldData) return
+          if (!oldData) return oldData   // ์บ์‹œ ๋ณด์กด
๐Ÿ“ 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
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 }
},
)
queryClient.setQueryData<CardResponse>(
['columnId', currentCard.cardData.columnId],
(oldData) => {
- if (!oldData) return
+ if (!oldData) return oldData // ์บ์‹œ ๋ณด์กด
const filtered = oldData.cards.filter((card) => {
return card.id !== cardData.id
})
return { ...oldData, cards: filtered }
},
)
๐Ÿค– Prompt for AI Agents
In src/app/dashboard/[id]/api/useCardMutation.ts around lines 53 to 64, the
setQueryData callback returns undefined when oldData is falsy, which overwrites
the cache with undefined and causes data loss. To fix this, modify the callback
to return oldData itself instead of just returning without a value when oldData
is falsy, preserving the existing cache data.

// 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 })
return {
...oldData,
cards: [...oldData.cards, movedCard],
}
},
)

clearDraggingCard()
return { previousData }
Expand Down
Loading