diff --git a/src/app/workspaces/[workspaceId]/settings/page.tsx b/src/app/workspaces/[workspaceId]/settings/page.tsx new file mode 100644 index 0000000..99658a4 --- /dev/null +++ b/src/app/workspaces/[workspaceId]/settings/page.tsx @@ -0,0 +1,44 @@ +// 설정 페이지 라우트 — 활성 탭을 searchParam(?tab=)으로 읽고, 표시에 필요한 데이터를 RSC에서 조회해 주입한다. +// 실 API 전환 시 아래 조회부만 async(Supabase)로 교체하면 되고, 하위 뷰/훅은 그대로 둔다. +import { notFound } from 'next/navigation'; +import { getMockWorkspaceById } from '@/entities/workspace'; +import { + getMockWorkspaceMembersByWorkspaceId, + mockCurrentWorkspaceMember, +} from '@/entities/workspace-member'; +import { SettingsView, parseSettingsTab } from '@/views/settings'; + +interface WorkspaceSettingsPageProps { + params: Promise<{ + workspaceId: string; + }>; + searchParams: Promise<{ + tab?: string; + }>; +} + +export default async function WorkspaceSettingsPage({ + params, + searchParams, +}: WorkspaceSettingsPageProps) { + const { workspaceId } = await params; + const { tab } = await searchParams; + const workspace = getMockWorkspaceById(workspaceId); + + if (!workspace) { + notFound(); + } + + const members = getMockWorkspaceMembersByWorkspaceId(workspaceId); + + return ( + + ); +} diff --git a/src/entities/meeting-note/index.ts b/src/entities/meeting-note/index.ts index e77fbd2..6d29886 100644 --- a/src/entities/meeting-note/index.ts +++ b/src/entities/meeting-note/index.ts @@ -1,3 +1,7 @@ export { getMockMeetingNotesByWorkspaceId } from './model/mock-meeting-notes-by-workspace'; -export type { MeetingNote, MeetingNoteFormValues, MeetingNoteParticipant } from './model/meeting-note.types'; +export type { + MeetingNote, + MeetingNoteFormValues, + MeetingNoteParticipant, +} from './model/meeting-note.types'; export { MeetingNoteCard } from './ui/MeetingNoteCard'; diff --git a/src/entities/meeting-note/ui/MeetingNoteCard.tsx b/src/entities/meeting-note/ui/MeetingNoteCard.tsx index b5e970e..039a90a 100644 --- a/src/entities/meeting-note/ui/MeetingNoteCard.tsx +++ b/src/entities/meeting-note/ui/MeetingNoteCard.tsx @@ -37,10 +37,12 @@ export function MeetingNoteCard({
-

+

{meetingNote.title}

-

{meetingNote.meetingDate}

+

+ {meetingNote.meetingDate} +

@@ -61,12 +63,15 @@ export function MeetingNoteCard({ {isExpanded ? (
-

+

결정사항

    {meetingNote.decisions.map((decision) => ( -
  • +
  • {decision}
  • @@ -75,13 +80,16 @@ export function MeetingNoteCard({
-

+

후속 업무

    {meetingNote.followUpActions.map((actionItem) => ( -
  • - +
  • + {actionItem}
  • ))} diff --git a/src/entities/side-project/meeting-note/index.ts b/src/entities/side-project/meeting-note/index.ts deleted file mode 100644 index 85fa5ae..0000000 --- a/src/entities/side-project/meeting-note/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// meeting-note 엔티티의 Public API (모델) -export { type MeetingNote } from './model/meeting-note.types'; -export { mockMeetingNotes } from './model/meeting-note.mock'; diff --git a/src/entities/side-project/meeting-note/model/meeting-note.mock.ts b/src/entities/side-project/meeting-note/model/meeting-note.mock.ts deleted file mode 100644 index 93b0f37..0000000 --- a/src/entities/side-project/meeting-note/model/meeting-note.mock.ts +++ /dev/null @@ -1,29 +0,0 @@ -// 회의록 목데이터 -import type { MeetingNote } from './meeting-note.types'; - -export const mockMeetingNotes: MeetingNote[] = [ - { - id: 'note-1', - title: 'Sprint 2 플래닝', - date: '2025-07-01', - summary: '스프린트 목표와 백로그 우선순위를 확정했습니다.', - }, - { - id: 'note-2', - title: 'Sprint 1 회고', - date: '2025-06-28', - summary: '지난 스프린트의 성과와 개선점을 논의했습니다.', - }, - { - id: 'note-3', - title: '디자인 시스템 논의', - date: '2025-06-24', - summary: '공통 컴포넌트와 디자인 토큰 구조를 정리했습니다.', - }, - { - id: 'note-4', - title: 'API 명세 리뷰', - date: '2025-06-20', - summary: '엔드포인트 규격과 에러 응답 형식을 검토했습니다.', - }, -]; diff --git a/src/entities/side-project/meeting-note/model/meeting-note.types.ts b/src/entities/side-project/meeting-note/model/meeting-note.types.ts deleted file mode 100644 index 0f30439..0000000 --- a/src/entities/side-project/meeting-note/model/meeting-note.types.ts +++ /dev/null @@ -1,8 +0,0 @@ -// 회의록(MeetingNote) 도메인 모델 -export interface MeetingNote { - id: string; - title: string; - date: string; - /** 본문 요약(미리보기) */ - summary: string; -} diff --git a/src/entities/workspace-member/config/labels.ts b/src/entities/workspace-member/config/labels.ts new file mode 100644 index 0000000..d9a6dd8 --- /dev/null +++ b/src/entities/workspace-member/config/labels.ts @@ -0,0 +1,21 @@ +// 멤버의 역할/참여 상태를 UI에 표시하기 위한 한글 라벨과 뱃지 톤 매핑입니다. +import type { WorkspaceMemberRole, WorkspaceMemberStatus } from '../model/workspace-member.types'; + +// Badge 컴포넌트의 tone 값과 일치시킵니다. +export type BadgeTone = 'brand' | 'neutral' | 'success' | 'warning'; + +export const WORKSPACE_MEMBER_ROLE_META: Record< + WorkspaceMemberRole, + { label: string; tone: BadgeTone } +> = { + owner: { label: '팀장', tone: 'brand' }, + member: { label: '팀원', tone: 'neutral' }, +}; + +export const WORKSPACE_MEMBER_STATUS_META: Record< + WorkspaceMemberStatus, + { label: string; tone: BadgeTone } +> = { + joined: { label: '참여 중', tone: 'success' }, + invited: { label: '초대됨', tone: 'warning' }, +}; diff --git a/src/entities/workspace-member/index.ts b/src/entities/workspace-member/index.ts index 185bed6..a4e1128 100644 --- a/src/entities/workspace-member/index.ts +++ b/src/entities/workspace-member/index.ts @@ -1,7 +1,16 @@ // 목업 워크스페이스 멤버 데이터와 타입의 공개 API입니다. -export type { WorkspaceMember } from './model/workspace-member.types'; +export type { + WorkspaceMember, + WorkspaceMemberRole, + WorkspaceMemberStatus, +} from './model/workspace-member.types'; export { mockCurrentWorkspaceMember } from './model/mock-current-workspace-member'; export { getMockWorkspaceMembersByWorkspaceId, mockWorkspaceMembers, } from './model/mock-workspace-members'; +export { + WORKSPACE_MEMBER_ROLE_META, + WORKSPACE_MEMBER_STATUS_META, + type BadgeTone, +} from './config/labels'; diff --git a/src/entities/workspace-member/model/mock-current-workspace-member.ts b/src/entities/workspace-member/model/mock-current-workspace-member.ts index 1908b7b..691a45e 100644 --- a/src/entities/workspace-member/model/mock-current-workspace-member.ts +++ b/src/entities/workspace-member/model/mock-current-workspace-member.ts @@ -6,5 +6,7 @@ export const mockCurrentWorkspaceMember: WorkspaceMember = { userId: 'user-1', workspaceNickname: '김민서', avatarLabel: '김', + email: 'minseo@example.com', role: 'owner', + status: 'joined', }; diff --git a/src/entities/workspace-member/model/mock-workspace-members.ts b/src/entities/workspace-member/model/mock-workspace-members.ts index fd0de3e..35ebf39 100644 --- a/src/entities/workspace-member/model/mock-workspace-members.ts +++ b/src/entities/workspace-member/model/mock-workspace-members.ts @@ -7,70 +7,144 @@ export const mockWorkspaceMembers: WorkspaceMember[] = [ userId: 'user-1', workspaceNickname: '김지은', avatarLabel: '김', + email: 'jieun@example.com', role: 'owner', + status: 'joined', }, { workspaceId: 'test', userId: 'user-2', workspaceNickname: '박서준', avatarLabel: '박', + email: 'seojun@example.com', role: 'member', + status: 'joined', }, { workspaceId: 'test', userId: 'user-3', workspaceNickname: '이하은', avatarLabel: '이', + email: 'haeun@example.com', role: 'member', + status: 'joined', }, { workspaceId: 'test', userId: 'user-4', workspaceNickname: '최민준', avatarLabel: '최', + email: 'minjun@example.com', role: 'member', + status: 'invited', }, { workspaceId: 'store-workspace', userId: 'user-1', workspaceNickname: '김민서', avatarLabel: '김', + email: 'minseo@example.com', role: 'owner', + status: 'joined', }, { workspaceId: 'store-workspace', userId: 'user-2', workspaceNickname: '이준혁', avatarLabel: '이', + email: 'junhyeok@example.com', role: 'member', + status: 'joined', }, { workspaceId: 'store-workspace', userId: 'user-3', workspaceNickname: '박소연', avatarLabel: '박', + email: 'soyeon@example.com', role: 'member', + status: 'invited', }, { workspaceId: 'store-workspace', userId: 'user-4', workspaceNickname: '최다은', avatarLabel: '최', + email: 'daeun@example.com', role: 'member', + status: 'joined', }, { workspaceId: 'store-workspace', userId: 'user-5', workspaceNickname: '정우진', avatarLabel: '정', + email: 'woojin@example.com', role: 'member', + status: 'joined', }, { workspaceId: 'store-workspace', userId: 'user-6', workspaceNickname: '이서영', avatarLabel: '이', + email: 'seoyeong@example.com', role: 'member', + status: 'invited', + }, + { + workspaceId: 'team-workspace', + userId: 'user-1', + workspaceNickname: '김민서', + avatarLabel: '김', + email: 'minseo@example.com', + role: 'owner', + status: 'joined', + }, + { + workspaceId: 'team-workspace', + userId: 'user-2', + workspaceNickname: '이준혁', + avatarLabel: '이', + email: 'junhyeok@example.com', + role: 'member', + status: 'joined', + }, + { + workspaceId: 'team-workspace', + userId: 'user-3', + workspaceNickname: '박소연', + avatarLabel: '박', + email: 'soyeon@example.com', + role: 'member', + status: 'invited', + }, + { + workspaceId: 'side-workspace', + userId: 'user-1', + workspaceNickname: '김민서', + avatarLabel: '김', + email: 'minseo@example.com', + role: 'owner', + status: 'joined', + }, + { + workspaceId: 'side-workspace', + userId: 'user-2', + workspaceNickname: '정우진', + avatarLabel: '정', + email: 'woojin@example.com', + role: 'member', + status: 'joined', + }, + { + workspaceId: 'side-workspace', + userId: 'user-3', + workspaceNickname: '이서영', + avatarLabel: '이', + email: 'seoyeong@example.com', + role: 'member', + status: 'invited', }, ]; diff --git a/src/entities/workspace-member/model/workspace-member.types.ts b/src/entities/workspace-member/model/workspace-member.types.ts index cff1994..484afac 100644 --- a/src/entities/workspace-member/model/workspace-member.types.ts +++ b/src/entities/workspace-member/model/workspace-member.types.ts @@ -1,8 +1,15 @@ // Supabase 데이터가 연결되기 전까지 사용하는 목업 일정용 워크스페이스 멤버 형태입니다. +export type WorkspaceMemberRole = 'owner' | 'member'; + +// 워크스페이스 참여 상태: 초대를 수락해 참여 중이거나(joined), 초대만 발송된 상태(invited)입니다. +export type WorkspaceMemberStatus = 'joined' | 'invited'; + export interface WorkspaceMember { workspaceId: string; userId: string; workspaceNickname: string; avatarLabel: string; - role: 'owner' | 'member'; + email: string; + role: WorkspaceMemberRole; + status: WorkspaceMemberStatus; } diff --git a/src/entities/workspace/model/mock-workspace.ts b/src/entities/workspace/model/mock-workspace.ts index 73d66c8..ae70db6 100644 --- a/src/entities/workspace/model/mock-workspace.ts +++ b/src/entities/workspace/model/mock-workspace.ts @@ -7,16 +7,19 @@ const mockWorkspacesById: Record = { id: 'store-workspace', name: '카페 그레이 운영', purpose: 'store-operation', + description: '카페 그레이 매장 운영을 위한 워크스페이스입니다.', }, 'team-workspace': { id: 'team-workspace', name: '캡스톤 디자인 팀', purpose: 'team-project', + description: '캡스톤 디자인 프로젝트 협업 공간입니다.', }, 'side-workspace': { id: 'side-workspace', name: 'Fitto 앱 개발팀', purpose: 'side-project', + description: 'Fitto 앱 사이드 프로젝트 개발팀 워크스페이스입니다.', }, }; diff --git a/src/entities/workspace/model/workspace.types.ts b/src/entities/workspace/model/workspace.types.ts index 0e2b61a..d3a11a1 100644 --- a/src/entities/workspace/model/workspace.types.ts +++ b/src/entities/workspace/model/workspace.types.ts @@ -5,6 +5,8 @@ export interface Workspace { id: string; name: string; purpose: WorkspacePurpose; + // 설정 페이지에서 편집하는 워크스페이스 소개 문구입니다. (선택 값) + description?: string; } // get_my_workspaces RPC 반환 형태 (내 워크스페이스 목록) diff --git a/src/features/dashboard/edit-layout/model/useDashboardLayout.ts b/src/features/dashboard/edit-layout/model/useDashboardLayout.ts index a124a3d..40d65dd 100644 --- a/src/features/dashboard/edit-layout/model/useDashboardLayout.ts +++ b/src/features/dashboard/edit-layout/model/useDashboardLayout.ts @@ -38,13 +38,10 @@ export function useDashboardLayout({ void saveDashboardLayout(workspaceId, pageType, { layout }); }, [layout, workspaceId, pageType]); - const handleLayoutChange = useCallback( - (next: Layout) => { - const positions = next.map(toPosition); - setLayout(positions); - }, - [], - ); + const handleLayoutChange = useCallback((next: Layout) => { + const positions = next.map(toPosition); + setLayout(positions); + }, []); const addWidget = useCallback( (item: LayoutItem) => diff --git a/src/features/manage-meeting-notes/ui/MeetingNoteForm.tsx b/src/features/manage-meeting-notes/ui/MeetingNoteForm.tsx index bb74ee5..35844ab 100644 --- a/src/features/manage-meeting-notes/ui/MeetingNoteForm.tsx +++ b/src/features/manage-meeting-notes/ui/MeetingNoteForm.tsx @@ -272,7 +272,7 @@ export function MeetingNoteForm({ workspaceId }: MeetingNoteFormProps) {
    목록으로 @@ -282,11 +282,11 @@ export function MeetingNoteForm({ workspaceId }: MeetingNoteFormProps) { onSubmit={handleSubmit} className="mt-[21px] rounded-[32px] border border-[#eceffa] bg-white px-[26.5px] pt-[26.5px] pb-[28px] shadow-[0_20px_48px_rgba(91,78,232,0.08)]" > -

    새 회의록

    +

    새 회의록