Skip to content

Commit fefadde

Browse files
authored
Merge pull request #214 from part3-4team-Taskify/minji
[Refactor, Style] TaskModal: 마감일 필수 설정 제거 / Card: 카드&댓글 삭제 확인 모달 추가, 제목&내용 공백 검사
2 parents d560779 + 250e253 commit fefadde

File tree

12 files changed

+247
-135
lines changed

12 files changed

+247
-135
lines changed

src/api/card.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export const createCard = async ({
5454
columnId: number;
5555
title: string;
5656
description: string;
57-
dueDate: string;
57+
dueDate?: string;
5858
tags: string[];
5959
imageUrl?: string;
6060
}) => {

src/components/button/PaginationButton.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ export const PaginationButton: React.FC<PaginationButtonProps> = ({
1212
...props
1313
}) => {
1414
const baseStyle =
15-
"w-[40px] h-[40px] flex justify-center items-center border border-[var(--color-gray3)] rounded-md text-[16px] font-medium transition";
15+
"w-[40px] h-[40px] flex justify-center items-center bg-white border border-[var(--color-gray3)] rounded-md text-[16px] font-medium transition";
1616

1717
const enabledTextColor =
1818
"text-[var(--color-gray1)] bg-white hover:bg-[var(--color-gray5)] cursor-pointer";
19-
const disabledTextColor = "text-[var(--color-gray3)] cursor-default";
19+
const disabledTextColor =
20+
"bg-[var(--color-gray4)] text-[var(--color-gray3)] cursor-default";
2021
const finalStyle = `${baseStyle} ${disabled ? disabledTextColor : enabledTextColor}`;
2122

2223
return (

src/components/columnCard/Card.tsx

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ export default function Card({
2121
<div
2222
onClick={onClick}
2323
className={`
24+
relative
2425
flex flex-col md:flex-row lg:flex-col cursor-pointer
25-
items-start rounded-md bg-white border border-gray-200 p-4
26-
w-[284px] sm:w-full md:w-[544px] md:h-[93px] lg:w-[314px] lg:h-auto
26+
sm:items-center items-start
27+
rounded-md bg-white border border-gray-200 p-4
28+
w-[284px] md:w-[510px] md:h-[93px] lg:w-[314px] lg:h-auto
2729
`}
2830
>
2931
{/* 이미지 영역 */}
@@ -48,7 +50,7 @@ export default function Card({
4850
</div>
4951
)}
5052

51-
<div className="flex flex-col justify-between flex-1 w-full">
53+
<div className="flex flex-col justify-between w-full">
5254
{/* 제목 */}
5355
<h3
5456
className={`
@@ -64,7 +66,7 @@ export default function Card({
6466
<div
6567
className={`
6668
flex flex-col gap-2 mt-2 whitespace-nowrap
67-
sm:max-w-none max-w-[192px]
69+
sm:max-w-none max-w-[190px]
6870
md:flex-row md:items-center md:justify-between md:mt-1
6971
lg:flex-col lg:items-start lg:mt-2
7072
text-sm md:text-xs
@@ -85,37 +87,43 @@ export default function Card({
8587
})}
8688
</div>
8789

88-
{/* 날짜 + 닉네임 */}
89-
<div className="flex items-center gap-[29.5px] md:gap-3 sm:pr-4 text-[var(--color-gray1)]">
90+
{/* 마감일 */}
91+
<div
92+
className="flex items-center justify-between
93+
lg:gap-37 gap-8 sm:pr-4 text-[var(--color-gray1)]"
94+
>
9095
<div className="flex items-center gap-1">
9196
<Image
9297
src="/svgs/calendar.svg"
9398
alt="calendar"
9499
width={16}
95100
height={16}
96101
/>
97-
<span>{dueDate}</span>
102+
<span>{dueDate ?? "마감일 없음"}</span>
98103
</div>
99-
{assignee.profileImageUrl ? (
100-
<Image
101-
src={assignee.profileImageUrl}
102-
alt="프로필 이미지"
103-
width={22}
104-
height={22}
105-
className="sm:w-[24px] sm:h-[24px] rounded-full object-cover"
106-
/>
107-
) : (
108-
<div
109-
className="sm:w-[24px] sm:h-[24px] w-[22px] h-[22px] rounded-full
110-
overflow-hidden flex items-center justify-center"
111-
>
112-
<RandomProfile
113-
userId={assignee.id}
114-
name={assignee.nickname}
115-
className="sm:w-[24px] sm:h-[24px] w-[22px] h-[22px]"
104+
{/* 프로필 아이콘 */}
105+
<div className="shrink-0">
106+
{assignee.profileImageUrl ? (
107+
<Image
108+
src={assignee.profileImageUrl}
109+
alt="프로필 이미지"
110+
width={22}
111+
height={22}
112+
className="sm:w-[24px] sm:h-[24px] rounded-full object-cover shrink-0"
116113
/>
117-
</div>
118-
)}
114+
) : (
115+
<div
116+
className="sm:w-[24px] sm:h-[24px] w-[22px] h-[22px] rounded-full
117+
flex items-center justify-center"
118+
>
119+
<RandomProfile
120+
userId={assignee.id}
121+
name={assignee.nickname}
122+
className="sm:w-[24px] sm:h-[24px] w-[22px] h-[22px]"
123+
/>
124+
</div>
125+
)}
126+
</div>
119127
</div>
120128
</div>
121129
</div>

src/components/columnCard/Column.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export default function Column({
4949
{ id: number; userId: number; nickname: string }[]
5050
>([]);
5151

52-
const maxColumnTitleLength = 20;
52+
const maxColumnTitleLength = 15;
5353

5454
// 카드리스트의 스크롤을 칼럼 영역으로 이동
5555
const scrollRef = useRef<HTMLDivElement | null>(null);
@@ -130,7 +130,10 @@ export default function Column({
130130
{/* 왼쪽: 타이틀 + 카드 개수 */}
131131
<div className="flex items-center gap-2">
132132
<Image src="/svgs/ellipse.svg" alt="circle" width={8} height={8} />
133-
<h2 className="text-black3 text-[16px] md:text-[18px] font-bold">
133+
<h2
134+
className="text-black3 text-[16px] md:text-[18px] font-bold
135+
break-words sm:max-w-none max-w-[90px]"
136+
>
134137
{columnTitle}
135138
</h2>
136139
<span
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import React from "react";
2+
import { Modal } from "@/components/modal/Modal";
3+
import { CustomBtn } from "../button/CustomButton";
4+
import { toast } from "react-toastify";
5+
6+
interface DeleteModalProps {
7+
isDeleteModalOpen: boolean;
8+
setIsDeleteModalOpen: (open: boolean) => void;
9+
isConfirmDeleteModalOpen: boolean;
10+
setIsConfirmDeleteModalOpen: (open: boolean) => void;
11+
selectedTitle: string | null;
12+
selectedCreatedByMe: boolean | null;
13+
handleDelete: () => void;
14+
handleLeave: () => void;
15+
}
16+
17+
export const DeleteCheckModal: React.FC<DeleteModalProps> = ({
18+
isDeleteModalOpen,
19+
setIsDeleteModalOpen,
20+
isConfirmDeleteModalOpen,
21+
setIsConfirmDeleteModalOpen,
22+
selectedTitle,
23+
selectedCreatedByMe,
24+
handleDelete,
25+
handleLeave,
26+
}) => {
27+
return (
28+
<>
29+
<Modal
30+
width="sm:w-[320px] w-[260px]"
31+
height="sm:h-[190px] h-[160px]"
32+
isOpen={isDeleteModalOpen}
33+
onClose={() => setIsDeleteModalOpen(false)}
34+
className="flex items-center justify-center text-center"
35+
>
36+
<div className="flex flex-col items-center gap-1 text-center min-h-[60px]">
37+
<div className="text-[var(--primary)] font-medium sm:text-[18px] text-[16px]">
38+
{selectedTitle}
39+
</div>
40+
<div className="text-black3 font-medium sm:text-[18px] text-[16px]">
41+
{selectedCreatedByMe
42+
? "대시보드를 삭제하시겠습니까?"
43+
: "대시보드에서 나가시겠습니까?"}
44+
</div>
45+
</div>
46+
47+
<div className="flex items-center justify-center gap-2">
48+
<CustomBtn
49+
onClick={() => setIsDeleteModalOpen(false)}
50+
className="cursor-pointer border px-3 py-1 rounded-md
51+
sm:w-[100px] sm:h-[40px] w-[84px] h-[32px]
52+
text-[var(--primary)] border-[var(--color-gray3)]"
53+
>
54+
취소
55+
</CustomBtn>
56+
<CustomBtn
57+
onClick={() => {
58+
if (selectedCreatedByMe) {
59+
setIsDeleteModalOpen(false);
60+
setIsConfirmDeleteModalOpen(true); // 재확인 모달 오픈
61+
} else {
62+
handleLeave(); // 탈퇴일 때는 바로 닫힘
63+
toast.error("현재 탈퇴 기능이 준비 중입니다.");
64+
}
65+
}}
66+
className="cursor-pointer bg-[var(--primary)] px-3 py-1 rounded-md
67+
sm:w-[100px] sm:h-[40px] w-[84px] h-[32px]
68+
text-white"
69+
>
70+
확인
71+
</CustomBtn>
72+
</div>
73+
</Modal>
74+
75+
<Modal
76+
width="sm:w-[320px] w-[260px]"
77+
height="sm:h-[190px] h-[160px]"
78+
isOpen={isConfirmDeleteModalOpen}
79+
onClose={() => setIsConfirmDeleteModalOpen(false)}
80+
className="flex items-center justify-center text-center"
81+
>
82+
<div className="flex flex-col items-center gap-1 text-center min-h-[60px]">
83+
<div className="text-red-400 font-medium sm:text-[18px] text-[16px]">
84+
삭제 시 복구할 수 없습니다.
85+
</div>
86+
<div className="text-black3 font-medium sm:text-[18px] text-[16px]">
87+
정말 삭제하시겠습니까?
88+
</div>
89+
</div>
90+
91+
<div className="flex items-center justify-center gap-2">
92+
<CustomBtn
93+
onClick={() => setIsConfirmDeleteModalOpen(false)}
94+
className="cursor-pointer border px-3 py-1 rounded-md
95+
sm:w-[100px] sm:h-[40px] w-[84px] h-[32px]
96+
text-[var(--primary)] border-[var(--color-gray3)]"
97+
>
98+
취소
99+
</CustomBtn>
100+
<CustomBtn
101+
onClick={() => {
102+
setIsConfirmDeleteModalOpen(false);
103+
handleDelete(); // 진짜 삭제 실행
104+
toast.success("대시보드가 삭제되었습니다.");
105+
}}
106+
className="cursor-pointer bg-[var(--primary)] px-3 py-1 rounded-md
107+
sm:w-[100px] sm:h-[40px] w-[84px] h-[32px]
108+
text-white"
109+
>
110+
확인
111+
</CustomBtn>
112+
</div>
113+
</Modal>
114+
</>
115+
);
116+
};

src/components/modal/DeleteModal.tsx

Lines changed: 23 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,105 +4,50 @@ import { CustomBtn } from "../button/CustomButton";
44
import { toast } from "react-toastify";
55

66
interface DeleteModalProps {
7-
isDeleteModalOpen: boolean;
8-
setIsDeleteModalOpen: (open: boolean) => void;
9-
isConfirmDeleteModalOpen: boolean;
10-
setIsConfirmDeleteModalOpen: (open: boolean) => void;
11-
selectedTitle: string | null;
12-
selectedCreatedByMe: boolean | null;
13-
handleDelete: () => void;
14-
handleLeave: () => void;
7+
isOpen: boolean;
8+
title?: string;
9+
description?: string;
10+
onConfirm: () => void;
11+
onCancel: () => void;
1512
}
1613

1714
export const DeleteModal: React.FC<DeleteModalProps> = ({
18-
isDeleteModalOpen,
19-
setIsDeleteModalOpen,
20-
isConfirmDeleteModalOpen,
21-
setIsConfirmDeleteModalOpen,
22-
selectedTitle,
23-
selectedCreatedByMe,
24-
handleDelete,
25-
handleLeave,
15+
isOpen,
16+
title = "이 항목",
17+
description,
18+
onConfirm,
19+
onCancel,
2620
}) => {
2721
return (
2822
<>
2923
<Modal
3024
width="sm:w-[320px] w-[260px]"
3125
height="sm:h-[190px] h-[160px]"
32-
isOpen={isDeleteModalOpen}
33-
onClose={() => setIsDeleteModalOpen(false)}
26+
isOpen={isOpen}
27+
onClose={onCancel}
3428
className="flex items-center justify-center text-center"
3529
>
36-
<div className="flex flex-col items-center gap-1 text-center min-h-[60px]">
37-
<div className="text-[var(--primary)] font-medium sm:text-[18px] text-[16px]">
38-
{selectedTitle}
39-
</div>
40-
<div className="text-black3 font-medium sm:text-[18px] text-[16px]">
41-
{selectedCreatedByMe
42-
? "대시보드를 삭제하시겠습니까?"
43-
: "대시보드에서 나가시겠습니까?"}
44-
</div>
30+
<div className="flex flex-col items-center gap-2 text-center sm:min-h-[40px] min-h-[30px]">
31+
<span className="text-black3 font-medium sm:text-[18px] text-[16px]">
32+
{title} 삭제하시겠습니까?
33+
</span>
34+
{description && (
35+
<span className="text-black3 font-medium text-[16px]">
36+
{description}
37+
</span>
38+
)}
4539
</div>
46-
47-
<div className="flex items-center justify-center gap-2">
48-
<CustomBtn
49-
onClick={() => setIsDeleteModalOpen(false)}
50-
className="cursor-pointer border px-3 py-1 rounded-md
51-
sm:w-[100px] sm:h-[40px] w-[84px] h-[32px]
52-
text-[var(--primary)] border-[var(--color-gray3)]"
53-
>
54-
취소
55-
</CustomBtn>
56-
<CustomBtn
57-
onClick={() => {
58-
if (selectedCreatedByMe) {
59-
setIsDeleteModalOpen(false);
60-
setIsConfirmDeleteModalOpen(true); // 재확인 모달 오픈
61-
} else {
62-
handleLeave(); // 탈퇴일 때는 바로 닫힘
63-
toast.error("현재 탈퇴 기능이 준비 중입니다.");
64-
}
65-
}}
66-
className="cursor-pointer bg-[var(--primary)] px-3 py-1 rounded-md
67-
sm:w-[100px] sm:h-[40px] w-[84px] h-[32px]
68-
text-white"
69-
>
70-
확인
71-
</CustomBtn>
72-
</div>
73-
</Modal>
74-
75-
<Modal
76-
width="sm:w-[320px] w-[260px]"
77-
height="sm:h-[190px] h-[160px]"
78-
isOpen={isConfirmDeleteModalOpen}
79-
onClose={() => setIsConfirmDeleteModalOpen(false)}
80-
className="flex items-center justify-center text-center"
81-
>
82-
<div className="flex flex-col items-center gap-1 text-center min-h-[60px]">
83-
<div className="text-red-400 font-medium sm:text-[18px] text-[16px]">
84-
삭제 시 복구할 수 없습니다.
85-
</div>
86-
<div className="text-black3 font-medium sm:text-[18px] text-[16px]">
87-
정말 삭제하시겠습니까?
88-
</div>
89-
</div>
90-
9140
<div className="flex items-center justify-center gap-2">
9241
<CustomBtn
93-
onClick={() => setIsConfirmDeleteModalOpen(false)}
42+
onClick={onCancel}
9443
className="cursor-pointer border px-3 py-1 rounded-md
9544
sm:w-[100px] sm:h-[40px] w-[84px] h-[32px]
9645
text-[var(--primary)] border-[var(--color-gray3)]"
9746
>
9847
취소
9948
</CustomBtn>
10049
<CustomBtn
101-
onClick={() => {
102-
setIsConfirmDeleteModalOpen(false);
103-
handleDelete(); // 진짜 삭제 실행
104-
toast.success("대시보드가 삭제되었습니다.");
105-
}}
50+
onClick={onConfirm}
10651
className="cursor-pointer bg-[var(--primary)] px-3 py-1 rounded-md
10752
sm:w-[100px] sm:h-[40px] w-[84px] h-[32px]
10853
text-white"

0 commit comments

Comments
 (0)