@@ -318,7 +317,7 @@ export default function ProjectPage() {
}
return (
-
+
{/* 메인 콘텐츠 */}
@@ -330,6 +329,8 @@ export default function ProjectPage() {
onCreateTask={handleCreateTask}
onUpdateTask={handleUpdateTask}
onDeleteTask={handleDeleteTask}
+ onToggleMemo={()=>{setShowMemoPanel((prev)=>!prev)}}
+ showMemoPanel={showMemoPanel}
onProjectInfoClick={() => { setShowProjectInfoPanel((prev) => !prev); setShowMemoPanel(false); }}
project={{
project_id: projectId,
@@ -355,6 +356,8 @@ export default function ProjectPage() {
onDeleteTask={handleDeleteTask}
onSelectTask={() => {}}
onTaskCreated={handleRefresh}
+ onToggleMemo={()=>{setShowMemoPanel((prev)=>!prev)}}
+ showMemoPanel={showMemoPanel}
onProjectInfoClick={() => { setShowProjectInfoPanel((prev) => !prev); setShowMemoPanel(false); }}
/>
)}
@@ -387,7 +390,7 @@ export default function ProjectPage() {
: "w-0 opacity-0"
}`}
>
-
+ setShowMemoPanel(false)} />
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 25c8711..ad2694b 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -36,7 +36,7 @@ export default function RootLayout({
-
{children}
+
{children}
diff --git a/src/components/features/calendarView/CalendarView.tsx b/src/components/features/calendarView/CalendarView.tsx
index c5329aa..51ecee2 100644
--- a/src/components/features/calendarView/CalendarView.tsx
+++ b/src/components/features/calendarView/CalendarView.tsx
@@ -45,6 +45,7 @@ interface CalendarViewProps {
started_at?: string;
ended_at?: string;
} | null;
+ showMemoPanel?: boolean;
onCreateTask?: (
taskData: Omit
) => void;
@@ -52,6 +53,7 @@ interface CalendarViewProps {
onDeleteTask?: (taskId: string) => void;
onSelectTask?: (task: Task) => void;
onTaskCreated?: () => void;
+ onToggleMemo?: () => void;
onProjectInfoClick?: () => void;
}
@@ -59,11 +61,13 @@ export default function CalendarView({
tasks = [],
boardId,
project,
+ showMemoPanel,
onCreateTask,
onUpdateTask,
onDeleteTask,
onSelectTask,
onTaskCreated,
+ onToggleMemo,
onProjectInfoClick,
}: CalendarViewProps) {
// 프로젝트 정보 추출
@@ -296,8 +300,10 @@ export default function CalendarView({
currentDate={currentDate}
eventsCount={filteredTasks.length}
showHelp={showHelp}
+ showMemo={showMemoPanel}
showFilter={showFilter}
onToggleHelp={() => setShowHelp(!showHelp)}
+ onToggleMemo={onToggleMemo}
onToggleFilter={() => setShowFilter(!showFilter)}
onProjectInfoClick={onProjectInfoClick}
onAddTask={() => {
diff --git a/src/components/features/calendarView/components/CalendarHeader.tsx b/src/components/features/calendarView/components/CalendarHeader.tsx
index e36a047..dd0a0e2 100644
--- a/src/components/features/calendarView/components/CalendarHeader.tsx
+++ b/src/components/features/calendarView/components/CalendarHeader.tsx
@@ -13,17 +13,19 @@ import { showToast } from "@/lib/utils/toast";
interface CalendarHeaderProps {
projectName: string;
- projectStartedAt?: string;
- projectEndedAt?: string;
currentView: View;
currentDate: Date;
eventsCount: number;
showHelp: boolean;
+ showMemo?: boolean;
showFilter?: boolean;
+ projectStartedAt?: string;
+ projectEndedAt?: string;
onToggleHelp: () => void;
onToggleFilter?: () => void;
onAddTask?: () => void;
onProjectInfoClick?: () => void;
+ onToggleMemo?: () => void;
}
export default function CalendarHeader({
@@ -33,8 +35,10 @@ export default function CalendarHeader({
currentView,
currentDate,
eventsCount,
+ showMemo,
showHelp,
showFilter,
+ onToggleMemo,
onToggleHelp,
onToggleFilter,
onAddTask,
diff --git a/src/components/features/kanban/KanbanBoard.tsx b/src/components/features/kanban/KanbanBoard.tsx
index f38260f..ce00baa 100644
--- a/src/components/features/kanban/KanbanBoard.tsx
+++ b/src/components/features/kanban/KanbanBoard.tsx
@@ -39,6 +39,8 @@ interface KanbanBoardProps {
started_at?: string;
ended_at?: string;
} | null;
+ showMemoPanel?: boolean;
+ onToggleMemo?: () => void;
onCreateTask?: (
taskData: Omit
) => void;
@@ -59,6 +61,8 @@ const KanbanBoard = ({
onSelectTask,
onTaskCreated,
onProjectInfoClick,
+ showMemoPanel,
+ onToggleMemo,
}: KanbanBoardProps) => {
const projectId = project?.project_id || "";
const projectName = project?.project_name || "이름 없는 프로젝트";
@@ -333,6 +337,7 @@ const KanbanBoard = ({
onToggleFilter={() => setShowFilter(!showFilter)}
onToggleHelp={() => setShowHelp(!showHelp)}
showHelp={showHelp}
+ showMemo={showMemoPanel}
hasActiveFilter={
filter.priority !== "all" ||
filter.assignee !== "all" ||
@@ -340,6 +345,7 @@ const KanbanBoard = ({
}
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
+ onToggleMemo={onToggleMemo}
tasksCount={tasks.length}
project={project}
onProjectInfoClick={onProjectInfoClick}
diff --git a/src/components/features/kanban/MemoView.tsx b/src/components/features/kanban/MemoView.tsx
index 4983199..c8301b5 100644
--- a/src/components/features/kanban/MemoView.tsx
+++ b/src/components/features/kanban/MemoView.tsx
@@ -53,9 +53,10 @@ type SortType = "newest" | "oldest";
interface MemoFormProps {
projectId: string;
+ onClose?: () => void;
}
-const MemoView = ({ projectId }: MemoFormProps) => {
+const MemoView = ({ projectId, onClose }: MemoFormProps) => {
// === 기본 상태 관리 ===
const [memos, setMemos] = useState([]); // 서버에서 받은 원본 메모 목록
const [newMemo, setNewMemo] = useState(""); // 새 메모 입력 내용
@@ -524,6 +525,16 @@ const MemoView = ({ projectId }: MemoFormProps) => {
}`}
/>
+ {onClose && (
+
+ )}
diff --git a/src/components/features/kanban/components/KanbanHeader.tsx b/src/components/features/kanban/components/KanbanHeader.tsx
index 2f41ac9..7b76861 100644
--- a/src/components/features/kanban/components/KanbanHeader.tsx
+++ b/src/components/features/kanban/components/KanbanHeader.tsx
@@ -4,39 +4,43 @@ import { useState, useRef, useEffect } from "react";
import { format } from "date-fns";
import { ko } from "date-fns/locale";
import { showToast } from "@/lib/utils/toast";
-import { Info, SlidersHorizontal, HelpCircle, Plus, Search, X } from "lucide-react";
+import { Info, SlidersHorizontal, HelpCircle, Plus, Search, X, StickyNote } from "lucide-react";
interface KanbanHeaderProps {
projectName: string;
- onAddClick: () => void;
- onToggleFilter: () => void;
- onToggleHelp: () => void;
- showHelp: boolean;
- hasActiveFilter?: boolean;
- searchQuery?: string;
- onSearchChange?: (q: string) => void;
- tasksCount: number;
project?: {
project_id?: string;
project_name: string;
started_at?: string;
ended_at?: string;
} | null;
+ showHelp: boolean;
+ showMemo?: boolean;
+ tasksCount: number;
+ searchQuery?: string;
+ hasActiveFilter?: boolean;
+ onAddClick: () => void;
+ onToggleHelp: () => void;
+ onToggleMemo?: () => void;
+ onToggleFilter: () => void;
onProjectInfoClick?: () => void;
+ onSearchChange?: (q: string) => void;
}
export default function KanbanHeader({
+ project,
projectName,
+ showHelp,
+ showMemo,
+ tasksCount,
+ searchQuery = "",
+ hasActiveFilter = false,
onAddClick,
onToggleFilter,
onToggleHelp,
- showHelp,
- hasActiveFilter = false,
- searchQuery = "",
onSearchChange,
- tasksCount,
- project,
onProjectInfoClick,
+ onToggleMemo,
}: KanbanHeaderProps) {
const [searchOpen, setSearchOpen] = useState(false);
const searchRef = useRef
(null);
@@ -178,6 +182,18 @@ export default function KanbanHeader({
)}
+ {onToggleMemo && (
+
+ )}
+
+
diff --git a/src/components/layout/BottomNavigation.tsx b/src/components/layout/BottomNavigation.tsx
index 7f7ba4d..3ac4739 100644
--- a/src/components/layout/BottomNavigation.tsx
+++ b/src/components/layout/BottomNavigation.tsx
@@ -52,25 +52,7 @@ const BottomNavigation = ({
),
},
- {
- id: "memo" as NavItem,
- label: "메모",
- icon: (
-
- ),
- },
+
];
return (
diff --git a/src/components/shared/Container.tsx b/src/components/shared/Container.tsx
index 183c6f2..d63da06 100644
--- a/src/components/shared/Container.tsx
+++ b/src/components/shared/Container.tsx
@@ -9,7 +9,7 @@ export default function Container({
}: ContainerProps) {
return (
{children}
diff --git a/src/features/project/ui/ProjectCard.tsx b/src/features/project/ui/ProjectCard.tsx
index c865707..95a0e71 100644
--- a/src/features/project/ui/ProjectCard.tsx
+++ b/src/features/project/ui/ProjectCard.tsx
@@ -1,5 +1,7 @@
"use client";
+import { useState } from "react";
+
import { Icon } from "@/components/shared/Icon";
import { DeleteDialog } from "./DeleteDialog";
import { deleteProject, deleteProjectMember } from "../model";
@@ -8,7 +10,7 @@ import { showToast } from "@/lib/utils/toast";
import { useQueryClient } from "@tanstack/react-query";
import { queryKeys } from "@/lib/constants/queryKeys";
import type { Project, ProjectStatus } from "../model";
-import { Pencil } from "lucide-react";
+import { MoreVertical,Pencil } from "lucide-react";
interface ProjectCardProps {
project: Project;
@@ -38,6 +40,8 @@ export default function ProjectCard({ project, projectMember }: ProjectCardProps
const dot = STATUS_DOT[project.status] ?? STATUS_DOT.active;
const deadline = formatDeadline(project.ended_at);
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+
async function handleDelete() {
await deleteProject(project.project_id);
await deleteProjectMember(project.project_id);
@@ -50,6 +54,42 @@ export default function ProjectCard({ project, projectMember }: ProjectCardProps
onClick={() => router.push(`/project/workspace/${project.project_id}`)}
className="group relative flex flex-col bg-white dark:bg-card rounded-[14px] border border-[#bde3ec] dark:border-border p-6 cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-0.5"
>
+ {/* ⋮ 메뉴 버튼 — 항상 표시 */}
+ e.stopPropagation()}
+>
+
+
+ {isMenuOpen && (
+ <>
+ {/* 외부 클릭 시 닫기 */}
+
setIsMenuOpen(false)}
+ />
+
+
+
+ { setIsMenuOpen(false); handleDelete(); }} />
+
+
+ >
+ )}
+
+
{/* 타입 라벨 */}
@@ -66,7 +106,7 @@ export default function ProjectCard({ project, projectMember }: ProjectCardProps
- {/* 마감일 + 멤버 수 + 호버 액션 */}
+ {/* 마감일 + 멤버 수 */}
{deadline ?? "마감일 없음"}
@@ -77,20 +117,7 @@ export default function ProjectCard({ project, projectMember }: ProjectCardProps
{memberCount}명
-
e.stopPropagation()}
- >
-
-
-
-
-
+