Skip to content

Commit 3e06dd8

Browse files
committed
feat: 계획 관리 페이지 플랜 수정 기능 구현
1 parent 14ffc88 commit 3e06dd8

3 files changed

Lines changed: 112 additions & 14 deletions

File tree

app/(with-sidebar)/plans/page.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
deletePlanItem,
1010
fetchPlans,
1111
Plan,
12+
updatePlan,
1213
} from '@/services/plans/planManageService.service';
1314

1415
import PageHeader from '@/components/common/PageHeader';
@@ -94,6 +95,28 @@ const Page = () => {
9495
}
9596
};
9697

98+
// 플랜 수정 핸들러
99+
const handleUpdatePlan = async (
100+
planId: string,
101+
title: string,
102+
description: string
103+
) => {
104+
if (!user) {
105+
alert('로그인이 필요합니다.');
106+
return;
107+
}
108+
109+
try {
110+
await updatePlan(user.uid, planId, { title, description });
111+
112+
// 목록 새로고침
113+
const fetchedPlans = await fetchPlans(user.uid);
114+
setPlans(fetchedPlans);
115+
} catch (err) {
116+
console.error(err);
117+
}
118+
};
119+
97120
return (
98121
<div className="bg-background min-h-screen p-11">
99122
<PageHeader
@@ -167,6 +190,7 @@ const Page = () => {
167190
title={plan.title}
168191
description={plan.description}
169192
onDeletePlan={handleDeletePlan}
193+
onUpdatePlan={handleUpdatePlan}
170194
/>
171195
))
172196
) : (

components/plans/PlanSection.tsx

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import { useState, useEffect, useRef } from 'react';
44
import {
5+
Check,
56
ChevronDown,
67
ChevronUp,
78
Edit2,
89
MoreVertical,
910
Plus,
1011
Trash2,
12+
X,
1113
} from 'lucide-react';
1214
import Card from '../home/Card';
1315
import TaskItem from './TaskItem';
@@ -26,6 +28,11 @@ interface PlanSectionProps {
2628
title: string;
2729
description?: string;
2830
onDeletePlan: (planId: string, title: string) => void;
31+
onUpdatePlan: (
32+
planId: string,
33+
newTitle: string,
34+
newDescription: string
35+
) => void;
2936
}
3037

3138
export default function PlanSection({
@@ -34,14 +41,18 @@ export default function PlanSection({
3441
title,
3542
description,
3643
onDeletePlan,
44+
onUpdatePlan,
3745
}: PlanSectionProps) {
3846
const [isOpen, setIsOpen] = useState(true);
3947
const [tasks, setTasks] = useState<PlanItem[]>([]);
4048
const [isTasksLoading, setIsTasksLoading] = useState(true);
4149
const [isAddingTask, setIsAddingTask] = useState(false);
4250

43-
const [showPlanMenu, setShowPlanMenu] = useState(false);
51+
const [isEditingPlan, setIsEditingPlan] = useState(false);
52+
const [editTitle, setEditTitle] = useState(title);
53+
const [editDesc, setEditDesc] = useState(description || '');
4454

55+
const [showPlanMenu, setShowPlanMenu] = useState(false);
4556
const menuRef = useRef<HTMLDivElement>(null);
4657

4758
// 1. 초기 데이터 로드
@@ -148,6 +159,33 @@ export default function PlanSection({
148159
}
149160
};
150161

162+
// 6. 플랜 수정 핸들러
163+
const handleStartEdit = (e: React.MouseEvent) => {
164+
e.stopPropagation(); // 아코디언 토글 방지
165+
166+
setEditTitle(title); // props로 받은 최신값으로 초기화
167+
setEditDesc(description || '');
168+
169+
setIsEditingPlan(true);
170+
setShowPlanMenu(false); // 메뉴 닫기
171+
};
172+
173+
const handleSaveEdit = (e: React.MouseEvent) => {
174+
e.stopPropagation();
175+
176+
// 부모 컴포넌트에게 변경된 내용 전달
177+
onUpdatePlan(planId, editTitle, editDesc);
178+
179+
setIsEditingPlan(false); // 수정 모드 종료
180+
};
181+
182+
// 7. 플랜 수정 취소 핸들러
183+
const handleCancelEdit = (e: React.MouseEvent) => {
184+
e.stopPropagation();
185+
186+
setIsEditingPlan(false); // 수정 모드 종료
187+
};
188+
151189
// 완료된 할 일 개수 계산
152190
const completedCount = tasks.filter((t) => t.isChecked).length;
153191
const totalCount = tasks.length;
@@ -163,12 +201,51 @@ export default function PlanSection({
163201
{/* 헤더 영역 */}
164202
<div
165203
className="flex cursor-pointer items-start justify-between"
166-
onClick={() => setIsOpen(!isOpen)}
204+
onClick={() => !isEditingPlan && setIsOpen(!isOpen)}
167205
>
168-
<div>
169-
<h3 className="text-xl font-bold text-gray-900">{title}</h3>
170-
<p className="mt-1 text-sm text-gray-500">{description}</p>
171-
</div>
206+
{isEditingPlan ? (
207+
<div
208+
className="flex cursor-default flex-col gap-2"
209+
onClick={(e) => e.stopPropagation()}
210+
>
211+
{/* 제목 입력창 */}
212+
<input
213+
type="text"
214+
value={editTitle}
215+
onChange={(e) => setEditTitle(e.target.value)}
216+
className="w-full border-b-2 border-[#556BD6] bg-transparent text-xl font-bold text-gray-900 focus:outline-none"
217+
placeholder="플랜 제목"
218+
autoFocus
219+
/>
220+
{/* 설명 입력창 */}
221+
<input
222+
type="text"
223+
value={editDesc}
224+
onChange={(e) => setEditDesc(e.target.value)}
225+
className="w-full border-b border-gray-300 bg-transparent text-sm text-gray-600 focus:border-[#556BD6] focus:outline-none"
226+
placeholder="설명 (선택사항)"
227+
/>
228+
<div className="mt-1 flex gap-2">
229+
<button
230+
onClick={handleSaveEdit}
231+
className="flex items-center gap-1 rounded bg-[#556BD6] px-2 py-1 text-xs text-white transition-colors hover:bg-[#4456a8]"
232+
>
233+
<Check size={12} /> 저장
234+
</button>
235+
<button
236+
onClick={handleCancelEdit}
237+
className="flex items-center gap-1 rounded bg-gray-200 px-2 py-1 text-xs text-gray-600 transition-colors hover:bg-gray-300"
238+
>
239+
<X size={12} /> 취소
240+
</button>
241+
</div>
242+
</div>
243+
) : (
244+
<div>
245+
<h3 className="text-xl font-bold text-gray-900">{title}</h3>
246+
<p className="mt-1 text-sm text-gray-500">{description}</p>
247+
</div>
248+
)}
172249

173250
<div className="flex items-center gap-4">
174251
<div className="text-right">
@@ -193,10 +270,7 @@ export default function PlanSection({
193270
<div className="absolute top-8 right-0 w-32 overflow-hidden rounded-lg border border-gray-100 bg-white py-1 shadow-lg">
194271
<button
195272
className="flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50"
196-
onClick={(e) => {
197-
e.stopPropagation();
198-
alert('플랜 수정 기능');
199-
}}
273+
onClick={handleStartEdit}
200274
>
201275
<Edit2 size={14} /> 수정
202276
</button>

services/plans/planManageService.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,17 @@ export const deletePlanItem = async (uid: string, itemId: string) => {
146146
export const updatePlan = async (
147147
uid: string,
148148
planId: string,
149-
updates: { text?: string; description?: string }
149+
updates: { title?: string; description?: string }
150150
) => {
151151
const planRef = doc(db, 'users', uid, 'plans', planId);
152152

153153
const updatePayload: {
154-
text?: string;
154+
title?: string;
155155
description?: string;
156156
} = {};
157157

158-
if (updates.text !== undefined) {
159-
updatePayload.text = updates.text;
158+
if (updates.title !== undefined) {
159+
updatePayload.title = updates.title;
160160
}
161161
if (updates.description !== undefined) {
162162
updatePayload.description = updates.description;

0 commit comments

Comments
 (0)