Skip to content

Commit 789a619

Browse files
authored
Merge pull request #196 from part3-4team-Taskify/minji
[Refactor, Fix] Card: 글자수 제한 에러 수정 & text size 반응형 적용
2 parents 9dafd2c + 74e9ac2 commit 789a619

19 files changed

+155
-168
lines changed

src/api/card.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export const updateCard = async (
123123
export const getCardsByColumn = async ({
124124
columnId,
125125
cursorId,
126-
size = 10,
126+
size = 300,
127127
}: {
128128
columnId: number;
129129
cursorId?: number;

src/components/card/ChangePassword.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default function ChangePassword() {
4545
return (
4646
<div
4747
className="flex flex-col w-[284px] sm:w-[544px] lg:w-[620px] min:h-[454px] sm:min-h-[466px]
48-
bg-white rounded-[16px] p-[24px]"
48+
bg-white rounded-[12px] p-[24px]"
4949
>
5050
<h2 className="text-black3 text-[18px] sm:text-[24px] font-bold mb-4">
5151
비밀번호 변경
@@ -72,7 +72,7 @@ export default function ChangePassword() {
7272
value={newPassword}
7373
onChange={setNewPassword}
7474
pattern=".{8,}"
75-
invalidMessage="8자 이상 입력해주세요."
75+
invalidMessage="8자 이상 입력해 주세요."
7676
className="max-w-[624px]"
7777
/>
7878
<Input

src/components/card/Profile.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const ProfileCard = () => {
7070
}, []);
7171

7272
return (
73-
<div className="flex flex-col w-[284px] sm:w-[544px] lg:w-[620px] h-[496px] sm:h-[366px] bg-white rounded-[16px] p-[24px]">
73+
<div className="flex flex-col w-[284px] sm:w-[544px] lg:w-[620px] h-[496px] sm:h-[366px] bg-white rounded-[12px] p-[24px]">
7474
{/* 프로필 제목 */}
7575
<h2 className="text-black3 text-[18px] sm:text-[24px] font-bold mb-4">
7676
프로필
Lines changed: 30 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useRef, useState, useCallback } from "react";
1+
import { useEffect, useState } from "react";
22
import {
33
DndContext,
44
closestCenter,
@@ -13,99 +13,51 @@ import {
1313
arrayMove,
1414
} from "@dnd-kit/sortable";
1515
import { CardType } from "@/types/task";
16-
import SortableCard from "@/components/columnCard/SortableCard";
1716
import { getCardsByColumn } from "@/api/card";
17+
import SortableCard from "@/components/columnCard/SortableCard";
18+
import { toast } from "react-toastify";
1819

19-
type CardListProps = {
20+
interface CardListProps {
2021
columnId: number;
2122
teamId: string;
2223
initialTasks: CardType[];
2324
onCardClick: (card: CardType) => void;
24-
};
25-
26-
const ITEMS_PER_PAGE = 6;
25+
}
2726

28-
export default function CardList({
29-
columnId,
27+
export const CardList = ({
3028
initialTasks,
29+
columnId,
3130
onCardClick,
32-
}: CardListProps) {
33-
const [cards, setCards] = useState<CardType[]>(initialTasks);
34-
const [cursorId, setCursorId] = useState<number | null>(
35-
initialTasks.length > 0 ? initialTasks[initialTasks.length - 1].id : null
36-
);
37-
const [hasMore, setHasMore] = useState(true);
38-
const observerRef = useRef<HTMLDivElement | null>(null);
39-
const isFetchingRef = useRef(false);
31+
}: CardListProps) => {
32+
const [cards, setCards] = useState<CardType[]>([]);
4033

4134
const sensors = useSensors(
4235
useSensor(PointerSensor, {
43-
activationConstraint: {
44-
distance: 5,
45-
},
36+
activationConstraint: { distance: 5 },
4637
})
4738
);
4839

49-
const fetchMoreCards = useCallback(async () => {
50-
if (isFetchingRef.current || !hasMore) return;
51-
52-
isFetchingRef.current = true;
53-
54-
try {
55-
const res = await getCardsByColumn({
56-
columnId,
57-
size: ITEMS_PER_PAGE,
58-
cursorId: cursorId ?? undefined,
59-
});
60-
61-
const newCards = res.cards as CardType[];
62-
63-
if (newCards.length > 0) {
64-
setCards((prev) => {
65-
const existingIds = new Set(prev.map((card) => card.id));
66-
const uniqueCards = newCards.filter(
67-
(card) => !existingIds.has(card.id)
68-
);
69-
return [...prev, ...uniqueCards];
70-
});
71-
72-
setCursorId((prevCursorId) => {
73-
const newCursor = newCards[newCards.length - 1]?.id ?? prevCursorId;
74-
return newCursor;
40+
// 카드 목록 api 호출 (마감일 빠른 순 정렬)
41+
useEffect(() => {
42+
const fetchCards = async () => {
43+
try {
44+
const res = await getCardsByColumn({ columnId });
45+
const sorted = [...res.cards].sort((a, b) => {
46+
const dateA = a.dueDate ? new Date(a.dueDate).getTime() : Infinity;
47+
const dateB = b.dueDate ? new Date(b.dueDate).getTime() : Infinity;
48+
return dateA - dateB;
7549
});
50+
setCards(sorted);
51+
} catch (error) {
52+
console.error("카드 불러오기 실패:", error);
53+
toast.error("카드를 불러오는 데 실패했습니다.");
7654
}
55+
};
7756

78-
if (newCards.length < ITEMS_PER_PAGE) {
79-
setHasMore(false);
80-
}
81-
} catch (error) {
82-
console.error("카드 로딩 실패:", error);
83-
} finally {
84-
isFetchingRef.current = false;
85-
}
86-
}, [columnId, cursorId, hasMore]);
87-
88-
useEffect(() => {
89-
if (!observerRef.current) return;
90-
91-
const observer = new IntersectionObserver(
92-
(entries) => {
93-
if (entries[0].isIntersecting && hasMore) {
94-
fetchMoreCards();
95-
}
96-
},
97-
{ threshold: 0.5 }
98-
);
99-
100-
observer.observe(observerRef.current);
101-
102-
return () => observer.disconnect();
103-
}, [fetchMoreCards, hasMore]);
104-
105-
useEffect(() => {
106-
setCards(initialTasks);
107-
}, [initialTasks]);
57+
fetchCards();
58+
}, [columnId, initialTasks]);
10859

60+
// 드래그 & 드롭
10961
const handleDragEnd = (event: DragEndEvent) => {
11062
const { active, over } = event;
11163

@@ -118,30 +70,22 @@ export default function CardList({
11870
setCards(newOrder);
11971
};
12072

121-
// 마감일 빠른 순 정렬
122-
const sortedCards = [...cards].sort((a, b) => {
123-
const dateA = a.dueDate ? new Date(a.dueDate).getTime() : Infinity;
124-
const dateB = b.dueDate ? new Date(b.dueDate).getTime() : Infinity;
125-
return dateA - dateB;
126-
});
127-
12873
return (
12974
<DndContext
13075
sensors={sensors}
13176
collisionDetection={closestCenter}
13277
onDragEnd={handleDragEnd}
13378
>
13479
<SortableContext
135-
items={sortedCards.map((card) => card.id)}
80+
items={cards.map((card) => card.id)}
13681
strategy={verticalListSortingStrategy}
13782
>
13883
<div className="grid gap-3 w-full grid-cols-1">
139-
{sortedCards.map((card) => (
84+
{cards.map((card) => (
14085
<SortableCard key={card.id} card={card} onClick={onCardClick} />
14186
))}
142-
{hasMore && <div ref={observerRef} className="h-20" />}
14387
</div>
14488
</SortableContext>
14589
</DndContext>
14690
);
147-
}
91+
};

src/components/columnCard/Column.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { updateColumn, deleteColumn } from "@/api/columns";
1010
import { getDashboardMembers, getCardDetail } from "@/api/card";
1111
import { MemberType } from "@/types/users";
1212
import { TEAM_ID } from "@/constants/team";
13-
import CardList from "./CardList";
13+
import { CardList } from "./CardList";
1414
import CardDetailModal from "@/components/modalDashboard/CardDetailModal";
1515
import { CardDetailType } from "@/types/cards";
1616
import { toast } from "react-toastify";
@@ -65,7 +65,7 @@ export default function Column({
6565

6666
const handleEditColumn = async (newTitle: string) => {
6767
if (!newTitle.trim()) {
68-
toast.error("칼럼 제목을 입력해주세요.");
68+
toast.error("칼럼 제목을 입력해 주세요.");
6969
return;
7070
}
7171

@@ -140,7 +140,7 @@ export default function Column({
140140
<TodoButton />
141141
</div>
142142

143-
{/* 무한스크롤 카드 리스트로 대체 */}
143+
{/* 카드 리스트 */}
144144
<div
145145
className="flex-1 w-full overflow-y-auto overflow-x-hidden"
146146
style={{ scrollbarGutter: "stable" }}
@@ -163,7 +163,7 @@ export default function Column({
163163
dashboardId={dashboardId}
164164
columnId={columnId}
165165
members={members}
166-
updateCard={fetchColumnsAndCards}
166+
onChangeCard={fetchColumnsAndCards}
167167
/>
168168
)}
169169

@@ -196,7 +196,7 @@ export default function Column({
196196
setIsCardDetailModalOpen(false);
197197
setSelectedCard(null);
198198
}}
199-
updateCard={fetchColumnsAndCards}
199+
onChangeCard={fetchColumnsAndCards}
200200
/>
201201
)}
202202
</div>

src/components/modal/ChangeBebridge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const ChangeBebridge = ({ onUpdate }: ChangeBebridgeProps) => {
7777
};
7878

7979
return (
80-
<div className="lg:w-[620px] lg:h-[344px] sm:w-[544px] sm:h-[344px] w-[284px] h-[312px] bg-white sm:rounded-[16px] rounded-[8px] p-[24px] flex flex-col">
80+
<div className="lg:w-[620px] lg:h-[344px] sm:w-[544px] sm:h-[344px] w-[284px] h-[312px] bg-white rounded-[12px] p-[24px] flex flex-col">
8181
<h2 className="text-black3 text-[20px] sm:text-[24px] font-bold">
8282
{dashboardDetail.title}
8383
</h2>

src/components/modal/InviteDashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export default function InviteDashboard({ onClose }: { onClose?: () => void }) {
109109
onChange={setEmail}
110110
label="이메일"
111111
labelClassName="text-lg sm:text-base text-black3 mt-6"
112-
placeholder="이메일을 입력해주세요"
112+
placeholder="이메일을 입력해 주세요"
113113
className="max-w-[620px] mb-1"
114114
/>
115115

src/components/modalDashboard/CardDetail.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export default function CardDetail({ card, columnName }: CardDetailProps) {
1616
{/* 담당자 정보 박스 */}
1717
<div className="absolute w-[181px] h-[155px] lg:[200px] top-20 right-10 rounded-lg p-3.5 bg-white border border-[#D9D9D9]">
1818
<div className="mb-3">
19-
<p className="text-sm font-semibold text-gray-800 mb-1">담당자</p>
19+
<p className="text-sm font-semibold text-black3 mb-1">담당자</p>
2020
<div className="flex items-center gap-2">
2121
<ProfileIcon
2222
userId={card.assignee.id}
@@ -26,16 +26,16 @@ export default function CardDetail({ card, columnName }: CardDetailProps) {
2626
imgClassName="w-6 h-6"
2727
fontClassName="text-sm"
2828
/>
29-
<span className="text-sm text-gray-700">
29+
<span className="text-sm text-black3">
3030
{card.assignee.nickname}
3131
</span>
3232
</div>
3333

3434
<div>
35-
<p className="text-sm font-semibold text-gray-800 mb-1 mt-3">
35+
<p className="text-sm font-semibold text-black3 mb-1 mt-3">
3636
마감일
3737
</p>
38-
<p className="text-sm text-gray-700">
38+
<p className="text-sm text-black3">
3939
{new Date(card.dueDate).toLocaleString("ko-KR", {
4040
year: "numeric",
4141
month: "2-digit",
@@ -70,7 +70,7 @@ export default function CardDetail({ card, columnName }: CardDetailProps) {
7070
{/* 설명 */}
7171
<p
7272
className="
73-
text-gray-700 p-2 overflow-auto
73+
text-black3 p-2 overflow-auto
7474
w-full max-w-[470px] md:max-w-[349px]
7575
whitespace-pre-wrap word-break break-words
7676
h-[70px]

src/components/modalDashboard/CardDetailModal.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface CardDetailModalProps {
1717
currentUserId: number;
1818
dashboardId: number;
1919
onClose: () => void;
20-
updateCard: () => void;
20+
onChangeCard?: () => void;
2121
}
2222

2323
interface ColumnType {
@@ -31,7 +31,7 @@ export default function CardDetailPage({
3131
currentUserId,
3232
dashboardId,
3333
onClose,
34-
updateCard,
34+
onChangeCard,
3535
}: CardDetailModalProps) {
3636
const [cardData, setCardData] = useState<CardDetailType>(card);
3737
const [commentText, setCommentText] = useState("");
@@ -65,9 +65,9 @@ export default function CardDetailPage({
6565
mutationFn: () => deleteCard(card.id),
6666
onSuccess: () => {
6767
queryClient.invalidateQueries({ queryKey: ["cards"] });
68-
toast.success("카드가 삭제되었습니다.");
68+
if (onChangeCard) onChangeCard();
6969
onClose();
70-
if (updateCard) updateCard();
70+
toast.success("카드가 삭제되었습니다.");
7171
},
7272
});
7373

@@ -182,17 +182,24 @@ export default function CardDetailPage({
182182
const matchedColumn = columns.find(
183183
(col) => col.title === data.status
184184
);
185-
await updateCardMutate({
186-
columnId: matchedColumn?.id,
187-
assignee: { ...cardData.assignee, nickname: data.assignee },
188-
title: data.title,
189-
description: data.description,
190-
dueDate: data.deadline,
191-
tags: data.tags,
192-
imageUrl: data.image || undefined,
193-
});
194-
setIsEditModalOpen(false);
195-
if (updateCard) updateCard();
185+
try {
186+
await updateCardMutate({
187+
columnId: matchedColumn?.id,
188+
assignee: { ...cardData.assignee, nickname: data.assignee },
189+
title: data.title,
190+
description: data.description,
191+
dueDate: data.deadline,
192+
tags: data.tags,
193+
imageUrl: data.image || undefined,
194+
});
195+
196+
if (onChangeCard) onChangeCard();
197+
onClose();
198+
toast.success("카드가 수정되었습니다.");
199+
} catch (err) {
200+
console.error("카드 수정 실패:", err);
201+
toast.error("카드 수정에 실패했습니다.");
202+
}
196203
}}
197204
initialData={{
198205
status: columnName,

src/components/modalInput/AssigneeSelect.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ export default function AssigneeSelect({
4343
return (
4444
<div className="inline-flex flex-col items-start gap-2.5 w-full max-w-[520px]">
4545
{label && (
46-
<p className="font-18m text-[var(--color-black)]">
46+
<p className="text-black3 font-medium text-[14px] sm:text-[18px]">
4747
{label}
48-
{required && <span className="text-[var(--color-purple)]">*</span>}
48+
{required && <span className="text-[var(--color-purple)]"> *</span>}
4949
</p>
5050
)}
5151

@@ -65,11 +65,13 @@ export default function AssigneeSelect({
6565
>
6666
{value.charAt(0).toUpperCase()}
6767
</span>
68-
<span className="font-18r">{value}</span>
68+
<span className="font-normal text-[14px] sm:text-[16px]">
69+
{value}
70+
</span>
6971
</>
7072
) : (
71-
<span className="font-18r text-[var(--color-gray2)]">
72-
이름을 입력해주세요
73+
<span className="font-normal text-[14px] sm:text-[16px] text-[var(--color-gray2)]">
74+
담당자를 선택해 주세요
7375
</span>
7476
)}
7577
</div>

0 commit comments

Comments
 (0)