diff --git a/src/app/globals.css b/src/app/globals.css index a33a764..330df6e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -13,6 +13,7 @@ --color-brand-surface: #f7f8fc; --color-brand-soft: #eef0fb; --color-brand-secondary: #f1f3f9; + --color-brand-panel-soft: #f1f3f999; --color-brand-start: #615fff; --color-brand-end: #8e51ff; --color-brand-deep: #7f22fe; diff --git a/src/app/workspaces/[workspaceId]/layout.tsx b/src/app/workspaces/[workspaceId]/layout.tsx index a7cc4a9..bd7f5fb 100644 --- a/src/app/workspaces/[workspaceId]/layout.tsx +++ b/src/app/workspaces/[workspaceId]/layout.tsx @@ -1,4 +1,6 @@ // 워크스페이스 공통 사이드바와 헤더를 적용하는 라우트 레이아웃입니다. +import { notFound } from 'next/navigation'; +import { getMockWorkspaceById } from '@/entities/workspace'; import { WorkspaceShell } from '@/widgets/workspace-shell'; interface WorkspaceLayoutProps { @@ -10,6 +12,15 @@ interface WorkspaceLayoutProps { export default async function WorkspaceLayout({ children, params }: WorkspaceLayoutProps) { const { workspaceId } = await params; + const workspace = getMockWorkspaceById(workspaceId); - return {children}; + if (!workspace) { + notFound(); + } + + return ( + + {children} + + ); } diff --git a/src/app/workspaces/[workspaceId]/page.tsx b/src/app/workspaces/[workspaceId]/page.tsx new file mode 100644 index 0000000..2d00c26 --- /dev/null +++ b/src/app/workspaces/[workspaceId]/page.tsx @@ -0,0 +1,25 @@ +import { notFound, redirect } from 'next/navigation'; +import { getMockWorkspaceById } from '@/entities/workspace'; + +interface WorkspaceHomePageProps { + params: Promise<{ + workspaceId: string; + }>; +} + +export default async function WorkspaceHomePage({ + params, +}: WorkspaceHomePageProps) { + const { workspaceId } = await params; + const workspace = getMockWorkspaceById(workspaceId); + + if (!workspace) { + notFound(); + } + + if (workspace.purpose === 'store-operation') { + redirect(`/workspaces/${workspaceId}/work-schedule`); + } + + redirect(`/workspaces/${workspaceId}/project-management`); +} diff --git a/src/app/workspaces/[workspaceId]/project-management/page.tsx b/src/app/workspaces/[workspaceId]/project-management/page.tsx new file mode 100644 index 0000000..3b86497 --- /dev/null +++ b/src/app/workspaces/[workspaceId]/project-management/page.tsx @@ -0,0 +1,15 @@ +import { ProjectManagementPage } from '@/views/project-management'; + +interface WorkspaceProjectManagementPageProps { + params: Promise<{ + workspaceId: string; + }>; +} + +export default async function WorkspaceProjectManagementPage({ + params, +}: WorkspaceProjectManagementPageProps) { + const { workspaceId } = await params; + + return ; +} diff --git a/src/entities/project-column/index.ts b/src/entities/project-column/index.ts new file mode 100644 index 0000000..28c8778 --- /dev/null +++ b/src/entities/project-column/index.ts @@ -0,0 +1,2 @@ +export type { ProjectBoardColumn } from './model/types'; +export { ProjectColumn } from './ui/ProjectColumn'; diff --git a/src/entities/project-column/model/types.ts b/src/entities/project-column/model/types.ts new file mode 100644 index 0000000..f9519ab --- /dev/null +++ b/src/entities/project-column/model/types.ts @@ -0,0 +1,10 @@ +import type { Task, TaskStatus } from '@/entities/task'; + +export type ProjectColumnTone = 'slate' | 'brand' | 'green'; + +export type ProjectBoardColumn = { + id: TaskStatus; + title: string; + tone: ProjectColumnTone; + tasks: Task[]; +}; diff --git a/src/entities/project-column/ui/ProjectColumn.tsx b/src/entities/project-column/ui/ProjectColumn.tsx new file mode 100644 index 0000000..279fc5f --- /dev/null +++ b/src/entities/project-column/ui/ProjectColumn.tsx @@ -0,0 +1,98 @@ +import type { DragEvent } from 'react'; +import { TaskCard } from '@/entities/task'; +import type { TaskStatus } from '@/entities/task'; +import { cn } from '@/shared/lib/utils'; +import type { ProjectBoardColumn } from '../model/types'; + +type ProjectColumnProps = { + column: ProjectBoardColumn; + onDeleteTask: (taskId: string) => void; + onDropTask: (columnId: TaskStatus, targetIndex: number, taskId?: string) => void; + onDragStartTask: (event: DragEvent, taskId: string) => void; + onDragEndTask: () => void; + draggingTaskId: string | null; + dragOverIndex: number | null; + onDragOverTask: (columnId: TaskStatus, targetIndex: number) => void; + onDragLeaveColumn: (columnId: TaskStatus) => void; +}; + +const toneStyles = { + slate: 'bg-brand-panel-soft text-[#9aa3b2]', + brand: 'bg-brand-panel-soft text-brand-start', + green: 'bg-brand-panel-soft text-[#00c950]', +} as const; + +export function ProjectColumn({ + column, + onDeleteTask, + onDropTask, + onDragStartTask, + onDragEndTask, + draggingTaskId, + dragOverIndex, + onDragOverTask, + onDragLeaveColumn, +}: ProjectColumnProps) { + const handleDrop = (event: DragEvent, targetIndex: number) => { + event.preventDefault(); + event.stopPropagation(); + const taskId = event.dataTransfer.getData('text/plain') || undefined; + onDropTask(column.id, targetIndex, taskId); + }; + + return ( +
{ + if (!event.currentTarget.contains(event.relatedTarget as Node | null)) { + onDragLeaveColumn(column.id); + } + }} + > +
+
+ +

{column.title}

+
+ {column.tasks.length} +
+ +
{ + event.preventDefault(); + onDragOverTask(column.id, column.tasks.length); + }} + onDrop={(event) => handleDrop(event, column.tasks.length)} + > + {column.tasks.map((task, index) => ( +
{ + event.preventDefault(); + event.stopPropagation(); + onDragOverTask(column.id, index); + }} + onDrop={(event) => handleDrop(event, index)} + > + +
+ ))} + + {dragOverIndex === column.tasks.length ? ( +
+ ) : null} +
+
+ ); +} diff --git a/src/entities/task/index.ts b/src/entities/task/index.ts new file mode 100644 index 0000000..ec1c76b --- /dev/null +++ b/src/entities/task/index.ts @@ -0,0 +1,3 @@ +export { getMockTasksByWorkspaceId } from './model/mock-tasks-by-workspace'; +export type { Task, TaskStatus } from './model/task.types'; +export { TaskCard } from './ui/TaskCard'; diff --git a/src/entities/task/model/mock-tasks-by-workspace.ts b/src/entities/task/model/mock-tasks-by-workspace.ts new file mode 100644 index 0000000..ed0eaa0 --- /dev/null +++ b/src/entities/task/model/mock-tasks-by-workspace.ts @@ -0,0 +1,80 @@ +import type { Task } from './task.types'; + +const mockTasksByWorkspaceId: Record = { + test: [ + { + id: 'task-1', + workspaceId: 'test', + title: '사용자 인터뷰 설문지 제작', + assignee: '박서준', + assigneeInitial: '박', + assigneeColor: '#00C950', + dueDate: '7/5', + status: 'todo', + }, + { + id: 'task-2', + workspaceId: 'test', + title: 'DB 스키마 설계', + assignee: '김지은', + assigneeInitial: '김', + assigneeColor: '#FE9A00', + dueDate: '7/6', + status: 'todo', + }, + { + id: 'task-3', + workspaceId: 'test', + title: '스프린트 1 회고 준비', + assignee: '이하은', + assigneeInitial: '이', + assigneeColor: '#615FFF', + dueDate: '7/10', + status: 'todo', + }, + { + id: 'task-4', + workspaceId: 'test', + title: '와이어프레임 초안 작성', + assignee: '김지은', + assigneeInitial: '김', + assigneeColor: '#00B8DB', + dueDate: '7/3', + status: 'in-progress', + }, + { + id: 'task-5', + workspaceId: 'test', + title: '랜딩 페이지 디자인', + assignee: '최민준', + assigneeInitial: '최', + assigneeColor: '#2B7FFF', + dueDate: '7/8', + status: 'in-progress', + }, + { + id: 'task-6', + workspaceId: 'test', + title: 'API 명세서 문서화', + assignee: '이하은', + assigneeInitial: '이', + assigneeColor: '#615FFF', + dueDate: '7/2', + status: 'done', + }, + { + id: 'task-7', + workspaceId: 'test', + title: '로고 시안 3종 작성', + assignee: '박서준', + assigneeInitial: '박', + assigneeColor: '#FE9A00', + dueDate: '6/30', + status: 'done', + }, + ], +}; + +export function getMockTasksByWorkspaceId(workspaceId: string): Task[] { + return mockTasksByWorkspaceId[workspaceId]?.map((task) => ({ ...task })) ?? []; +} diff --git a/src/entities/task/model/task.types.ts b/src/entities/task/model/task.types.ts new file mode 100644 index 0000000..ba506e6 --- /dev/null +++ b/src/entities/task/model/task.types.ts @@ -0,0 +1,12 @@ +export type TaskStatus = 'todo' | 'in-progress' | 'done'; + +export type Task = { + id: string; + workspaceId: string; + title: string; + assignee: string; + assigneeInitial: string; + assigneeColor: string; + dueDate: string; + status: TaskStatus; +}; diff --git a/src/entities/task/ui/TaskCard.tsx b/src/entities/task/ui/TaskCard.tsx new file mode 100644 index 0000000..a622225 --- /dev/null +++ b/src/entities/task/ui/TaskCard.tsx @@ -0,0 +1,59 @@ +import type { DragEvent } from 'react'; +import { Clock3, X } from 'lucide-react'; +import type { Task } from '../model/task.types'; + +type TaskCardProps = { + task: Task; + onDelete: (taskId: string) => void; + onDragStart: (event: DragEvent, taskId: string) => void; + onDragEnd: () => void; + isDragging?: boolean; +}; + +export function TaskCard({ + task, + onDelete, + onDragStart, + onDragEnd, + isDragging = false, +}: TaskCardProps) { + return ( +
onDragStart(event, task.id)} + onDragEnd={onDragEnd} + className="group border-brand/10 rounded-[22px] border bg-white p-4.5 shadow-[0_1px_2px_rgba(0,0,0,0.08),0_2px_6px_rgba(0,0,0,0.04)] transition-opacity" + style={{ opacity: isDragging ? 0.55 : 1 }} + > +
+

+ {task.title} +

+ +
+
+
+
+ {task.assigneeInitial} +
+ {task.assignee} +
+ +
+ + {task.dueDate} +
+
+
+ ); +} diff --git a/src/entities/workspace/index.ts b/src/entities/workspace/index.ts index 659fd91..f6d636d 100644 --- a/src/entities/workspace/index.ts +++ b/src/entities/workspace/index.ts @@ -1,5 +1,5 @@ // workspace 엔티티 Public API export type { Workspace, WorkspacePurpose, WorkspaceSummary } from './model/workspace.types'; -export { mockWorkspace } from './model/mock-workspace'; +export { getMockWorkspaceById, mockWorkspace } from './model/mock-workspace'; export { WORKSPACE_PURPOSE_META, FALLBACK_PURPOSE_META } from './config/purpose'; export { getMyWorkspaces } from './api/get-my-workspaces'; diff --git a/src/entities/workspace/model/mock-workspace.ts b/src/entities/workspace/model/mock-workspace.ts index e482c6e..b352c01 100644 --- a/src/entities/workspace/model/mock-workspace.ts +++ b/src/entities/workspace/model/mock-workspace.ts @@ -1,8 +1,22 @@ // 워크스페이스 API 연결 전 사이드바에 표시하는 현재 워크스페이스 목업입니다. +import { cache } from 'react'; import type { Workspace } from './workspace.types'; -export const mockWorkspace: Workspace = { - id: 'test', - name: '카페 그레이 운영', - purpose: 'store-operation', +const mockWorkspacesById: Record = { + test: { + id: 'test', + name: '캡스톤 디자인 팀', + purpose: 'team-project', + }, + 'store-test': { + id: 'store-test', + name: '카페 그레이 운영', + purpose: 'store-operation', + }, }; + +export const mockWorkspace: Workspace = mockWorkspacesById['store-test']; + +export const getMockWorkspaceById = cache( + (workspaceId: string): Workspace | null => mockWorkspacesById[workspaceId] ?? null, +); diff --git a/src/features/project-board/index.ts b/src/features/project-board/index.ts new file mode 100644 index 0000000..7e0b190 --- /dev/null +++ b/src/features/project-board/index.ts @@ -0,0 +1 @@ +export { ProjectBoard } from './ui/ProjectBoard'; diff --git a/src/features/project-board/model/project-board-columns.ts b/src/features/project-board/model/project-board-columns.ts new file mode 100644 index 0000000..ae2509b --- /dev/null +++ b/src/features/project-board/model/project-board-columns.ts @@ -0,0 +1,23 @@ +import type { ProjectBoardColumn } from '@/entities/project-column'; +import type { Task, TaskStatus } from '@/entities/task'; + +const projectBoardColumnConfig: Array<{ + id: TaskStatus; + title: string; + tone: ProjectBoardColumn['tone']; +}> = [ + { id: 'todo', title: '대기', tone: 'slate' }, + { id: 'in-progress', title: '진행 중', tone: 'brand' }, + { id: 'done', title: '완료', tone: 'green' }, +]; + +export function createProjectBoardColumns(tasks: Task[]): ProjectBoardColumn[] { + return projectBoardColumnConfig.map((column) => ({ + ...column, + tasks: tasks.filter((task) => task.status === column.id), + })); +} + +export function flattenProjectBoardColumns(columns: ProjectBoardColumn[]): Task[] { + return columns.flatMap((column) => column.tasks); +} diff --git a/src/features/project-board/ui/ProjectBoard.tsx b/src/features/project-board/ui/ProjectBoard.tsx new file mode 100644 index 0000000..d8c2ee1 --- /dev/null +++ b/src/features/project-board/ui/ProjectBoard.tsx @@ -0,0 +1,235 @@ +'use client'; + +import type { DragEvent, KeyboardEvent } from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { Plus, X } from 'lucide-react'; +import { ProjectColumn } from '@/entities/project-column'; +import type { ProjectBoardColumn } from '@/entities/project-column'; +import { getMockTasksByWorkspaceId } from '@/entities/task'; +import type { Task, TaskStatus } from '@/entities/task'; +import { + createProjectBoardColumns, + flattenProjectBoardColumns, +} from '../model/project-board-columns'; + +const DEFAULT_ASSIGNEE = { + name: '김지은', + initial: '김', + color: '#1BB6DB', +}; + +type ProjectBoardProps = { + workspaceId: string; +}; + +export function ProjectBoard({ workspaceId }: ProjectBoardProps) { + const [isComposerOpen, setIsComposerOpen] = useState(false); + const [taskTitle, setTaskTitle] = useState(''); + const [tasks, setTasks] = useState(() => getMockTasksByWorkspaceId(workspaceId)); + const [draggingTaskId, setDraggingTaskId] = useState(null); + const [dragOverState, setDragOverState] = useState<{ + columnId: TaskStatus; + index: number; + } | null>(null); + const inputRef = useRef(null); + const taskCount = tasks.length; + const columns = createProjectBoardColumns(tasks); + + useEffect(() => { + if (!isComposerOpen) { + return; + } + + inputRef.current?.focus(); + }, [isComposerOpen]); + + const createTask = () => { + const trimmedTitle = taskTitle.trim(); + + if (!trimmedTitle) { + return; + } + + const today = new Date(); + const formattedDate = `${today.getMonth() + 1}/${today.getDate()}`; + + const newTask: Task = { + id: `task-${taskCount + 1}-${Date.now()}`, + workspaceId, + title: trimmedTitle, + assignee: DEFAULT_ASSIGNEE.name, + assigneeInitial: DEFAULT_ASSIGNEE.initial, + assigneeColor: DEFAULT_ASSIGNEE.color, + dueDate: formattedDate, + status: 'todo', + }; + + setTasks((currentTasks) => [...currentTasks, newTask]); + setTaskTitle(''); + setIsComposerOpen(false); + }; + + const handleComposerKeyDown = (event: KeyboardEvent) => { + if (event.key !== 'Enter') { + return; + } + + event.preventDefault(); + createTask(); + }; + + const handleDeleteTask = (taskId: string) => { + setTasks((currentTasks) => currentTasks.filter((task) => task.id !== taskId)); + }; + + const handleDropTask = ( + targetColumnId: TaskStatus, + targetIndex: number, + droppedTaskId?: string, + ) => { + const activeTaskId = droppedTaskId ?? draggingTaskId; + + if (!activeTaskId) { + return; + } + + let movedTask: Task | null = null; + let sourceColumnId: TaskStatus | null = null; + let sourceTaskIndex: number | null = null; + + const nextColumns = columns.map((column) => { + const taskIndex = column.tasks.findIndex((task) => task.id === activeTaskId); + + if (taskIndex === -1) { + return column; + } + + sourceColumnId = column.id; + sourceTaskIndex = taskIndex; + movedTask = { ...column.tasks[taskIndex], status: targetColumnId }; + + return { + ...column, + tasks: column.tasks.filter((task) => task.id !== activeTaskId), + }; + }); + + if (!movedTask) { + setDraggingTaskId(null); + setDragOverState(null); + return; + } + + const nextTask = movedTask; + + const reorderedColumns = nextColumns.map((column) => { + if (column.id !== targetColumnId) { + return column; + } + + const insertionIndex = + sourceColumnId === targetColumnId + ? Math.min( + sourceTaskIndex !== null && sourceTaskIndex < targetIndex + ? targetIndex - 1 + : targetIndex, + column.tasks.length, + ) + : Math.min(targetIndex, column.tasks.length); + + return { + ...column, + tasks: [ + ...column.tasks.slice(0, insertionIndex), + nextTask, + ...column.tasks.slice(insertionIndex), + ], + }; + }); + + setTasks(flattenProjectBoardColumns(reorderedColumns)); + setDraggingTaskId(null); + setDragOverState(null); + }; + + const handleDragStartTask = (event: DragEvent, taskId: string) => { + event.dataTransfer.effectAllowed = 'move'; + event.dataTransfer.setData('text/plain', taskId); + setDraggingTaskId(taskId); + }; + + const handleDragEndTask = () => { + setDraggingTaskId(null); + setDragOverState(null); + }; + + return ( +
+
+

+ 프로젝트 관리 +

+ +
+ + {isComposerOpen ? ( +
+
+ setTaskTitle(event.target.value)} + onKeyDown={handleComposerKeyDown} + /> + +
+
+ ) : null} + +
+ {columns.map((column: ProjectBoardColumn) => ( + + setDragOverState({ columnId, index }) + } + onDragLeaveColumn={(columnId) => { + setDragOverState((current) => + current?.columnId === columnId ? null : current, + ); + }} + /> + ))} +
+
+ ); +} diff --git a/src/views/project-management/index.ts b/src/views/project-management/index.ts new file mode 100644 index 0000000..918e057 --- /dev/null +++ b/src/views/project-management/index.ts @@ -0,0 +1 @@ +export { default as ProjectManagementPage } from './ui/ProjectManagementPage'; diff --git a/src/views/project-management/ui/ProjectManagementPage.tsx b/src/views/project-management/ui/ProjectManagementPage.tsx new file mode 100644 index 0000000..15838f9 --- /dev/null +++ b/src/views/project-management/ui/ProjectManagementPage.tsx @@ -0,0 +1,21 @@ +import { Plus_Jakarta_Sans } from 'next/font/google'; +import { ProjectBoard } from '@/features/project-board'; + +const jakarta = Plus_Jakarta_Sans({ + subsets: ['latin'], + weight: ['400', '500', '600', '700', '800'], +}); + +type ProjectManagementPageProps = { + workspaceId: string; +}; + +export default function ProjectManagementPage({ + workspaceId, +}: ProjectManagementPageProps) { + return ( +
+ +
+ ); +} diff --git a/src/widgets/workspace-shell/model/workspace-navigation.ts b/src/widgets/workspace-shell/model/workspace-navigation.ts index cb56ede..d36b57f 100644 --- a/src/widgets/workspace-shell/model/workspace-navigation.ts +++ b/src/widgets/workspace-shell/model/workspace-navigation.ts @@ -1,16 +1,16 @@ // 워크스페이스 목적별 사이드바 메뉴 항목을 정의합니다. import type { WorkspacePurpose } from '@/entities/workspace'; import { + BarChart3, Bell, Calendar, ClipboardList, FileBox, + FileText, LayoutDashboard, MessageSquare, - Settings, Rocket, - FileText, - BarChart3, + Settings, type LucideIcon, } from 'lucide-react'; @@ -30,7 +30,6 @@ export const storeOperationNavigationItems: WorkspaceNavigationItem[] = [ { label: '설정', href: 'settings', icon: Settings }, ]; -// TODO: 사이드 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다. export const sideProjectNavigationItems: WorkspaceNavigationItem[] = [ { label: '대시보드', href: 'dashboard', icon: LayoutDashboard }, { label: '스프린트 보드', href: 'sprint-board', icon: Rocket }, @@ -42,10 +41,9 @@ export const sideProjectNavigationItems: WorkspaceNavigationItem[] = [ { label: '설정', href: 'settings', icon: Settings }, ]; -// TODO: 팀 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다. export const teamProjectNavigationItems: WorkspaceNavigationItem[] = [ { label: '대시보드', href: 'dashboard', icon: LayoutDashboard }, - { label: '프로젝트 관리', href: 'sprint-board', icon: Rocket }, + { label: '프로젝트 관리', href: 'project-management', icon: Rocket }, { label: '캘린더', href: 'calendar', icon: Calendar }, { label: '공지', href: 'notices', icon: Bell }, { label: '회의록', href: 'meeting-notes', icon: FileText }, diff --git a/src/widgets/workspace-shell/ui/WorkspaceHeader.tsx b/src/widgets/workspace-shell/ui/WorkspaceHeader.tsx index 1e15ae5..f9e0c51 100644 --- a/src/widgets/workspace-shell/ui/WorkspaceHeader.tsx +++ b/src/widgets/workspace-shell/ui/WorkspaceHeader.tsx @@ -11,7 +11,9 @@ interface WorkspaceHeaderProps { } function getCurrentPageTitle(pathname: string, navigationItems: WorkspaceNavigationItem[]): string { - const currentNavigationItem = navigationItems.find((item) => pathname.endsWith(`/${item.href}`)); + const currentNavigationItem = navigationItems.find((item) => + pathname.endsWith(`/${item.href}`), + ); return currentNavigationItem?.label ?? '대시보드'; } diff --git a/src/widgets/workspace-shell/ui/WorkspaceShell.tsx b/src/widgets/workspace-shell/ui/WorkspaceShell.tsx index bf8189e..5ae01ff 100644 --- a/src/widgets/workspace-shell/ui/WorkspaceShell.tsx +++ b/src/widgets/workspace-shell/ui/WorkspaceShell.tsx @@ -2,19 +2,19 @@ // 공통 워크스페이스 사이드바, 헤더, 페이지 콘텐츠 프레임을 조합합니다. import { useState } from 'react'; +import type { Workspace } from '@/entities/workspace'; import { WorkspaceHeader } from './WorkspaceHeader'; import { WorkspaceSidebar } from './WorkspaceSidebar'; -import { mockWorkspace } from '@/entities/workspace'; import { getWorkspaceNavigation } from '@/widgets/workspace-shell/lib/get-workspace-navigation'; interface WorkspaceShellProps { + workspace: Workspace; workspaceId: string; children: React.ReactNode; } -export function WorkspaceShell({ workspaceId, children }: WorkspaceShellProps) { +export function WorkspaceShell({ workspace, workspaceId, children }: WorkspaceShellProps) { const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); - const workspace = mockWorkspace; // 나중에는 workspaceId로 Supabase 조회 const navigationItems = getWorkspaceNavigation(workspace.purpose); return ( diff --git a/src/widgets/workspace-shell/ui/WorkspaceSidebar.tsx b/src/widgets/workspace-shell/ui/WorkspaceSidebar.tsx index 249f4a4..626031b 100644 --- a/src/widgets/workspace-shell/ui/WorkspaceSidebar.tsx +++ b/src/widgets/workspace-shell/ui/WorkspaceSidebar.tsx @@ -1,6 +1,6 @@ 'use client'; -// 매장 운영 워크스페이스 페이지에서 공통으로 사용하는 좌측 사이드바입니다. +// 워크스페이스 페이지에서 공통으로 사용하는 좌측 사이드바입니다. import Image from 'next/image'; import Link from 'next/link'; import { ChevronRight, LogOut, Menu, Store } from 'lucide-react';