From c72c4dc0e1b54b87762172b0c2a1d55217b0d690 Mon Sep 17 00:00:00 2001 From: djeon Date: Wed, 15 Oct 2025 21:36:46 +0900 Subject: [PATCH] =?UTF-8?q?=EC=86=94=EB=A3=A8=EC=85=98=20=ED=9B=84?= =?UTF-8?q?=EB=B3=B4=20=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20Claude=20=EC=84=A4=EC=A0=95=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 회의록 작성 개선을 위한 솔루션 후보 5개 작성 - 각 솔루션별 구현 방법 및 검증 방안 포함 - Claude Code 로컬 설정 파일 업데이트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- design/uiux/prototype/01-로그인.html | 307 ++++++++ design/uiux/prototype/02-대시보드.html | 539 ++++++++++++++ design/uiux/prototype/03-회의예약.html | 699 ++++++++++++++++++ design/uiux/prototype/04-템플릿선택.html | 537 ++++++++++++++ design/uiux/prototype/05-회의진행.html | 858 +++++++++++++++++++++++ design/uiux/prototype/06-검증완료.html | 499 +++++++++++++ design/uiux/prototype/07-회의종료.html | 573 +++++++++++++++ design/uiux/prototype/08-회의록공유.html | 610 ++++++++++++++++ design/uiux/prototype/09-Todo관리.html | 749 ++++++++++++++++++++ design/uiux/prototype/common.css | 596 ++++++++++++++++ design/uiux/prototype/common.js | 408 +++++++++++ 11 files changed, 6375 insertions(+) create mode 100644 design/uiux/prototype/01-로그인.html create mode 100644 design/uiux/prototype/02-대시보드.html create mode 100644 design/uiux/prototype/03-회의예약.html create mode 100644 design/uiux/prototype/04-템플릿선택.html create mode 100644 design/uiux/prototype/05-회의진행.html create mode 100644 design/uiux/prototype/06-검증완료.html create mode 100644 design/uiux/prototype/07-회의종료.html create mode 100644 design/uiux/prototype/08-회의록공유.html create mode 100644 design/uiux/prototype/09-Todo관리.html create mode 100644 design/uiux/prototype/common.css create mode 100644 design/uiux/prototype/common.js diff --git a/design/uiux/prototype/01-로그인.html b/design/uiux/prototype/01-로그인.html new file mode 100644 index 0000000..774b43b --- /dev/null +++ b/design/uiux/prototype/01-로그인.html @@ -0,0 +1,307 @@ + + + + + + 로그인 - 회의록 작성 서비스 + + + + + + + + + + diff --git a/design/uiux/prototype/02-대시보드.html b/design/uiux/prototype/02-대시보드.html new file mode 100644 index 0000000..3b263d1 --- /dev/null +++ b/design/uiux/prototype/02-대시보드.html @@ -0,0 +1,539 @@ + + + + + + 대시보드 - 회의록 작성 서비스 + + + + +
+ + + + +
+ +
+
+ +
+ +
+
👤
+ 사용자 +
+
+
+
+ + +
+
+

안녕하세요!

+

오늘도 생산적인 하루 되세요

+
+ +
+ +
+
+

예정된 회의

+ 모두 보기 +
+
+
+ + +
+
+

최근 회의록

+ 모두 보기 +
+
+
+ + +
+
+

내가 할 일

+ 모두 보기 +
+
+
+ + +
+
+

이번 주 통계

+
+
+
+
0
+
회의
+
+
+
0
+
완료 Todo
+
+
+
+
+
+
+ + + +
+ + + + + diff --git a/design/uiux/prototype/03-회의예약.html b/design/uiux/prototype/03-회의예약.html new file mode 100644 index 0000000..ece90a9 --- /dev/null +++ b/design/uiux/prototype/03-회의예약.html @@ -0,0 +1,699 @@ + + + + + + 회의 예약 - 회의록 작성 및 공유 서비스 + + + + +
+ +
+
+
+ 회의 예약 +
+ +
+
2
+ 템플릿 선택 +
+ +
+
3
+ 회의 진행 +
+
+ +
+ +
+

회의 예약

+

+ 회의 정보를 입력하고 참석자를 초대하세요 +

+ +
+ +
+ + + +
+ + +
+
+ + + +
+ +
+ + +
+
+ +
+
+ + + +
+ +
+ +
+ 예상 소요: - +
+
+
+ + +
+ + + +
+ + +
+ + +
+ + +
+ + + +
+ +
+ + +
+ + +
+ + +
+ +
+
+ + +
+
+ + +
+
+
+ + +
+ + + + +
+ + +
+ +
+
+ + +
+
+ + +
+
+
+ + +
+ +
+ + +
+
+
+
+ + +
+
+
회의 미리보기
+ +
+
회의 제목
+
-
+
+ +
+
날짜 및 시간
+
-
+
+ +
+
장소
+
-
+
+ +
+
참석자
+
-
+
+ +
+
회의 설명
+
-
+
+
+
+
+
+ + + + + diff --git a/design/uiux/prototype/04-템플릿선택.html b/design/uiux/prototype/04-템플릿선택.html new file mode 100644 index 0000000..66c4a65 --- /dev/null +++ b/design/uiux/prototype/04-템플릿선택.html @@ -0,0 +1,537 @@ + + + + + + 템플릿 선택 - 회의록 작성 및 공유 서비스 + + + + +
+ +
+
+
+ 회의 예약 +
+ +
+
2
+ 템플릿 선택 +
+ +
+
3
+ 회의 진행 +
+
+ +

회의 템플릿 선택

+

+ 회의 유형에 맞는 템플릿을 선택하거나 커스터마이징하세요 +

+ +
+ +
+
+
+ + +
+
템플릿 미리보기
+ +
+ 템플릿을 선택하면
여기에 미리보기가 표시됩니다 +
+ + +
+
+ + +
+ +
+ + +
+
+
+ + + + + diff --git a/design/uiux/prototype/05-회의진행.html b/design/uiux/prototype/05-회의진행.html new file mode 100644 index 0000000..4d7708e --- /dev/null +++ b/design/uiux/prototype/05-회의진행.html @@ -0,0 +1,858 @@ + + + + + + 회의 진행 - 회의록 작성 및 공유 서비스 + + + + +
+ +
+
+
2025년 1분기 전략 회의
+
2025년 10월 20일 14:00
+ +
+ ⏱️ + 00:00:00 +
+ +
+ + 녹음 중 +
+
+ +
+
참석자 (3명)
+
+
+ +
+
타임라인
+
+
+
+ + +
+ +
+
+ + + + +
+
+ + +
+
+ + + +
+
+ + +
+ + +
+
+ + 저장됨 +
+ +
+
+
+
+
+
+
+ 음성 인식 중... +
+ +
+ + +
+
+
+ + +
+
+ + + +
+ +
+ +
+

+ AI가 자동으로 회의록을 작성하고 제안합니다 +

+
+
+ + + + + + +
+
+
+ + + + + diff --git a/design/uiux/prototype/06-검증완료.html b/design/uiux/prototype/06-검증완료.html new file mode 100644 index 0000000..87b242f --- /dev/null +++ b/design/uiux/prototype/06-검증완료.html @@ -0,0 +1,499 @@ + + + + + + 검증 완료 - 회의록 작성 및 공유 서비스 + + + + +
+
+
+ + + + +
0%
+
+

섹션 검증

+

+ 각 섹션을 확인하고 검증을 완료하세요 +

+
+ +
+ +
+

검증 항목

+
+
+ + +
+

검증 현황

+ +
+
전체 진행률
+
0%
+
+ +
+
검증 완료
+
+ 0 / 0 섹션 +
+
+ +
+
잠금 섹션
+
0
+
+ +
+
참석자별 진행률
+
+
+
+
+ + +
+ + +
+
+ + + + + diff --git a/design/uiux/prototype/07-회의종료.html b/design/uiux/prototype/07-회의종료.html new file mode 100644 index 0000000..8b988de --- /dev/null +++ b/design/uiux/prototype/07-회의종료.html @@ -0,0 +1,573 @@ + + + + + + 회의 종료 - 회의록 작성 및 공유 서비스 + + + + +
+
+
🎉
+

회의가 종료되었습니다

+

+ 회의 통계와 Todo를 확인하고 최종 확정하세요 +

+
+ + +
+
+
⏱️
+
-
+
회의 총 시간
+
+ +
+
👥
+
-
+
참석자 수
+
+ +
+
💬
+
-
+
총 발언 횟수
+
+ +
+
+
-
+
생성된 Todo
+
+
+ + +
+ +
+
+ 📋 + 필수 항목 검사 +
+
+
+ + +
+
+ + AI Todo 추출 결과 +
+
+ +
+ + +
+
+ 📊 + 화자별 발언 통계 +
+
+
+ + +
+
+ 🔑 + 주요 키워드 +
+
+ + +
+
🗓️ AI가 감지한 다음 회의
+
2025년 10월 27일 14:00
+ +
+
+
+ + +
+ +
+ + +
+
+
+ + + + + + + + diff --git a/design/uiux/prototype/08-회의록공유.html b/design/uiux/prototype/08-회의록공유.html new file mode 100644 index 0000000..f7c635c --- /dev/null +++ b/design/uiux/prototype/08-회의록공유.html @@ -0,0 +1,610 @@ + + + + + + 회의록 공유 - 회의록 작성 및 공유 서비스 + + + + + + + + + + diff --git a/design/uiux/prototype/09-Todo관리.html b/design/uiux/prototype/09-Todo관리.html new file mode 100644 index 0000000..04069b8 --- /dev/null +++ b/design/uiux/prototype/09-Todo관리.html @@ -0,0 +1,749 @@ + + + + + + Todo 관리 - 회의록 작성 및 공유 서비스 + + + + +
+ +
+
+

Todo 관리

+

회의에서 생성된 Todo를 추적하고 관리하세요

+
+ +
+ + +
+
+ + +
+
+ 담당자: + +
+ +
+ 상태: + +
+ +
+ 우선순위: + +
+ +
+ 정렬: + +
+
+ + +
+
+
+
시작 전
+
0
+
+
+
+ +
+
+
진행 중
+
0
+
+
+
+ +
+
+
완료
+
0
+
+
+
+
+ + +
+ + + + + + + + + + + + + + +
+ + 상태내용담당자마감일우선순위진행률액션
+
+
+ + + + + + + diff --git a/design/uiux/prototype/common.css b/design/uiux/prototype/common.css new file mode 100644 index 0000000..5cc0a0e --- /dev/null +++ b/design/uiux/prototype/common.css @@ -0,0 +1,596 @@ +/* ============================================ + 회의록 작성 및 공유 개선 서비스 - 공통 스타일 + ============================================ */ + +/* === CSS Reset === */ +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + font-size: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font-family: 'Pretendard', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Apple SD Gothic Neo', sans-serif; + line-height: 1.5; + color: #4B5563; + background-color: #F9FAFB; +} + +/* === CSS Variables === */ +:root { + /* Primary Colors */ + --primary-light: #4DFFDB; + --primary-main: #00D9B1; + --primary-dark: #00A88A; + + /* Secondary Colors */ + --secondary-light: #A5B4FC; + --secondary-main: #6366F1; + --secondary-dark: #4F46E5; + + /* Semantic Colors */ + --success-light: #6EE7B7; + --success-main: #10B981; + --success-dark: #059669; + + --warning-light: #FCD34D; + --warning-main: #F59E0B; + --warning-dark: #D97706; + + --error-light: #FCA5A5; + --error-main: #EF4444; + --error-dark: #DC2626; + + --info-light: #93C5FD; + --info-main: #3B82F6; + --info-dark: #2563EB; + + /* Neutral Colors */ + --gray-50: #F9FAFB; + --gray-100: #F3F4F6; + --gray-200: #E5E7EB; + --gray-300: #D1D5DB; + --gray-400: #9CA3AF; + --gray-500: #6B7280; + --gray-600: #4B5563; + --gray-700: #374151; + --gray-800: #1F2937; + --gray-900: #111827; + + /* Typography */ + --font-display: 700 3rem/1.25 'Pretendard'; + --font-h1: 700 2.25rem/1.25 'Pretendard'; + --font-h2: 600 1.875rem/1.25 'Pretendard'; + --font-h3: 600 1.5rem/1.5 'Pretendard'; + --font-h4: 600 1.25rem/1.5 'Pretendard'; + --font-body-large: 400 1.125rem/1.5 'Pretendard'; + --font-body: 400 1rem/1.5 'Pretendard'; + --font-body-small: 400 0.875rem/1.5 'Pretendard'; + --font-caption: 400 0.75rem/1.5 'Pretendard'; + + /* Spacing (8px 기반) */ + --spacing-1: 4px; + --spacing-2: 8px; + --spacing-3: 12px; + --spacing-4: 16px; + --spacing-5: 20px; + --spacing-6: 24px; + --spacing-8: 32px; + --spacing-10: 40px; + --spacing-12: 48px; + --spacing-16: 64px; + + /* Border Radius */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-full: 9999px; + + /* Shadows */ + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15); + --shadow-lg: 0 20px 25px rgba(0, 0, 0, 0.15); + + /* Transitions */ + --transition-fast: 150ms ease-out; + --transition-base: 200ms ease-out; + --transition-slow: 300ms ease-out; +} + +/* === Typography === */ +h1 { font: var(--font-h1); color: var(--gray-900); } +h2 { font: var(--font-h2); color: var(--gray-900); } +h3 { font: var(--font-h3); color: var(--gray-900); } +h4 { font: var(--font-h4); color: var(--gray-900); } +p { font: var(--font-body); margin-bottom: var(--spacing-4); } +small { font: var(--font-caption); color: var(--gray-500); } + +/* === Buttons === */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: var(--spacing-3) var(--spacing-6); + font-size: 1rem; + font-weight: 500; + border: none; + border-radius: var(--radius-md); + cursor: pointer; + transition: all var(--transition-fast); + text-decoration: none; + gap: var(--spacing-2); +} + +.btn-primary { + background-color: var(--primary-main); + color: white; +} + +.btn-primary:hover { + background-color: var(--primary-light); + transform: translateY(-1px); + box-shadow: var(--shadow-sm); +} + +.btn-primary:active { + background-color: var(--primary-dark); + transform: scale(0.98); +} + +.btn-primary:disabled { + background-color: var(--gray-300); + color: var(--gray-500); + cursor: not-allowed; + transform: none; +} + +.btn-secondary { + background-color: transparent; + color: var(--primary-main); + border: 1px solid var(--primary-main); +} + +.btn-secondary:hover { + background-color: rgba(0, 217, 177, 0.1); +} + +.btn-secondary:active { + background-color: rgba(0, 217, 177, 0.2); +} + +.btn-text { + background-color: transparent; + color: var(--gray-700); +} + +.btn-text:hover { + background-color: var(--gray-100); +} + +.btn-text:active { + background-color: var(--gray-200); +} + +.btn-icon { + width: 40px; + height: 40px; + padding: 0; + border-radius: var(--radius-full); + background-color: transparent; +} + +.btn-icon:hover { + background-color: var(--gray-100); +} + +.btn-sm { + padding: var(--spacing-2) var(--spacing-4); + font-size: 0.875rem; +} + +.btn-lg { + padding: var(--spacing-4) var(--spacing-8); + font-size: 1.125rem; +} + +/* FAB (Floating Action Button) */ +.fab { + position: fixed; + bottom: var(--spacing-4); + right: var(--spacing-4); + width: 56px; + height: 56px; + background-color: var(--primary-main); + color: white; + border-radius: var(--radius-full); + border: none; + box-shadow: var(--shadow-md); + cursor: pointer; + transition: all var(--transition-base); + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + z-index: 1000; +} + +.fab:hover { + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); + transform: translateY(-2px); +} + +.fab:active { + transform: scale(0.95); +} + +/* === Input Fields === */ +.input, +.textarea, +.select { + width: 100%; + padding: var(--spacing-3) var(--spacing-4); + font-size: 1rem; + font-family: inherit; + border: 1px solid var(--gray-300); + border-radius: var(--radius-md); + background-color: white; + transition: all var(--transition-fast); +} + +.input:focus, +.textarea:focus, +.select:focus { + outline: 4px solid rgba(0, 217, 177, 0.2); + border-color: var(--primary-main); + border-width: 2px; +} + +.input.error, +.textarea.error { + border-color: var(--error-main); +} + +.input:disabled, +.textarea:disabled, +.select:disabled { + background-color: var(--gray-100); + color: var(--gray-400); + cursor: not-allowed; +} + +.textarea { + min-height: 120px; + resize: vertical; +} + +.input-group { + margin-bottom: var(--spacing-4); +} + +.input-label { + display: block; + margin-bottom: var(--spacing-2); + font-weight: 500; + color: var(--gray-700); +} + +.input-error { + display: block; + margin-top: var(--spacing-1); + font-size: 0.875rem; + color: var(--error-main); +} + +/* === Cards === */ +.card { + background-color: white; + border: 1px solid var(--gray-200); + border-radius: var(--radius-lg); + padding: var(--spacing-6); + box-shadow: var(--shadow-sm); + transition: all var(--transition-base); +} + +.card.interactive { + cursor: pointer; +} + +.card.interactive:hover { + box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +.card.interactive:active { + box-shadow: var(--shadow-sm); + transform: scale(0.99); +} + +/* === Badges === */ +.badge { + display: inline-flex; + align-items: center; + padding: var(--spacing-1) var(--spacing-3); + font-size: 0.75rem; + font-weight: 500; + border-radius: var(--radius-full); +} + +.badge-primary { + background-color: var(--primary-light); + color: var(--primary-dark); +} + +.badge-success { + background-color: var(--success-light); + color: var(--success-dark); +} + +.badge-warning { + background-color: var(--warning-light); + color: var(--warning-dark); +} + +.badge-error { + background-color: var(--error-light); + color: var(--error-dark); +} + +.badge-neutral { + background-color: var(--gray-200); + color: var(--gray-700); +} + +/* === Modal === */ +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: none; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.modal-backdrop.active { + display: flex; +} + +.modal { + background-color: white; + border-radius: var(--radius-xl); + padding: var(--spacing-8); + max-width: 600px; + width: 90%; + max-height: 90vh; + overflow-y: auto; + box-shadow: var(--shadow-lg); +} + +.modal-header { + margin-bottom: var(--spacing-6); +} + +.modal-title { + font: var(--font-h3); + color: var(--gray-900); +} + +.modal-body { + margin-bottom: var(--spacing-8); +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: var(--spacing-3); +} + +/* === Toast === */ +.toast-container { + position: fixed; + top: var(--spacing-4); + right: var(--spacing-4); + z-index: 10000; + display: flex; + flex-direction: column; + gap: var(--spacing-3); +} + +.toast { + background-color: white; + border-radius: var(--radius-md); + padding: var(--spacing-4) var(--spacing-5); + max-width: 400px; + box-shadow: var(--shadow-md); + display: flex; + align-items: center; + gap: var(--spacing-3); + border-left: 4px solid; + animation: slideIn 200ms ease-out; +} + +@keyframes slideIn { + from { + transform: translateX(400px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +.toast-success { + border-left-color: var(--success-main); +} + +.toast-error { + border-left-color: var(--error-main); +} + +.toast-warning { + border-left-color: var(--warning-main); +} + +.toast-info { + border-left-color: var(--info-main); +} + +/* === Layout === */ +.container { + max-width: 1536px; + margin: 0 auto; + padding: 0 var(--spacing-6); +} + +.header { + background-color: white; + border-bottom: 1px solid var(--gray-200); + padding: var(--spacing-4) 0; + position: sticky; + top: 0; + z-index: 100; +} + +.header-content { + display: flex; + align-items: center; + justify-content: space-between; +} + +.logo { + font-size: 1.5rem; + font-weight: 700; + color: var(--primary-main); + text-decoration: none; +} + +.sidebar { + position: fixed; + left: 0; + top: 0; + width: 250px; + height: 100vh; + background-color: white; + border-right: 1px solid var(--gray-200); + padding: var(--spacing-6); + overflow-y: auto; +} + +.main-content { + margin-left: 250px; + padding: var(--spacing-8); + min-height: 100vh; +} + +/* === Grid System === */ +.grid { + display: grid; + gap: var(--spacing-4); +} + +.grid-2 { + grid-template-columns: repeat(2, 1fr); +} + +.grid-3 { + grid-template-columns: repeat(3, 1fr); +} + +.grid-4 { + grid-template-columns: repeat(4, 1fr); +} + +/* === Utility Classes === */ +.hidden { + display: none !important; +} + +.text-center { + text-align: center; +} + +.text-right { + text-align: right; +} + +.mt-2 { margin-top: var(--spacing-2); } +.mt-4 { margin-top: var(--spacing-4); } +.mt-6 { margin-top: var(--spacing-6); } +.mt-8 { margin-top: var(--spacing-8); } + +.mb-2 { margin-bottom: var(--spacing-2); } +.mb-4 { margin-bottom: var(--spacing-4); } +.mb-6 { margin-bottom: var(--spacing-6); } +.mb-8 { margin-bottom: var(--spacing-8); } + +.flex { + display: flex; +} + +.flex-col { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-2 { gap: var(--spacing-2); } +.gap-4 { gap: var(--spacing-4); } +.gap-6 { gap: var(--spacing-6); } + +/* === Responsive === */ +@media (max-width: 1023px) { + .sidebar { + transform: translateX(-100%); + transition: transform 300ms ease-out; + } + + .sidebar.active { + transform: translateX(0); + } + + .main-content { + margin-left: 0; + } + + .grid-3 { + grid-template-columns: repeat(2, 1fr); + } + + .grid-4 { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 767px) { + .container { + padding: 0 var(--spacing-4); + } + + .grid-2, + .grid-3, + .grid-4 { + grid-template-columns: 1fr; + } + + .modal { + width: 95%; + padding: var(--spacing-6); + } +} diff --git a/design/uiux/prototype/common.js b/design/uiux/prototype/common.js new file mode 100644 index 0000000..fab26d5 --- /dev/null +++ b/design/uiux/prototype/common.js @@ -0,0 +1,408 @@ +/* ============================================ + 회의록 작성 및 공유 개선 서비스 - 공통 Javascript + ============================================ */ + +// === LocalStorage 키 상수 === +const STORAGE_KEYS = { + CURRENT_USER: 'currentUser', + USERS: 'users', + MEETINGS: 'meetings', + TODOS: 'todos', + INITIALIZED: 'initialized' +}; + +// === 예제 데이터 초기화 === +function initializeData() { + if (localStorage.getItem(STORAGE_KEYS.INITIALIZED)) { + return; + } + + // 사용자 데이터 + const users = [ + { id: 'user1', name: '김민준', email: 'minjun@example.com', avatar: '👤' }, + { id: 'user2', name: '박서연', email: 'seoyeon@example.com', avatar: '👩' }, + { id: 'user3', name: '이준호', email: 'junho@example.com', avatar: '👨' }, + { id: 'user4', name: '최유진', email: 'yujin@example.com', avatar: '👧' }, + { id: 'user5', name: '정도현', email: 'dohyun@example.com', avatar: '🧑' } + ]; + + // 회의 데이터 + const meetings = [ + { + id: 'meeting1', + title: '2025년 1분기 전략 회의', + date: '2025-10-20', + time: '14:00', + endTime: '15:30', + location: '3층 회의실', + participants: ['user1', 'user2', 'user3'], + template: 'general', + status: 'scheduled', // scheduled, in_progress, completed + content: { + sections: { + participants: '김민준, 박서연, 이준호', + agenda: '1분기 목표 설정 및 전략 수립', + discussion: '', + decisions: '', + todos: '' + } + }, + createdAt: new Date().toISOString() + }, + { + id: 'meeting2', + title: '주간 스크럼 회의', + date: '2025-10-18', + time: '10:00', + endTime: '10:30', + location: '온라인', + participants: ['user1', 'user2', 'user3', 'user4'], + template: 'scrum', + status: 'completed', + content: { + sections: { + participants: '김민준, 박서연, 이준호, 최유진', + yesterday: '- API 개발 완료\n- 테스트 코드 작성', + today: '- 프론트엔드 개발 시작\n- 디자인 리뷰', + blockers: '없음' + } + }, + createdAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString() + } + ]; + + // Todo 데이터 + const todos = [ + { + id: 'todo1', + meetingId: 'meeting1', + content: '1분기 마케팅 예산안 작성', + assignee: 'user2', + dueDate: '2025-10-25', + priority: 'high', // high, normal, low + status: 'pending', // pending, in_progress, completed + progress: 0 + }, + { + id: 'todo2', + meetingId: 'meeting1', + content: '경쟁사 분석 보고서 작성', + assignee: 'user3', + dueDate: '2025-10-22', + priority: 'high', + status: 'in_progress', + progress: 60 + }, + { + id: 'todo3', + meetingId: 'meeting2', + content: '테스트 커버리지 80% 달성', + assignee: 'user3', + dueDate: '2025-10-20', + priority: 'normal', + status: 'completed', + progress: 100 + } + ]; + + // 현재 로그인 사용자 + const currentUser = users[0]; + + // LocalStorage에 저장 + localStorage.setItem(STORAGE_KEYS.USERS, JSON.stringify(users)); + localStorage.setItem(STORAGE_KEYS.MEETINGS, JSON.stringify(meetings)); + localStorage.setItem(STORAGE_KEYS.TODOS, JSON.stringify(todos)); + localStorage.setItem(STORAGE_KEYS.CURRENT_USER, JSON.stringify(currentUser)); + localStorage.setItem(STORAGE_KEYS.INITIALIZED, 'true'); +} + +// === LocalStorage 헬퍼 함수 === +function getFromStorage(key) { + const data = localStorage.getItem(key); + return data ? JSON.parse(data) : null; +} + +function saveToStorage(key, data) { + localStorage.setItem(key, JSON.stringify(data)); +} + +// === 날짜/시간 유틸리티 === +function formatDate(dateString) { + const date = new Date(dateString); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +} + +function formatTime(timeString) { + return timeString; // HH:MM 형식 그대로 반환 +} + +function formatDateTime(dateString, timeString) { + return `${formatDate(dateString)} ${formatTime(timeString)}`; +} + +function getDdayText(dueDateString) { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const dueDate = new Date(dueDateString); + dueDate.setHours(0, 0, 0, 0); + + const diff = Math.ceil((dueDate - today) / (1000 * 60 * 60 * 24)); + + if (diff < 0) return `D${diff} (지남)`; + if (diff === 0) return '오늘'; + return `D-${diff}`; +} + +function formatDuration(minutes) { + const hours = Math.floor(minutes / 60); + const mins = minutes % 60; + if (hours > 0 && mins > 0) return `${hours}시간 ${mins}분`; + if (hours > 0) return `${hours}시간`; + return `${mins}분`; +} + +// === 사용자 관련 함수 === +function getCurrentUser() { + return getFromStorage(STORAGE_KEYS.CURRENT_USER); +} + +function getUserById(userId) { + const users = getFromStorage(STORAGE_KEYS.USERS) || []; + return users.find(u => u.id === userId); +} + +function getUserName(userId) { + const user = getUserById(userId); + return user ? user.name : '알 수 없음'; +} + +// === 회의 관련 함수 === +function getAllMeetings() { + return getFromStorage(STORAGE_KEYS.MEETINGS) || []; +} + +function getMeetingById(meetingId) { + const meetings = getAllMeetings(); + return meetings.find(m => m.id === meetingId); +} + +function saveMeeting(meeting) { + const meetings = getAllMeetings(); + const index = meetings.findIndex(m => m.id === meeting.id); + if (index >= 0) { + meetings[index] = meeting; + } else { + meetings.push(meeting); + } + saveToStorage(STORAGE_KEYS.MEETINGS, meetings); +} + +function getScheduledMeetings() { + const meetings = getAllMeetings(); + return meetings.filter(m => m.status === 'scheduled'); +} + +function getRecentMeetings(limit = 6) { + const meetings = getAllMeetings(); + return meetings + .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)) + .slice(0, limit); +} + +// === Todo 관련 함수 === +function getAllTodos() { + return getFromStorage(STORAGE_KEYS.TODOS) || []; +} + +function getTodoById(todoId) { + const todos = getAllTodos(); + return todos.find(t => t.id === todoId); +} + +function saveTodo(todo) { + const todos = getAllTodos(); + const index = todos.findIndex(t => t.id === todo.id); + if (index >= 0) { + todos[index] = todo; + } else { + todos.push(todo); + } + saveToStorage(STORAGE_KEYS.TODOS, todos); +} + +function getPendingTodos(userId) { + const todos = getAllTodos(); + return todos.filter(t => t.assignee === userId && t.status !== 'completed'); +} + +function getTodosByStatus(status) { + const todos = getAllTodos(); + return todos.filter(t => t.status === status); +} + +// === Toast 알림 === +function showToast(message, type = 'info') { + // Toast 컨테이너 생성 (없으면) + let container = document.querySelector('.toast-container'); + if (!container) { + container = document.createElement('div'); + container.className = 'toast-container'; + document.body.appendChild(container); + } + + // Toast 생성 + const toast = document.createElement('div'); + toast.className = `toast toast-${type}`; + + const icons = { + success: '✓', + error: '✗', + warning: '⚠', + info: 'ℹ' + }; + + toast.innerHTML = ` + ${icons[type]} + ${message} + `; + + container.appendChild(toast); + + // 4초 후 자동 제거 + setTimeout(() => { + toast.style.animation = 'slideOut 150ms ease-in'; + setTimeout(() => toast.remove(), 150); + }, 4000); +} + +// slideOut 애니메이션 추가 +if (!document.querySelector('style[data-toast-style]')) { + const style = document.createElement('style'); + style.setAttribute('data-toast-style', 'true'); + style.textContent = ` + @keyframes slideOut { + to { + transform: translateX(400px); + opacity: 0; + } + } + `; + document.head.appendChild(style); +} + +// === 모달 제어 === +function openModal(modalId) { + const modal = document.getElementById(modalId); + if (modal) { + modal.classList.add('active'); + } +} + +function closeModal(modalId) { + const modal = document.getElementById(modalId); + if (modal) { + modal.classList.remove('active'); + } +} + +// 모달 백드롭 클릭 시 닫기 +document.addEventListener('click', (e) => { + if (e.target.classList.contains('modal-backdrop')) { + closeModal(e.target.id); + } +}); + +// === 화면 전환 === +function navigateTo(page) { + window.location.href = page; +} + +function goBack() { + window.history.back(); +} + +// === 유틸리티 함수 === +function generateId(prefix = 'id') { + return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; +} + +function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; +} + +// === 템플릿 데이터 === +const TEMPLATES = { + general: { + id: 'general', + name: '일반 회의', + description: '기본 회의 구조', + sections: [ + { id: 'participants', name: '참석자', required: true }, + { id: 'agenda', name: '회의 목적', required: false }, + { id: 'discussion', name: '논의 내용', required: true }, + { id: 'decisions', name: '결정 사항', required: true }, + { id: 'todos', name: 'Todo', required: false } + ] + }, + scrum: { + id: 'scrum', + name: '스크럼 회의', + description: '데일리 스탠드업', + sections: [ + { id: 'participants', name: '참석자', required: true }, + { id: 'yesterday', name: '어제 한 일', required: true }, + { id: 'today', name: '오늘 할 일', required: true }, + { id: 'blockers', name: '이슈/블로커', required: false } + ] + }, + kickoff: { + id: 'kickoff', + name: '프로젝트 킥오프', + description: '프로젝트 시작 회의', + sections: [ + { id: 'participants', name: '참석자', required: true }, + { id: 'overview', name: '프로젝트 개요', required: true }, + { id: 'goals', name: '목표', required: true }, + { id: 'schedule', name: '일정', required: true }, + { id: 'roles', name: '역할 분담', required: true }, + { id: 'risks', name: '리스크', required: false } + ] + }, + weekly: { + id: 'weekly', + name: '주간 회의', + description: '주간 진행 상황 리뷰', + sections: [ + { id: 'participants', name: '참석자', required: true }, + { id: 'achievements', name: '주간 실적', required: true }, + { id: 'issues', name: '주요 이슈', required: false }, + { id: 'nextWeek', name: '다음 주 계획', required: true }, + { id: 'support', name: '지원 필요 사항', required: false } + ] + } +}; + +function getTemplate(templateId) { + return TEMPLATES[templateId]; +} + +function getAllTemplates() { + return Object.values(TEMPLATES); +} + +// === 페이지 로드 시 데이터 초기화 === +document.addEventListener('DOMContentLoaded', () => { + initializeData(); +});