Skip to content

Commit 396db15

Browse files
authored
Merge pull request #218 from part3-4team-Taskify/minji
[Fix, Refactor, Style] 전체: post 요청 중복 방지 / CommentList: 카드 담당자=댓글 작성자일 때만 수정/삭제되던 버그 fix
2 parents 4f980c4 + d6c0767 commit 396db15

File tree

14 files changed

+153
-98
lines changed

14 files changed

+153
-98
lines changed

src/api/members.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,9 @@ export const getMembers = async ({
3232
});
3333

3434
const members: Member[] = response.data.members || [];
35-
36-
console.log("✅ getMembers 응답 데이터:", members); // 디버깅용 로그
3735
return members;
3836
} catch (error) {
39-
console.error("🚨 getMembers API 실패:", error);
37+
console.error("getMembers API 실패:", error);
4038
return [];
4139
}
4240
};

src/components/modal/DeleteDashboardModal.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ export default function DeleteDashboardModal({
3838
height="h-[160px] sm:h-[174px]"
3939
>
4040
<div className="flex flex-col sm:gap-10 gap-6 text-center">
41-
<p className="text-xl mt-1.5">대시보드를 삭제하시겠습니까?</p>
41+
<p className="text-black3 font-medium sm:text-[20px] text-[18px] mt-1.5">
42+
대시보드를 삭제하시겠습니까?
43+
</p>
4244
<div className="flex justify-between gap-3">
4345
<CustomBtn variant="outlineDisabled" onClick={onClose}>
4446
취소

src/components/modal/DeleteModal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from "react";
22
import { Modal } from "@/components/modal/Modal";
33
import { CustomBtn } from "../button/CustomButton";
4-
import { toast } from "react-toastify";
54

65
interface DeleteModalProps {
76
isOpen: boolean;

src/components/modal/NewDashboard.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState } from "react";
2+
import { usePostGuard } from "@/hooks/usePostGuard";
23
import Input from "../input/Input";
34
import Image from "next/image";
45
import { createDashboard } from "@/api/dashboards";
@@ -23,9 +24,10 @@ interface NewDashboardProps {
2324
export default function NewDashboard({ onClose, onCreate }: NewDashboardProps) {
2425
const [title, setTitle] = useState("");
2526
const [selected, setSelected] = useState<number | null>(null);
26-
const [loading, setLoading] = useState(false);
2727
const [titleLength, setTitleLength] = useState<number>(0);
2828

29+
const { guard: postGuard, isLoading } = usePostGuard();
30+
2931
const maxTitleLength = 30;
3032
const colors = ["#7ac555", "#760DDE", "#FF9800", "#76A5EA", "#E876EA"];
3133

@@ -44,16 +46,15 @@ export default function NewDashboard({ onClose, onCreate }: NewDashboardProps) {
4446
};
4547

4648
try {
47-
setLoading(true);
48-
const response = await createDashboard(payload);
49-
onCreate?.(response.data);
50-
onClose?.();
51-
toast.success("대시보드가 생성되었습니다.");
49+
await postGuard(async () => {
50+
const response = await createDashboard(payload);
51+
onCreate?.(response.data);
52+
onClose?.();
53+
toast.success("대시보드가 생성되었습니다.");
54+
});
5255
} catch (error) {
5356
console.error("대시보드 생성 실패:", error);
5457
toast.error("대시보드 생성에 실패했습니다.");
55-
} finally {
56-
setLoading(false);
5758
}
5859
};
5960

@@ -116,12 +117,12 @@ export default function NewDashboard({ onClose, onCreate }: NewDashboardProps) {
116117
</button>
117118
<button
118119
onClick={handleSubmit}
119-
disabled={!title || selected === null}
120+
disabled={!title || selected === null || isLoading}
120121
className={`cursor-pointer sm:w-[256px] sm:h-[54px] w-[144px] h-[54px] rounded-[8px]
121122
border border-[var(--color-gray3)] text-[var(--color-white)]
122123
${!title || selected === null ? "bg-gray-300 cursor-not-allowed" : "bg-[var(--primary)]"}`}
123124
>
124-
{loading ? "생성 중..." : "생성"}
125+
{isLoading ? "생성 중..." : "생성"}
125126
</button>
126127
</div>
127128
</div>

src/components/modalDashboard/CardDetailModal.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { Representative } from "@/components/modalDashboard/Representative";
77
import TaskModal from "@/components/modalInput/TaskModal";
88
import { DeleteModal } from "@/components/modal/DeleteModal";
99
import { toast } from "react-toastify";
10-
import { TEAM_ID } from "@/constants/team";
1110
import { useDashboardPermission } from "@/hooks/useDashboardPermission";
1211
import { useCardDetailState } from "@/hooks/useCardDetailState";
1312
import { useCardDetail } from "@/hooks/useCardDetail";
@@ -16,7 +15,6 @@ import type { CardDetailType } from "@/types/cards";
1615

1716
interface CardDetailModalProps {
1817
card: CardDetailType;
19-
currentUserId: number;
2018
dashboardId: number;
2119
createdByMe: boolean;
2220
onClose: () => void;
@@ -25,15 +23,16 @@ interface CardDetailModalProps {
2523

2624
export default function CardDetailPage({
2725
card,
28-
currentUserId,
2926
dashboardId,
3027
createdByMe,
3128
onClose,
3229
onChangeCard,
3330
}: CardDetailModalProps) {
3431
const { canEditCards } = useDashboardPermission(dashboardId, createdByMe);
35-
const { cardData, setCardData, columnName, columns, members } =
36-
useCardDetailState(card, dashboardId);
32+
const { cardData, setCardData, columnName, members } = useCardDetailState(
33+
card,
34+
dashboardId
35+
);
3736

3837
const {
3938
commentText,
@@ -142,11 +141,7 @@ export default function CardDetailPage({
142141
</div>
143142

144143
<div className="w-full lg:max-w-[445px] md:max-w-[420px] sm:max-h-[140px] max-h-[70px] my-2 overflow-y-auto">
145-
<CommentList
146-
cardId={cardData.id}
147-
currentUserId={currentUserId}
148-
teamId={TEAM_ID}
149-
/>
144+
<CommentList cardId={cardData.id} />
150145
</div>
151146
</div>
152147
</div>
@@ -168,6 +163,7 @@ export default function CardDetailPage({
168163
setCardData(updatedCard);
169164
onChangeCard?.();
170165
} catch (error) {
166+
console.error("카드 불러오기 실패:", error);
171167
toast.error("카드 정보를 불러오는 데 실패했습니다.");
172168
} finally {
173169
setIsEditModalOpen(false);

src/components/modalDashboard/CommentList.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
import { useEffect } from "react";
22
import { useInView } from "react-intersection-observer";
33
import { useInfiniteQuery } from "@tanstack/react-query";
4+
import useUserStore from "@/store/useUserStore";
45
import { getComments } from "@/api/comment";
56
import type { Comment as CommentType } from "@/types/comments";
67
import UpdateComment from "./UpdateComment";
8+
import { TEAM_ID } from "@/constants/team";
79

810
interface CommentListProps {
911
cardId: number;
10-
currentUserId: number;
11-
teamId: string;
1212
}
1313

14-
export default function CommentList({
15-
cardId,
16-
currentUserId,
17-
}: CommentListProps) {
14+
export default function CommentList({ cardId }: CommentListProps) {
1815
const { ref, inView } = useInView();
16+
const { user } = useUserStore();
1917

2018
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
2119
useInfiniteQuery({
@@ -36,6 +34,8 @@ export default function CommentList({
3634
const allComments: CommentType[] =
3735
data?.pages.flatMap((page) => page.comments) ?? [];
3836

37+
if (!user) return null;
38+
3939
return (
4040
<div
4141
className="min-h-[80px] sm:max-h-[240px] max-h-[215px] w-full
@@ -54,8 +54,8 @@ export default function CommentList({
5454
<div key={comment.id} className=" shrink-0 py-2 last:border-b-0">
5555
<UpdateComment
5656
comment={comment}
57-
currentUserId={currentUserId}
58-
teamId={""}
57+
currentUserId={user.id}
58+
teamId={TEAM_ID}
5959
/>
6060
</div>
6161
))

src/components/modalDashboard/Representative.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ interface RepresentativeProps {
88
export const Representative = ({ card }: RepresentativeProps) => {
99
return (
1010
<div
11-
className="flex flex-col justify-center gap-4 sm:pl-4 pl-3
11+
className="flex flex-col justify-center gap-4 sm:pl-4
1212
lg:w-[200px] sm:w-[180px] w-[290px]
1313
sm:h-[155px] h-[65px]
1414
rounded-lg bg-white border border-[#D9D9D9]"
1515
>
1616
{/* 내부 아이템 컨테이너 */}
17-
<div className="flex sm:flex-col sm:gap-4 gap-17">
17+
<div
18+
className="flex items-center justify-center
19+
sm:items-start sm:justify-between
20+
sm:flex-col sm:gap-4 gap-18"
21+
>
1822
{/* 담당자 컨테이너 */}
1923
<div>
2024
<p className="font-12sb text-black3 mb-[4px]">담당자</p>
@@ -36,18 +40,20 @@ export const Representative = ({ card }: RepresentativeProps) => {
3640

3741
{/* 마감일 컨테이너 */}
3842
<div>
39-
<p className="font-12sb text-black3 sm:mb-1 mb-[7px]">마감일</p>
40-
<p className="font-normal text-black3 sm:text-[14px] text-[12px]">
41-
{card.dueDate
42-
? new Date(card.dueDate).toLocaleString("ko-KR", {
43-
year: "numeric",
44-
month: "2-digit",
45-
day: "2-digit",
46-
hour: "2-digit",
47-
minute: "2-digit",
48-
})
49-
: "마감일 없음"}
50-
</p>
43+
<p className="font-12sb text-black3 mb-[4px]">마감일</p>
44+
<div className="flex items-center w-full h-6">
45+
<p className="font-normal text-black3 sm:text-[14px] text-[12px]">
46+
{card.dueDate
47+
? new Date(card.dueDate).toLocaleString("ko-KR", {
48+
year: "numeric",
49+
month: "2-digit",
50+
day: "2-digit",
51+
hour: "2-digit",
52+
minute: "2-digit",
53+
})
54+
: "마감일 없음"}
55+
</p>
56+
</div>
5157
</div>
5258
</div>
5359
</div>

src/components/modalInput/ModalInput.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export default function ModalInput({
3939
const [titleLength, setTitleLength] = useState<number>(0);
4040
const maxTitleLength = 50;
4141

42+
// 기존 제목이 있다면 글자수 업데이트
43+
useEffect(() => {
44+
if (label === "제목" && defaultValue) {
45+
setTitleLength(defaultValue.length);
46+
}
47+
}, [label, defaultValue]);
48+
4249
useEffect(() => {
4350
if (label === "태그" && defaultValueArray.length > 0) {
4451
const initialTags = defaultValueArray.map((text, index) => ({

src/components/modalInput/TaskModal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ export default function TaskModal({
6161
onClose,
6262
onSubmit,
6363
});
64-
console.log("🔍 members in TaskModal", members);
6564
return (
6665
<div className="fixed inset-0 flex items-center justify-center bg-black/35 z-50">
6766
<div className="sm:w-[584px] w-[327px] h-[calc(var(--vh)_*_90)] rounded-lg bg-white p-4 sm:p-8 shadow-lg flex flex-col gap-4 sm:gap-8 overflow-y-auto">

src/hooks/usePostGuard.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useRef, useState } from "react";
2+
3+
export function usePostGuard() {
4+
const isPosting = useRef(false);
5+
const [isLoading, setIsLoading] = useState(false);
6+
7+
const guard = async <T>(callback: () => Promise<T>): Promise<T | null> => {
8+
if (isPosting.current) return null;
9+
10+
isPosting.current = true;
11+
setIsLoading(true);
12+
13+
try {
14+
return await callback();
15+
} catch (err) {
16+
throw err;
17+
} finally {
18+
isPosting.current = false;
19+
setIsLoading(false);
20+
}
21+
};
22+
23+
return { guard, isLoading };
24+
}

0 commit comments

Comments
 (0)