Skip to content

Commit d19dfb9

Browse files
authored
Merge pull request #141 from part3-4team-Taskify/search01
[Feat Fix] 수정 모달 상태변경
2 parents fd68c38 + 313bb5d commit d19dfb9

File tree

4 files changed

+127
-37
lines changed

4 files changed

+127
-37
lines changed

src/api/statusOptions.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { useEffect, useState } from "react";
2+
import { useRouter } from "next/router";
3+
import axiosInstance from "./axiosInstance";
4+
import { apiRoutes } from "./apiRoutes";
5+
6+
export interface StatusOption {
7+
label: string;
8+
value: number;
9+
}
10+
11+
export const useStatusOptions = () => {
12+
const router = useRouter();
13+
const [statusOptions, setStatusOptions] = useState<StatusOption[]>([]);
14+
const [loading, setLoading] = useState(true);
15+
16+
useEffect(() => {
17+
const teamId = router.query.teamId as string;
18+
const dashboardId = Number(router.query.dashboardId);
19+
20+
if (!teamId || isNaN(dashboardId)) {
21+
console.warn("❗ teamId 또는 dashboardId가 유효하지 않습니다.");
22+
return;
23+
}
24+
25+
const fetch = async () => {
26+
try {
27+
const res = await axiosInstance.get(apiRoutes.columns(), {
28+
params: { dashboardId },
29+
});
30+
31+
const options = res.data.data.map((col: any) => ({
32+
label: col.title,
33+
value: col.id,
34+
}));
35+
36+
setStatusOptions(options);
37+
} catch (err) {
38+
console.error("상태 목록 가져오기 실패:", err);
39+
} finally {
40+
setLoading(false);
41+
}
42+
};
43+
44+
fetch();
45+
}, [router.query.teamId, router.query.dashboardId]);
46+
47+
return { statusOptions, loading };
48+
};

src/components/modalInput/StatusSelect.tsx

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1-
import { useState } from "react";
1+
import { useEffect, useState } from "react";
22
import clsx from "clsx";
33
import Image from "next/image";
4+
import { useRouter } from "next/router";
5+
import axiosInstance from "@/api/axiosInstance";
6+
import { TEAM_ID } from "@/constants/team"; // ✅ 전역 team ID 사용
7+
8+
export interface StatusOption {
9+
label: string;
10+
value: number;
11+
}
412

513
interface StatusSelectProps {
614
value: string;
@@ -9,21 +17,51 @@ interface StatusSelectProps {
917
required?: boolean;
1018
}
1119

12-
const STATUS_OPTIONS = [
13-
{ label: "To Do", color: "bg-[#9D8CFC]" },
14-
{ label: "On Progress", color: "bg-[#9D8CFC]" },
15-
{ label: "Done", color: "bg-[#9D8CFC]" },
16-
];
17-
1820
export default function StatusSelect({
1921
value,
2022
onChange,
2123
label,
2224
required,
2325
}: StatusSelectProps) {
2426
const [isOpen, setIsOpen] = useState(false);
27+
const [statusOptions, setStatusOptions] = useState<StatusOption[]>([]);
28+
29+
const router = useRouter();
30+
const dashboardId = Number(router.query.dashboardId);
31+
32+
const selectedStatus = statusOptions.find((opt) => opt.label === value);
33+
34+
useEffect(() => {
35+
console.log("📌 TEAM_ID:", TEAM_ID);
36+
console.log("📌 dashboardId:", dashboardId);
2537

26-
const selectedStatus = STATUS_OPTIONS.find((opt) => opt.label === value);
38+
if (!TEAM_ID || isNaN(dashboardId)) {
39+
console.warn("❌ TEAM_ID 또는 dashboardId가 유효하지 않습니다.");
40+
return;
41+
}
42+
43+
const loadOptions = async () => {
44+
try {
45+
// ✅ 경로에 teamId 직접 넣기 (문자열 리터럴)
46+
const res = await axiosInstance.get(`/${TEAM_ID}/columns`, {
47+
params: { dashboardId },
48+
});
49+
50+
console.log("✅ 상태 목록 응답:", res.data);
51+
52+
const options = res.data.data.map((col: any) => ({
53+
label: col.title,
54+
value: col.id,
55+
}));
56+
57+
setStatusOptions(options);
58+
} catch (err) {
59+
console.error("❌ 상태 목록 불러오기 실패:", err);
60+
}
61+
};
62+
63+
loadOptions();
64+
}, [dashboardId]);
2765

2866
return (
2967
<div className="inline-flex flex-col items-start gap-2.5 w-full max-w-[520px]">
@@ -35,25 +73,22 @@ export default function StatusSelect({
3573
)}
3674

3775
<div className="relative w-full">
38-
{/* 현재 선택된 값 */}
3976
<div
4077
className="flex items-center justify-between h-[48px] px-4 border border-[var(--color-gray3)] rounded-md cursor-pointer focus-within:border-[var(--primary)]"
4178
onClick={() => setIsOpen(!isOpen)}
4279
>
43-
<div className="flex items-center gap-2">
44-
{selectedStatus ? (
45-
<>
46-
<span
47-
className={clsx("w-2 h-2 rounded-full", selectedStatus.color)}
48-
></span>
49-
<span className="font-18r">{selectedStatus.label}</span>
50-
</>
51-
) : (
52-
<span className="font-18r text-[var(--color-gray2)]">
53-
상태를 선택해주세요
80+
{selectedStatus ? (
81+
<div className="flex items-center gap-2 bg-[#F3EDFF] rounded-full px-3 py-1">
82+
<span className="w-2 h-2 rounded-full bg-[#5D2EFF]" />
83+
<span className="text-sm text-[#5D2EFF]">
84+
{selectedStatus.label}
5485
</span>
55-
)}
56-
</div>
86+
</div>
87+
) : (
88+
<span className="text-sm text-[var(--color-gray2)]">
89+
상태를 선택해주세요
90+
</span>
91+
)}
5792
<Image
5893
src="/svgs/arrow-down.svg"
5994
width={20}
@@ -62,26 +97,26 @@ export default function StatusSelect({
6297
/>
6398
</div>
6499

65-
{/* 드롭다운 리스트 */}
66100
{isOpen && (
67-
<ul className="absolute top-full left-0 mt-1 w-full bg-white border border-[var(--color-gray3)] rounded-md shadow-lg z-10">
68-
{STATUS_OPTIONS.map((status) => (
101+
<ul className="absolute top-full left-0 mt-1 w-full bg-white border border-gray-200 rounded-md shadow-lg z-10 px-2 py-2">
102+
{statusOptions.map((status) => (
69103
<li
70-
key={status.label}
104+
key={status.value}
71105
onClick={() => {
72106
onChange(status.label);
73107
setIsOpen(false);
74108
}}
75-
className="px-4 py-2 cursor-pointer hover:bg-[var(--color-gray1)] flex items-center justify-between"
109+
className={clsx(
110+
"flex items-center justify-between cursor-pointer mb-1 last:mb-0 rounded-full px-3 py-1 hover:bg-[#F3EDFF]",
111+
value === status.label && "bg-[#F3EDFF]"
112+
)}
76113
>
77114
<div className="flex items-center gap-2">
78-
<span
79-
className={clsx("w-2 h-2 rounded-full", status.color)}
80-
></span>
81-
<span className="text-sm">{status.label}</span>
115+
<span className="w-2 h-2 rounded-full bg-[#5D2EFF]" />
116+
<span className="text-sm text-[#5D2EFF]">{status.label}</span>
82117
</div>
83118
{value === status.label && (
84-
<span className="text-[var(--primary)]"></span>
119+
<span className="text-[#5D2EFF] text-sm"></span>
85120
)}
86121
</li>
87122
))}

src/components/modalInput/TaskModal.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ interface TaskModalProps {
1111
onClose: () => void;
1212
onSubmit: (data: TaskData) => void;
1313
initialData?: Partial<TaskData>;
14-
members: { nickname: string }[]; // ✅ 담당자 목록 받기
14+
members: { nickname: string }[];
15+
// ✅ teamId, dashboardId 제거됨
1516
}
1617

1718
export interface TaskData {
@@ -21,7 +22,7 @@ export interface TaskData {
2122
description: string;
2223
deadline: string;
2324
tags: string[];
24-
image?: string; // ✅ 이미지 URL
25+
image?: string;
2526
}
2627

2728
export default function TaskModal({
@@ -75,7 +76,7 @@ export default function TaskModal({
7576
<AssigneeSelect
7677
label="담당자"
7778
value={formData.assignee}
78-
users={members.map((m) => m.nickname)} // ✅ 드롭다운용
79+
users={members.map((m) => m.nickname)}
7980
required
8081
onChange={(value) => handleChange("assignee", value)}
8182
/>

src/components/sideMenu/SideMenu.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,10 @@ export default function SideMenu({
134134
<span className="hidden md:block font-12sb text-[var(--color-black)]">
135135
Dash Boards
136136
</span>
137-
<button className="ml-auto" onClick={() => setIsModalOpen(true)}>
137+
<button
138+
className="ml-auto cursor-pointer"
139+
onClick={() => setIsModalOpen(true)}
140+
>
138141
<Image
139142
src="/svgs/icon-add-box.svg"
140143
width={20}
@@ -149,7 +152,10 @@ export default function SideMenu({
149152
{/* ✅ 접힌 상태에서 아이콘만 있는 추가 버튼 로고 아래로 */}
150153
{isCollapsed && (
151154
<div className="flex justify-center mb-4">
152-
<button onClick={() => setIsModalOpen(true)}>
155+
<button
156+
onClick={() => setIsModalOpen(true)}
157+
className="cursor-pointer"
158+
>
153159
<Image
154160
src="/svgs/icon-add-box.svg"
155161
width={20}

0 commit comments

Comments
 (0)