mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 09:06:24 +00:00
프로토타입 회의록 대시보드 수정 및 UI/UX 설계 문서 적용
This commit is contained in:
parent
75e7146877
commit
a68340735b
@ -289,6 +289,10 @@
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.meeting-card.ongoing {
|
||||
border-left: 4px solid var(--color-error-main);
|
||||
}
|
||||
|
||||
.meeting-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@ -296,6 +300,79 @@
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.meeting-badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
|
||||
.badge-ongoing {
|
||||
background-color: var(--color-error-main);
|
||||
color: var(--color-white);
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.6; }
|
||||
}
|
||||
|
||||
.role-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-1);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
.crown-icon {
|
||||
color: var(--color-warning-main);
|
||||
}
|
||||
|
||||
.meeting-actions {
|
||||
display: flex;
|
||||
gap: var(--spacing-2);
|
||||
margin-top: var(--spacing-3);
|
||||
}
|
||||
|
||||
.btn-join {
|
||||
flex: 1;
|
||||
padding: var(--spacing-2) var(--spacing-4);
|
||||
background-color: var(--color-primary-main);
|
||||
color: var(--color-white);
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-weight: var(--font-weight-medium);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.btn-join:hover {
|
||||
background-color: var(--color-primary-dark);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
padding: var(--spacing-2) var(--spacing-3);
|
||||
background-color: var(--color-white);
|
||||
color: var(--color-primary-main);
|
||||
border: 1px solid var(--color-primary-main);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-body-small);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.btn-edit:hover {
|
||||
background-color: rgba(0, 217, 177, 0.1);
|
||||
}
|
||||
|
||||
.timer-text {
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-gray-500);
|
||||
margin-top: var(--spacing-2);
|
||||
}
|
||||
|
||||
.meeting-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
@ -364,6 +441,151 @@
|
||||
.dday.warning { color: var(--color-warning-main); }
|
||||
.dday.normal { color: var(--color-gray-500); }
|
||||
|
||||
/* Quick Actions */
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
gap: var(--spacing-4);
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
|
||||
.quick-action-btn {
|
||||
flex: 1;
|
||||
padding: var(--spacing-6);
|
||||
background: linear-gradient(135deg, var(--color-primary-light) 0%, var(--color-primary-main) 100%);
|
||||
color: var(--color-white);
|
||||
border: none;
|
||||
border-radius: var(--radius-lg);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-base);
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.quick-action-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.quick-action-btn.secondary {
|
||||
background: var(--color-white);
|
||||
color: var(--color-primary-main);
|
||||
border: 2px solid var(--color-primary-main);
|
||||
}
|
||||
|
||||
.quick-action-btn.secondary:hover {
|
||||
background-color: rgba(0, 217, 177, 0.05);
|
||||
}
|
||||
|
||||
.quick-action-icon {
|
||||
font-size: 32px;
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.quick-action-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.quick-action-desc {
|
||||
font-size: var(--font-size-body-small);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* FAB Action Modal */
|
||||
.fab-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: var(--z-modal-backdrop);
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding: var(--spacing-4);
|
||||
}
|
||||
|
||||
.fab-modal.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.fab-menu {
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
|
||||
padding: var(--spacing-6);
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
animation: slideUp var(--transition-base);
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.fab-menu-title {
|
||||
font-size: var(--font-size-h3);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-6);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fab-menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
padding: var(--spacing-5);
|
||||
background-color: var(--color-white);
|
||||
border: 2px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
margin-bottom: var(--spacing-3);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.fab-menu-item:hover {
|
||||
border-color: var(--color-primary-main);
|
||||
background-color: rgba(0, 217, 177, 0.05);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.fab-menu-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background-color: var(--color-primary-light);
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fab-menu-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.fab-menu-item-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.fab-menu-item-desc {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
/* Bottom Navigation (Mobile) */
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
@ -479,6 +701,20 @@
|
||||
<p class="welcome-subtitle" id="welcomeSubtitle">오늘의 일정을 확인하세요</p>
|
||||
</section>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<section class="quick-actions hide-mobile">
|
||||
<button class="quick-action-btn" onclick="navigateTo('04-템플릿선택.html')">
|
||||
<div class="quick-action-icon">🚀</div>
|
||||
<div class="quick-action-title">새 회의 시작</div>
|
||||
<div class="quick-action-desc">템플릿을 선택하여 회의를 바로 시작합니다</div>
|
||||
</button>
|
||||
<button class="quick-action-btn secondary" onclick="navigateTo('03-회의예약.html')">
|
||||
<div class="quick-action-icon">📅</div>
|
||||
<div class="quick-action-title">회의 예약</div>
|
||||
<div class="quick-action-desc">향후 진행할 회의를 미리 예약합니다</div>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<!-- Stats Grid -->
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card">
|
||||
@ -498,14 +734,25 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Recent Meetings -->
|
||||
<!-- 예정된/진행중 회의 -->
|
||||
<section class="section">
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">최근 회의</h2>
|
||||
<h2 class="section-title">예정된 회의</h2>
|
||||
<a href="12-회의록목록조회.html" class="view-all-link">전체 보기 →</a>
|
||||
</div>
|
||||
<div class="meeting-grid" id="meetingGrid">
|
||||
<!-- Meetings will be rendered here -->
|
||||
<div class="meeting-grid" id="upcomingMeetingGrid">
|
||||
<!-- Upcoming/Ongoing meetings will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 내 회의록 -->
|
||||
<section class="section">
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">내 회의록</h2>
|
||||
<a href="12-회의록목록조회.html" class="view-all-link">전체 보기 →</a>
|
||||
</div>
|
||||
<div class="meeting-grid" id="myMeetingGrid">
|
||||
<!-- My meetings will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -519,6 +766,17 @@
|
||||
<!-- Todos will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Shared Meetings -->
|
||||
<section class="section">
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">공유받은 회의록</h2>
|
||||
<a href="12-회의록목록조회.html" class="view-all-link">전체 보기 →</a>
|
||||
</div>
|
||||
<div class="meeting-grid" id="sharedMeetingGrid">
|
||||
<!-- Shared meetings will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -543,13 +801,198 @@
|
||||
</nav>
|
||||
|
||||
<!-- FAB -->
|
||||
<button class="fab" id="fabButton" title="새 회의 예약">+</button>
|
||||
<button class="fab" id="fabButton" title="새 회의 시작">+</button>
|
||||
|
||||
<!-- FAB Action Modal -->
|
||||
<div class="fab-modal" id="fabModal">
|
||||
<div class="fab-menu">
|
||||
<h3 class="fab-menu-title">무엇을 하시겠습니까?</h3>
|
||||
<div class="fab-menu-item" onclick="navigateTo('04-템플릿선택.html')">
|
||||
<div class="fab-menu-icon">🚀</div>
|
||||
<div class="fab-menu-content">
|
||||
<div class="fab-menu-item-title">새 회의 시작</div>
|
||||
<div class="fab-menu-item-desc">템플릿을 선택하여 회의를 바로 시작합니다</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fab-menu-item" onclick="navigateTo('03-회의예약.html')">
|
||||
<div class="fab-menu-icon">📅</div>
|
||||
<div class="fab-menu-content">
|
||||
<div class="fab-menu-item-title">회의 예약</div>
|
||||
<div class="fab-menu-item-desc">향후 진행할 회의를 미리 예약합니다</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
const { AppState, Storage, Toast, MeetingUtils, formatDateTime, getDday, navigateTo } = window.MeetingApp;
|
||||
|
||||
// 샘플 데이터 초기화
|
||||
function initSampleData() {
|
||||
// 기존 데이터가 없으면 샘플 데이터 생성
|
||||
const currentUserId = 'user-001'; // 현재 사용자 ID (예시)
|
||||
|
||||
if (!Storage.get('meetings') || Storage.get('meetings').length === 0) {
|
||||
const sampleMeetings = [
|
||||
{
|
||||
id: 'meeting-001',
|
||||
title: '긴급 버그 픽스 회의',
|
||||
date: new Date(Date.now() - 1800000).toISOString(), // 30분 전 시작
|
||||
startTime: new Date(Date.now() - 1800000).toISOString(),
|
||||
location: '온라인 (Zoom)',
|
||||
status: 'ongoing',
|
||||
description: '프로덕션 환경 긴급 버그 대응',
|
||||
creatorId: 'user-002',
|
||||
attendees: ['user-001', 'user-002', 'user-003']
|
||||
},
|
||||
{
|
||||
id: 'meeting-002',
|
||||
title: '주간 스프린트 회의',
|
||||
date: new Date(Date.now() + 86400000).toISOString(), // 내일
|
||||
startTime: new Date(Date.now() + 86400000).toISOString(),
|
||||
location: '온라인 (Zoom)',
|
||||
status: 'scheduled',
|
||||
description: '이번 주 스프린트 진행사항 및 다음 주 계획 논의',
|
||||
creatorId: 'user-001', // 현재 사용자가 생성자
|
||||
attendees: ['user-001', 'user-002', 'user-003', 'user-004']
|
||||
},
|
||||
{
|
||||
id: 'meeting-003',
|
||||
title: 'Q4 마케팅 전략 회의',
|
||||
date: new Date(Date.now() - 172800000).toISOString(), // 2일 전
|
||||
startTime: new Date(Date.now() - 172800000).toISOString(),
|
||||
location: '본사 3층 회의실',
|
||||
status: 'completed',
|
||||
description: '4분기 마케팅 캠페인 기획 및 예산 검토',
|
||||
creatorId: 'user-005',
|
||||
attendees: ['user-001', 'user-005']
|
||||
},
|
||||
{
|
||||
id: 'meeting-004',
|
||||
title: '디자인 리뷰',
|
||||
date: new Date(Date.now() + 3600000).toISOString(), // 1시간 후
|
||||
startTime: new Date(Date.now() + 3600000).toISOString(),
|
||||
location: '온라인 (Google Meet)',
|
||||
status: 'scheduled',
|
||||
description: 'UI/UX 디자인 최종 검토 및 피드백',
|
||||
creatorId: 'user-003',
|
||||
attendees: ['user-001', 'user-003', 'user-006']
|
||||
},
|
||||
{
|
||||
id: 'meeting-005',
|
||||
title: '월간 전체 회의',
|
||||
date: new Date(Date.now() + 259200000).toISOString(), // 3일 후
|
||||
startTime: new Date(Date.now() + 259200000).toISOString(),
|
||||
location: '대강당',
|
||||
status: 'scheduled',
|
||||
description: '월간 성과 공유 및 다음 달 목표 설정',
|
||||
creatorId: 'user-001', // 현재 사용자가 생성자
|
||||
attendees: ['user-001', 'user-002', 'user-003', 'user-004', 'user-005']
|
||||
},
|
||||
{
|
||||
id: 'meeting-006',
|
||||
title: '고객 피드백 리뷰',
|
||||
date: new Date(Date.now() - 345600000).toISOString(), // 4일 전
|
||||
startTime: new Date(Date.now() - 345600000).toISOString(),
|
||||
location: '온라인 (Teams)',
|
||||
status: 'completed',
|
||||
description: '최근 수집된 고객 VOC 분석 및 개선 방안 도출',
|
||||
creatorId: 'user-007',
|
||||
attendees: ['user-001', 'user-007']
|
||||
}
|
||||
];
|
||||
Storage.set('meetings', sampleMeetings);
|
||||
Storage.set('currentUserId', currentUserId);
|
||||
}
|
||||
|
||||
if (!Storage.get('todos') || Storage.get('todos').length === 0) {
|
||||
const sampleTodos = [
|
||||
{
|
||||
id: 'todo-001',
|
||||
title: '마케팅 자료 최종 검토 및 승인',
|
||||
assignee: '김민준',
|
||||
dueDate: new Date(Date.now() + 86400000).toISOString(), // 내일
|
||||
priority: 'high',
|
||||
status: 'in_progress',
|
||||
meetingId: 'meeting-002'
|
||||
},
|
||||
{
|
||||
id: 'todo-002',
|
||||
title: '개발 환경 설정 가이드 문서 작성',
|
||||
assignee: '이준호',
|
||||
dueDate: new Date(Date.now() + 172800000).toISOString(), // 2일 후
|
||||
priority: 'medium',
|
||||
status: 'in_progress',
|
||||
meetingId: 'meeting-003'
|
||||
},
|
||||
{
|
||||
id: 'todo-003',
|
||||
title: '고객 설문조사 결과 분석 및 보고서 작성',
|
||||
assignee: '박서연',
|
||||
dueDate: new Date(Date.now() + 432000000).toISOString(), // 5일 후
|
||||
priority: 'high',
|
||||
status: 'pending',
|
||||
meetingId: 'meeting-005'
|
||||
},
|
||||
{
|
||||
id: 'todo-004',
|
||||
title: 'UI/UX 디자인 개선안 3차 리뷰',
|
||||
assignee: '최유진',
|
||||
dueDate: new Date(Date.now() - 86400000).toISOString(), // 어제 (지연)
|
||||
priority: 'high',
|
||||
status: 'in_progress',
|
||||
meetingId: 'meeting-003'
|
||||
},
|
||||
{
|
||||
id: 'todo-005',
|
||||
title: '다음 스프린트 백로그 정리',
|
||||
assignee: '정도현',
|
||||
dueDate: new Date(Date.now() + 259200000).toISOString(), // 3일 후
|
||||
priority: 'medium',
|
||||
status: 'pending',
|
||||
meetingId: 'meeting-001'
|
||||
}
|
||||
];
|
||||
Storage.set('todos', sampleTodos);
|
||||
}
|
||||
|
||||
// 공유받은 회의록 샘플 데이터
|
||||
if (!Storage.get('sharedMeetings') || Storage.get('sharedMeetings').length === 0) {
|
||||
const sharedMeetings = [
|
||||
{
|
||||
id: 'meeting-s001',
|
||||
title: 'AI 기술 도입 검토 회의',
|
||||
date: new Date(Date.now() - 259200000).toISOString(), // 3일 전
|
||||
location: '본사 2층 회의실',
|
||||
status: 'completed',
|
||||
description: 'LLM 기반 서비스 개선 방안 논의',
|
||||
sharedBy: '홍길동'
|
||||
},
|
||||
{
|
||||
id: 'meeting-s002',
|
||||
title: '보안 정책 업데이트',
|
||||
date: new Date(Date.now() - 432000000).toISOString(), // 5일 전
|
||||
location: '온라인 (Zoom)',
|
||||
status: 'completed',
|
||||
description: '최신 보안 가이드라인 공유 및 적용 계획',
|
||||
sharedBy: '송주영'
|
||||
},
|
||||
{
|
||||
id: 'meeting-s003',
|
||||
title: '팀 빌딩 워크샵',
|
||||
date: new Date(Date.now() - 604800000).toISOString(), // 7일 전
|
||||
location: '제주 연수원',
|
||||
status: 'completed',
|
||||
description: '팀 협업 강화 및 커뮤니케이션 개선 활동',
|
||||
sharedBy: '백현정'
|
||||
}
|
||||
];
|
||||
Storage.set('sharedMeetings', sharedMeetings);
|
||||
}
|
||||
}
|
||||
|
||||
// 인증 체크
|
||||
MeetingApp.ready(() => {
|
||||
const authToken = Storage.get('authToken');
|
||||
@ -569,10 +1012,20 @@
|
||||
AppState.currentUser = currentUser;
|
||||
}
|
||||
|
||||
// 샘플 데이터 초기화
|
||||
initSampleData();
|
||||
|
||||
// 데이터 로드 및 렌더링
|
||||
loadDashboardData();
|
||||
renderMeetings();
|
||||
renderUpcomingMeetings();
|
||||
renderMyMeetings();
|
||||
renderTodos();
|
||||
renderSharedMeetings();
|
||||
|
||||
// 타이머 업데이트 (1분마다)
|
||||
setInterval(() => {
|
||||
renderUpcomingMeetings();
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
// 대시보드 통계 로드
|
||||
@ -594,27 +1047,182 @@
|
||||
document.getElementById('todoCompletionRate').textContent = `${completionRate}%`;
|
||||
}
|
||||
|
||||
// 회의 목록 렌더링
|
||||
function renderMeetings() {
|
||||
const meetings = Storage.get('meetings', []).slice(0, 3);
|
||||
const meetingGrid = document.getElementById('meetingGrid');
|
||||
// 유틸리티 함수: 시간까지 남은 시간 계산
|
||||
function getTimeUntilMeeting(startTime) {
|
||||
const now = new Date();
|
||||
const start = new Date(startTime);
|
||||
const diff = start - now;
|
||||
return diff;
|
||||
}
|
||||
|
||||
if (meetings.length === 0) {
|
||||
meetingGrid.innerHTML = '<p style="color: var(--color-gray-500);">아직 등록된 회의가 없습니다.</p>';
|
||||
// 유틸리티 함수: 타이머 텍스트 포맷팅
|
||||
function formatTimerText(milliseconds) {
|
||||
if (milliseconds < 0) return '시작됨';
|
||||
|
||||
const hours = Math.floor(milliseconds / 3600000);
|
||||
const minutes = Math.floor((milliseconds % 3600000) / 60000);
|
||||
|
||||
if (hours > 24) {
|
||||
const days = Math.floor(hours / 24);
|
||||
return `D-${days}`;
|
||||
} else if (hours > 0) {
|
||||
return `${hours}시간 후`;
|
||||
} else if (minutes > 10) {
|
||||
return `${minutes}분 후`;
|
||||
} else if (minutes > 0) {
|
||||
return `곧 시작`;
|
||||
} else {
|
||||
return '시작 가능';
|
||||
}
|
||||
}
|
||||
|
||||
// 예정된/진행중 회의 렌더링
|
||||
function renderUpcomingMeetings() {
|
||||
const meetings = Storage.get('meetings', []);
|
||||
const currentUserId = Storage.get('currentUserId', 'user-001');
|
||||
const upcomingMeetingGrid = document.getElementById('upcomingMeetingGrid');
|
||||
|
||||
// 진행중 및 예정된 회의 필터링
|
||||
const now = new Date();
|
||||
const upcomingMeetings = meetings.filter(m => {
|
||||
if (m.status === 'ongoing') return true;
|
||||
if (m.status === 'scheduled') {
|
||||
const meetingDate = new Date(m.startTime || m.date);
|
||||
return meetingDate >= now;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// 정렬: 진행중 회의 우선, 그 다음 가까운 회의순
|
||||
upcomingMeetings.sort((a, b) => {
|
||||
if (a.status === 'ongoing' && b.status !== 'ongoing') return -1;
|
||||
if (a.status !== 'ongoing' && b.status === 'ongoing') return 1;
|
||||
return new Date(a.startTime || a.date) - new Date(b.startTime || b.date);
|
||||
});
|
||||
|
||||
// 최대 3개만 표시
|
||||
const displayMeetings = upcomingMeetings.slice(0, 3);
|
||||
|
||||
if (displayMeetings.length === 0) {
|
||||
upcomingMeetingGrid.innerHTML = '<p style="color: var(--color-gray-500);">예정된 회의가 없습니다.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
meetingGrid.innerHTML = meetings.map(meeting => `
|
||||
<div class="meeting-card" onclick="navigateTo('05-회의진행.html')">
|
||||
upcomingMeetingGrid.innerHTML = displayMeetings.map(meeting => {
|
||||
const isCreator = meeting.creatorId === currentUserId;
|
||||
const isOngoing = meeting.status === 'ongoing';
|
||||
const timeUntil = isOngoing ? 0 : getTimeUntilMeeting(meeting.startTime || meeting.date);
|
||||
const canJoin = isOngoing || (timeUntil > 0 && timeUntil <= 600000); // 10분 이내
|
||||
const timerText = isOngoing ? '진행중' : formatTimerText(timeUntil);
|
||||
|
||||
let actionButtons = '';
|
||||
if (isOngoing) {
|
||||
// 진행중 회의: 모든 참석자에게 참여하기 버튼
|
||||
actionButtons = `
|
||||
<div class="meeting-actions">
|
||||
<button class="btn-join" onclick="navigateTo('05-회의진행.html')">참여하기</button>
|
||||
</div>
|
||||
`;
|
||||
} else if (isCreator) {
|
||||
// 생성자: 수정 버튼
|
||||
actionButtons = `
|
||||
<div class="meeting-actions">
|
||||
<button class="btn-edit" onclick="navigateTo('03-회의예약.html')">수정</button>
|
||||
</div>
|
||||
`;
|
||||
} else if (canJoin) {
|
||||
// 참석자: 10분 이내면 참여하기 버튼
|
||||
actionButtons = `
|
||||
<div class="meeting-actions">
|
||||
<button class="btn-join" onclick="navigateTo('05-회의진행.html')">참여하기</button>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
// 참석자: 10분 이상 남음
|
||||
actionButtons = `
|
||||
<div class="timer-text">${timerText} 참여 가능</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const roleIndicator = isCreator && !isOngoing ?
|
||||
'<span class="role-indicator"><span class="crown-icon">👑</span> 생성자</span>' : '';
|
||||
|
||||
return `
|
||||
<div class="meeting-card ${isOngoing ? 'ongoing' : ''}" onclick="event.stopPropagation(); navigateTo('10-회의록대시보드.html')">
|
||||
<div class="meeting-header">
|
||||
<div style="flex: 1;">
|
||||
<div class="meeting-badges">
|
||||
${isOngoing ? '<span class="badge badge-ongoing">진행중</span>' : `<span class="badge badge-neutral">${timerText}</span>`}
|
||||
${roleIndicator}
|
||||
</div>
|
||||
<div class="meeting-title">${meeting.title}</div>
|
||||
<div class="meeting-meta">📅 ${formatDateTime(meeting.date)}</div>
|
||||
<div class="meeting-meta">📍 ${meeting.location}</div>
|
||||
<div class="meeting-meta">👥 ${meeting.attendees ? meeting.attendees.length : 0}명</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size: var(--font-size-body-small); color: var(--color-gray-600); margin-bottom: var(--spacing-2);">
|
||||
${meeting.description}
|
||||
</div>
|
||||
${actionButtons}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// 내 회의록 렌더링
|
||||
function renderMyMeetings() {
|
||||
const meetings = Storage.get('meetings', []);
|
||||
const myMeetingGrid = document.getElementById('myMeetingGrid');
|
||||
|
||||
// 완료된 회의만 필터링
|
||||
const completedMeetings = meetings
|
||||
.filter(m => m.status === 'completed')
|
||||
.sort((a, b) => new Date(b.date) - new Date(a.date))
|
||||
.slice(0, 3);
|
||||
|
||||
if (completedMeetings.length === 0) {
|
||||
myMeetingGrid.innerHTML = '<p style="color: var(--color-gray-500);">작성한 회의록이 없습니다. 첫 회의를 시작해보세요!</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
myMeetingGrid.innerHTML = completedMeetings.map(meeting => `
|
||||
<div class="meeting-card" onclick="navigateTo('10-회의록상세조회.html')">
|
||||
<div class="meeting-header">
|
||||
<div>
|
||||
<div class="meeting-title">${meeting.title}</div>
|
||||
<div class="meeting-meta">📅 ${formatDateTime(meeting.date)}</div>
|
||||
<div class="meeting-meta">📍 ${meeting.location}</div>
|
||||
<div class="meeting-meta">👥 ${meeting.attendees ? meeting.attendees.length : 0}명</div>
|
||||
</div>
|
||||
<span class="badge ${MeetingUtils.getStatusClass(meeting.status)}">
|
||||
${MeetingUtils.getStatusLabel(meeting.status)}
|
||||
</span>
|
||||
<span class="badge badge-success">확정완료</span>
|
||||
</div>
|
||||
<div style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">
|
||||
${meeting.description}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 공유받은 회의록 렌더링
|
||||
function renderSharedMeetings() {
|
||||
const sharedMeetings = Storage.get('sharedMeetings', []);
|
||||
const sharedMeetingGrid = document.getElementById('sharedMeetingGrid');
|
||||
|
||||
if (sharedMeetings.length === 0) {
|
||||
sharedMeetingGrid.innerHTML = '<p style="color: var(--color-gray-500);">공유받은 회의록이 없습니다.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
sharedMeetingGrid.innerHTML = sharedMeetings.map(meeting => `
|
||||
<div class="meeting-card" onclick="navigateTo('10-회의록상세조회.html')">
|
||||
<div class="meeting-header">
|
||||
<div>
|
||||
<div class="meeting-title">${meeting.title}</div>
|
||||
<div class="meeting-meta">📅 ${formatDateTime(meeting.date)}</div>
|
||||
<div class="meeting-meta">👤 공유자: ${meeting.sharedBy}</div>
|
||||
</div>
|
||||
<span class="badge badge-primary">공유됨</span>
|
||||
</div>
|
||||
<div style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">
|
||||
${meeting.description}
|
||||
@ -678,9 +1286,30 @@
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// FAB 버튼
|
||||
document.getElementById('fabButton').addEventListener('click', () => {
|
||||
window.location.href = '03-회의예약.html';
|
||||
// FAB 버튼 - 모달 토글
|
||||
const fabButton = document.getElementById('fabButton');
|
||||
const fabModal = document.getElementById('fabModal');
|
||||
|
||||
fabButton.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
fabModal.classList.add('show');
|
||||
document.body.style.overflow = 'hidden';
|
||||
});
|
||||
|
||||
// 모달 배경 클릭 시 닫기
|
||||
fabModal.addEventListener('click', (e) => {
|
||||
if (e.target === fabModal) {
|
||||
fabModal.classList.remove('show');
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
});
|
||||
|
||||
// ESC 키로 모달 닫기
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && fabModal.classList.contains('show')) {
|
||||
fabModal.classList.remove('show');
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user