This commit is contained in:
yabo0812
2025-10-21 14:21:00 +09:00
4 changed files with 605 additions and 4 deletions
+329 -1
View File
@@ -135,6 +135,195 @@
background: var(--gray-50);
border-radius: 4px;
}
/* UFR-AI-040: 관련 회의록 자동 연결 스타일 */
.related-meetings-section {
background: linear-gradient(135deg, var(--accent-50) 0%, var(--primary-50) 100%);
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
border: 1px solid var(--accent-200);
}
.related-meetings-header {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
user-select: none;
margin-bottom: 0;
transition: all var(--transition-normal);
}
.related-meetings-header:hover {
opacity: 0.8;
}
.related-meetings-title {
display: flex;
align-items: center;
gap: 8px;
}
.related-meetings-title h3 {
font-size: 16px;
font-weight: 600;
color: var(--gray-900);
margin: 0;
}
.ai-badge-small {
background: var(--accent-500);
color: var(--white);
font-size: 11px;
font-weight: 600;
padding: 3px 8px;
border-radius: 12px;
text-transform: uppercase;
}
.chevron-icon {
transition: transform 0.3s ease;
color: var(--gray-600);
}
.chevron-icon.expanded {
transform: rotate(180deg);
}
.related-meetings-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
margin-top: 0;
}
.related-meetings-content.expanded {
max-height: 800px;
margin-top: 12px;
}
.related-meeting-card {
background: var(--white);
border: 1px solid var(--gray-200);
border-radius: 8px;
padding: 12px;
margin-bottom: 10px;
cursor: pointer;
transition: all var(--transition-normal);
}
.related-meeting-card:last-child {
margin-bottom: 0;
}
.related-meeting-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
border-color: var(--primary-500);
}
.related-meeting-card:active {
transform: translateY(0);
}
.meeting-card-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 8px;
}
.meeting-card-title {
font-size: 14px;
font-weight: 600;
color: var(--gray-900);
margin: 0;
flex: 1;
line-height: 1.4;
}
.relevance-score {
font-size: 11px;
font-weight: 700;
padding: 4px 8px;
border-radius: 12px;
white-space: nowrap;
margin-left: 8px;
flex-shrink: 0;
}
.relevance-high {
background-color: var(--success-bg);
color: var(--success);
}
.relevance-medium {
background-color: var(--primary-50);
color: var(--primary-500);
}
.relevance-low {
background-color: var(--gray-100);
color: var(--gray-700);
}
.meeting-card-meta {
font-size: 12px;
color: var(--gray-600);
margin-bottom: 6px;
display: flex;
align-items: center;
gap: 4px;
}
.meeting-card-attendees {
font-size: 12px;
color: var(--gray-700);
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 4px;
}
.meeting-card-keywords {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 8px;
}
.keyword-tag {
background-color: var(--accent-100);
color: var(--accent-700);
font-size: 11px;
font-weight: 500;
padding: 3px 8px;
border-radius: 4px;
}
.meeting-card-summary {
font-size: 12px;
color: var(--gray-600);
margin-top: 8px;
line-height: 1.4;
border-top: 1px solid var(--gray-100);
padding-top: 8px;
}
.no-related-meetings {
text-align: center;
padding: 24px;
color: var(--gray-500);
font-size: 13px;
}
.no-related-meetings .material-symbols-outlined {
font-size: 48px;
opacity: 0.3;
margin-bottom: 8px;
display: block;
}
</style>
</head>
<body>
@@ -173,6 +362,22 @@
<span>AI가 발언 내용을 분석하여 회의록을 작성하고 있습니다</span>
</div>
<!-- UFR-AI-040: 관련 회의록 자동 연결 섹션 -->
<div class="related-meetings-section">
<div class="related-meetings-header" onclick="toggleRelatedMeetings()">
<div class="related-meetings-title">
<span class="ai-badge-small">AI</span>
<h3>관련 회의록 <span style="color: var(--gray-600); font-weight: normal;" id="relatedCount">(분석 중...)</span></h3>
</div>
<span class="material-symbols-outlined chevron-icon" id="chevronIcon">expand_more</span>
</div>
<div class="related-meetings-content" id="relatedMeetingsContent">
<div id="relatedMeetingsList">
<!-- JavaScript로 동적 생성 -->
</div>
</div>
</div>
<!-- 회의록 섹션들 -->
<div id="sectionList">
<!-- JavaScript로 동적 생성 -->
@@ -213,7 +418,41 @@
let startTime = Date.now();
let elapsedInterval;
// 프롬프트 유형 정의 (NEW - UFR-AI-030)
// UFR-AI-040: 관련 회의록 데이터 (AI가 자동으로 찾아서 연결)
const RELATED_MEETINGS = [
{
id: 'MTG-2025-001',
title: '프로젝트 초기 기획 회의',
date: '2025-10-15',
attendees: ['김철수', '이영희', '박민수'],
relevanceScore: 87,
keywords: ['프로젝트', 'Mobile First', '기획', '킥오프'],
summary: '프로젝트 초기 방향성 논의 및 Mobile First 전략 수립. 개발 기간과 주요 마일스톤 확정.',
folder: '프로젝트/회의록'
},
{
id: 'MTG-2025-002',
title: '마이크로서비스 아키텍처 설계 회의',
date: '2025-10-18',
attendees: ['김철수', '정도현', '이영희'],
relevanceScore: 78,
keywords: ['마이크로서비스', '아키텍처', 'API', '설계'],
summary: '서비스 분리 기준 및 API 설계 방향 논의. 데이터베이스 구조와 통신 프로토콜 결정.',
folder: '프로젝트/회의록'
},
{
id: 'MTG-2025-003',
title: 'UI/UX 디자인 리뷰 회의',
date: '2025-10-19',
attendees: ['이영희', '최유진', '박민수'],
relevanceScore: 72,
keywords: ['UI/UX', 'Mobile', '디자인', '사용자경험'],
summary: 'Mobile First 디자인 시스템 및 사용자 경험 개선 방안. 프로토타입 피드백 반영.',
folder: '프로젝트/회의록'
}
];
// 프롬프트 유형 정의 (UFR-AI-030)
const PROMPT_TYPES = {
onePage: {
id: 'onePage',
@@ -288,6 +527,92 @@
elapsedInterval = setInterval(updateElapsedTime, 1000);
// UFR-AI-040: 관련 회의록 토글
function toggleRelatedMeetings() {
const content = document.getElementById('relatedMeetingsContent');
const chevron = document.getElementById('chevronIcon');
content.classList.toggle('expanded');
chevron.classList.toggle('expanded');
// 처음 열 때만 렌더링
if (content.classList.contains('expanded') && !content.dataset.rendered) {
renderRelatedMeetings();
content.dataset.rendered = 'true';
}
}
// UFR-AI-040: 관련 회의록 렌더링
function renderRelatedMeetings() {
const container = document.getElementById('relatedMeetingsList');
if (RELATED_MEETINGS.length === 0) {
container.innerHTML = `
<div class="no-related-meetings">
<span class="material-symbols-outlined">search_off</span>
<p>관련된 회의록을 찾을 수 없습니다</p>
</div>
`;
return;
}
container.innerHTML = RELATED_MEETINGS.map(meeting => {
const scoreClass = getRelevanceClass(meeting.relevanceScore);
const scoreLabel = meeting.relevanceScore + '%';
return `
<div class="related-meeting-card" onclick="navigateToRelatedMeeting('${meeting.id}')">
<div class="meeting-card-header">
<h4 class="meeting-card-title">${meeting.title}</h4>
<span class="relevance-score ${scoreClass}">${scoreLabel}</span>
</div>
<div class="meeting-card-meta">
<span class="material-symbols-outlined" style="font-size: 14px;">calendar_today</span>
${meeting.date}
</div>
<div class="meeting-card-attendees">
<span class="material-symbols-outlined" style="font-size: 14px;">group</span>
${meeting.attendees.join(', ')}
</div>
<div class="meeting-card-keywords">
${meeting.keywords.map(keyword =>
`<span class="keyword-tag">${keyword}</span>`
).join('')}
</div>
<div class="meeting-card-summary">
${meeting.summary}
</div>
</div>
`;
}).join('');
// 관련 회의록 개수 업데이트
document.getElementById('relatedCount').textContent = `(${RELATED_MEETINGS.length}건)`;
}
// UFR-AI-040: 유사도 점수에 따른 색상 클래스 반환
function getRelevanceClass(score) {
if (score >= 85) return 'relevance-high'; // 85-100%: 녹색 (높은 관련성)
if (score >= 75) return 'relevance-medium'; // 75-84%: 파란색 (중간 관련성)
return 'relevance-low'; // 70-74%: 회색 (낮은 관련성)
}
// UFR-AI-040: 관련 회의록으로 이동
function navigateToRelatedMeeting(meetingId) {
UIComponents.showToast(`회의록 상세 화면으로 이동: ${meetingId}`, 'info');
// TODO: 실제로는 회의록 상세 화면(10-회의록상세조회.html)으로 이동
// NavigationHelper.navigate('MEETING_DETAIL', { meetingId });
}
// UFR-AI-040: 관련 회의록 AI 분석 시뮬레이션
function simulateRelatedMeetingsAnalysis() {
// 2초 후 관련 회의록 개수 표시
setTimeout(() => {
document.getElementById('relatedCount').textContent = `(${RELATED_MEETINGS.length}건)`;
UIComponents.showToast(`AI가 ${RELATED_MEETINGS.length}건의 관련 회의록을 찾았습니다`, 'success', 3000);
}, 2000);
}
// 섹션 렌더링
function renderSections() {
const container = document.getElementById('sectionList');
@@ -648,6 +973,9 @@
// 초기 렌더링
renderSections();
// UFR-AI-040: 관련 회의록 AI 분석 시작
simulateRelatedMeetingsAnalysis();
// 실시간 발언 시뮬레이션
const speeches = [
{ speaker: '김철수', text: '프로젝트 킥오프 회의를 시작하겠습니다...' },